Android仿QQ消息粘性球控件

        网上已经有很多完善的粘性球控件了,但还是想自己写一个练习一下,只是实现了功能,还并不是很完善,学习的可以参考一下

废话不多说,上代码:


1、首先,显示出来的粘性球比较简单,用贝塞尔曲线画出来就可以了

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(mBallColor);
        mPath.reset();
        mPath.moveTo(mLTPoint.x, mLTPoint.y);
        mPath.lineTo(mRBPoint.x, mLTPoint.y);
        mRectF.set(mRBPoint.x - mContentHeight / 2, mLTPoint.y, mRBPoint.x + mContentHeight / 2, mRBPoint.y);
        mPath.arcTo(mRectF, -90, 180);
        mPath.lineTo(mLTPoint.x, mRBPoint.y);
        mRectF.set(mLTPoint.x - mContentHeight / 2, mLTPoint.y, mLTPoint.x + mContentHeight / 2, mRBPoint.y);
        mPath.arcTo(mRectF, 90, 180);
        canvas.drawPath(mPath, mPaint);
        mPaint.setColor(mTextColor);
        canvas.drawText(mText, mBaseLinePoint.x, mBaseLinePoint.y, mPaint);
        mPaint.setColor(mBallColor);
    }

        2、计算

        这部分稍微麻烦一点,主要是计算连接部分贝塞尔曲线的各个点位置

/**
     * 动圆移动的时候计算各个位置的值
     *
     * @param touchX
     * @param touchY
     */
    private void calculateSelf(float touchX, float touchY) {
        mLTPoint.x = touchX - (mContentWidth - mContentHeight) / 2;
        mLTPoint.y = touchY - mContentHeight / 2;
        mRBPoint.x = touchX + (mContentWidth - mContentHeight) / 2;
        mRBPoint.y = touchY + mContentHeight / 2;
        mBaseLinePoint.set(getPaddingLeft() + (mContentWidth - mBounds.width()) / 2 + mLTPoint.x - mContentHeight / 2, getPaddingTop() + (mContentHeight + mBounds.height()) / 2 + mLTPoint.y);
        mMCenter.set((mLTPoint.x + mRBPoint.x) / 2, (mLTPoint.y + mRBPoint.y) / 2);
        double radian = Math.atan2(mMCenter.y - mQCenter.y, mMCenter.x - mQCenter.x);
        // 计算静圆两点坐标
        mQPoint1.x = (float) (mQCenter.x + mQRadius * Math.cos(radian - mQRadian / 2.0));
        mQPoint1.y = (float) (mQCenter.y + mQRadius * Math.sin(radian - mQRadian / 2.0));
        mQPoint2.x = (float) (mQCenter.x + mQRadius * Math.sin(Math.PI / 2.0 - radian - mQRadian / 2.0));
        mQPoint2.y = (float) (mQCenter.y + mQRadius * Math.cos(Math.PI / 2.0 - radian - mQRadian / 2.0));
        calculateMPoint();
        // 控制点坐标
        mControl1.x = (mQCenter.x + mMCenter.x) / 2;
        mControl1.y = (mQCenter.y + mMCenter.y) / 2;
        mControl2.x = mControl1.x;
        mControl2.y = mControl1.y;

        mCurrDistance = Math.sqrt(Math.pow(mQCenter.x - mMCenter.x, 2) + Math.pow(mQCenter.y - mMCenter.y, 2));
        mQRadius = (int) ((mQOrigRadius - mQOrigRadius / 3.0) * (1 - mCurrDistance / mMaxDistance) + mQOrigRadius / 3.0);
        if (mCurrDistance > mMaxDistance) {
            mHasLeave = true;
        }
    }

    /**
     * 计算动圆上的两个贝塞尔曲线点
     */
    private void calculateMPoint() {
        double radian = Math.atan2(mMCenter.y - mQCenter.y, mQCenter.x - mMCenter.x);
        double lineRadian1 = radian - mMRadian / 2;
        if (lineRadian1 + Math.PI < 0) {
            lineRadian1 += 2 * Math.PI;
        }
        double lineRadian2 = radian + mMRadian / 2;
        if (lineRadian2 + Math.PI < 0) {
            lineRadian2 += 2 * Math.PI;
        }
        if (lineRadian2 > Math.PI) {
            lineRadian2 = -(2 * Math.PI - lineRadian2);
        }
        mMPoint1 = getIntersectionOfSelf(lineRadian1);
        mMPoint2 = getIntersectionOfSelf(lineRadian2);
    }

    /**
     * 根据直线角度获取该直线逻辑上需要的与拖拽物的交点
     *
     * @param lineRadian 直线角度
     * @return 交点坐标
     */
    private PointF getIntersectionOfSelf(double lineRadian) {
        if (lineRadian >= mRadianRight && lineRadian <= mRadianLeft) {
            // 相交于上方直线
            return MathUtil.intersectionLine(-Math.tan(lineRadian), mMCenter, 0, mLTPoint);
        } else if (lineRadian > mRadianLeft || lineRadian < -mRadianLeft) {
            // 相交于左方半圆
            PointF[] pointFS = MathUtil.intersectionCircle(-Math.tan(lineRadian), mMCenter, new PointF(mLTPoint.x, (mLTPoint.y + mRBPoint.y) / 2), mContentHeight / 2);
            if (pointFS.length > 1) {
                if (pointFS[0].x > pointFS[1].x) {
                    pointFS[0] = pointFS[1];
                }
            }
            return pointFS[0];
        } else if (lineRadian >= -mRadianLeft && lineRadian <= -mRadianRight) {
            // 相交于下方直线
            return MathUtil.intersectionLine(-Math.tan(lineRadian), mMCenter, 0, mRBPoint);
        } else if (lineRadian < mRadianRight || lineRadian > -mRadianRight) {
            // 相交于右方半圆
            PointF[] pointFS = MathUtil.intersectionCircle(-Math.tan(lineRadian), mMCenter, new PointF(mRBPoint.x, (mLTPoint.y + mRBPoint.y) / 2), mContentHeight / 2);
            if (pointFS.length > 1) {
                mMPoint2 = pointFS[1];
                if (pointFS[0].x < pointFS[1].x) {
                    pointFS[0] = pointFS[1];
                }
            }
            return pointFS[0];
        } else {
            return null;
        }
    }
        3、效果图

效果图

时间原因先不详细解释了,有问题的话欢迎询问,有什么bug也欢迎反馈会尽快解决,谢谢各位!

GitHub地址:https://github.com/qswjoker/StickBall.git


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值