自定义view——贝塞尔曲线之仿qq消息气泡拖拽

这里写图片描述

原理图
这里写图片描述

可以看一下和了解一下贝塞尔曲线:http://www.html-js.com/article/1628

MessageBubbleView:自定义view


public class MessageBubbleView extends View {
    // 两个圆的圆形
    private PointF mFixationPoint, mDragPoint;
    // 拖拽圆的半径
    private int mDragRadius = 10;
    private Paint mPaint;
    // 固定圆的最大半径(初始半径)
    private int mFixationRadiusMax = 7;
    private int mFixationRadiusMin = 3;
    private int mFixationRadius;

    public MessageBubbleView(Context context) {
        this(context, null);
    }

    public MessageBubbleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MessageBubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mDragRadius = dp2x(mDragRadius);
        mFixationRadiusMax = dp2x(mFixationRadiusMax);
        mFixationRadiusMin = dp2x(mFixationRadiusMin);
    }

    private int dp2x(int dpValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mDragPoint == null || mFixationPoint == null) {
            return;
        }
        //绘制拖拽的圆
        canvas.drawCircle(mDragPoint.x, mDragPoint.y, mDragRadius, mPaint);


        Path bezeierPath = getBezeierPath();

        if (bezeierPath != null) {//当大于最小的时候才绘制
            //绘制固定的圆
            canvas.drawCircle(mFixationPoint.x, mFixationPoint.y, mFixationRadius, mPaint);
            //绘制贝塞尔曲线
            canvas.drawPath(bezeierPath, mPaint);
        }
    }

    /**
     * 获取两个圆之间的距离
     */
    private double getDistance(PointF point1, PointF point2) {
        return Math.sqrt((point1.x - point2.x) * (point1.x - point2.x)
                + (point1.y - point2.y) * (point1.y - point2.y));
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                float downX = event.getX();
                float downY = event.getY();
                initPoint(downX, downY);
                break;
            case MotionEvent.ACTION_MOVE:
                float moveX = event.getX();
                float moveY = event.getY();
                updatePoint(moveX, moveY);
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        invalidate();
        return true;

    }

    /**
     * 更新拖拽点的坐标
     */
    private void updatePoint(float moveX, float moveY) {
        mDragPoint.x = moveX;
        mDragPoint.y = moveY;
    }

    /**
     * 初始化点
     */
    private void initPoint(float downX, float downY) {
        mFixationPoint = new PointF(downX, downY);
        mDragPoint = new PointF(downX, downY);
    }

    /**
     * 获得贝塞尔的曲线
     *
     * @return
     */
    public Path getBezeierPath() {
        //获得两个圆之间的距离
        double distance = getDistance(mDragPoint, mFixationPoint);
        mFixationRadius = (int) (mFixationRadiusMax - distance / 14);
        if (mFixationRadius < mFixationRadiusMin) {
            // 超过一定距离 贝塞尔和固定圆都不要画了
            return null;
        }

        Path bezeierPath = new Path();
        //求角a
        //求斜率
        float dy = mDragPoint.y - mFixationPoint.y;
        float dx = mDragPoint.x - mFixationPoint.x;
        float tanA = dy / dx;
        //求出角a
        double arcTanA = Math.atan(tanA);

        //p0
        float p0x = (float) (mFixationRadius * Math.sin(arcTanA) + mFixationPoint.x);
        float p0y = (float) (mFixationPoint.y - mFixationRadius * Math.cos(arcTanA));

        //p1
        float p1x = (float) (mDragRadius * Math.sin(arcTanA) + mDragPoint.x);
        float p1y = (float) (mDragPoint.y - mDragRadius * Math.cos(arcTanA));

        //p2
        float p2x = (float) (mDragPoint.x - mDragRadius * Math.sin(arcTanA));
        float p2y = (float) (mDragPoint.y + mDragRadius * Math.cos(arcTanA));

        //p3
        float p3x = (float) (mFixationPoint.x - mFixationRadius * Math.sin(arcTanA));
        float p3y = (float) (mFixationPoint.y + mFixationRadius * Math.cos(arcTanA));

        //绘制贝塞尔曲线
        bezeierPath.moveTo(p0x, p0y);
        //绘制第一条线
        PointF controlPoint = getControlPoint();
        bezeierPath.quadTo(controlPoint.x, controlPoint.y, p1x, p1y);//控制点+目标坐标
        //绘制到第二条
        bezeierPath.lineTo(p2x, p2y);
        bezeierPath.quadTo(controlPoint.x, controlPoint.y, p3x, p3y);
        //最后闭合
        bezeierPath.close();
        return bezeierPath;
    }

    /**
     * 获取控制点
     */
    public PointF getControlPoint() {
        return new PointF((mDragPoint.x + mFixationPoint.x) / 2, (mDragPoint.y + mFixationPoint.y) / 2);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值