贝塞尔曲线的应用-实现所有view的拖拽

2019-08-27  本文已影响0人  cao苗子

1.贝塞尔曲线分析图

贝塞尔.jpg

2.效果图

所有view拖拽.gif

好了,这就是我们今天要写的内容。

3.效果分析

看过我上一篇文章的同学,就知道,我们要画拖拽点,需要画两个点,一个是固定点,一个是拖拽点。
这里的固定点就是view的中心点

 int[] location = new int[2];
                mView.getLocationOnScreen(location);
                Bitmap bitmap = getBitmapByView(mView);
                mDragView.initPoint(location[0] + bitmap.getWidth()/2,location[1] + bitmap.getHeight()/2 - getStatusBarHeight(v.getContext()));

这是初始化的时候要设置的点。拖拽点会随着滑动而移动,然后不断的绘制

 case MotionEvent.ACTION_MOVE:
                mDragView.updatePoint(event.getRawX(),event.getRawY() - getStatusBarHeight(v.getContext()));
                break;

/**
     * 更新座标点
     * @param moveX
     * @param moveY
     */
    public void updatePoint(float moveX, float moveY) {
        mDragPointF.x = moveX;
        mDragPointF.y = moveY;
        invalidate();
    }

不断的取更新试图就可以了。
在绑定试图的时候,创建覆盖view

public class MainActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DragView.bindDragView(findViewById(R.id.tv), new DragViewTouchListener.DragViewListener() {
            @Override
            public void dismiss(View view) {
                Log.d("TAG","dismiss");
            }
        });
        DragView.bindDragView(findViewById(R.id.iv), new DragViewTouchListener.DragViewListener() {
            @Override
            public void dismiss(View view) {
                Log.d("TAG","dismiss");
            }
        });
    }
}

在 DragViewTouchListener 的构造函数里面,通过windowmanager创建可拖拽的view,并保存原始的view

 //记录自己的view
    private View mView;
    private WindowManager mWindowManager;
    //构造的view
    private DragView mDragView;
    private WindowManager.LayoutParams mParams;
    //爆炸容器
    private FrameLayout mBombLayout;
    private ImageView mBombImage;
    private DragViewListener mDragViewListener;

    public DragViewTouchListener(View view, DragViewListener dragViewListener){
        mDragViewListener = dragViewListener;
        mView = view;
        mWindowManager = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
        mDragView = new DragView(view.getContext());
        mDragView.setDragViewCallBackListener(this);
        //全屏拖动
        mParams = new WindowManager.LayoutParams();
        //设置背景透明
        mParams.format = PixelFormat.TRANSLUCENT;
        mBombLayout = new FrameLayout(view.getContext());
        mBombImage = new ImageView(view.getContext());
        mBombLayout.addView(mBombImage);
    }

然后在onTouch事件的 down事件里面 把自己的view隐藏 把拖拽view添加到windowManager中而且要是在原始view的位置。把原始view转中bitmap设置到拖拽view上,这样就看不出来了。

 /**
     * 从一个view中获取bitmap
     * @param mView
     * @return
     */
    private Bitmap getBitmapByView(View mView) {
        mView.buildDrawingCache();
        Bitmap bitmap = mView.getDrawingCache();
        return bitmap;
    }

  /**
     * 设置bitmao
     * @param bitmap
     */
    public void setDragBitmap(Bitmap bitmap) {
        this.mDragBitmap = bitmap;
        invalidate();
    }

4.onDraw 绘制

如果mDragBitmap不空记得绘制这个bitmap

@Override
    protected void onDraw(Canvas canvas) {
        if (mDragPointF == null || mDragPaint == null) {
            return;
        }
        if(this.mDragBitmap != null){
            int width = mDragBitmap.getWidth() > mDragBitmap.getHeight() ? mDragBitmap.getWidth() : mDragBitmap.getHeight();
            mDragRadius = width / 4;
        }
        //画拖拽圆
        canvas.drawCircle(mDragPointF.x, mDragPointF.y, mDragRadius, mDragPaint);

        //画一个固定的圆 根据拖拽距离改固定圆的半径
        canvas.drawCircle(mFixationPointF.x, mFixationPointF.y, mFixationRadius, mFixationPaint);

        Path bezierPath = getBezierPath();
        if(bezierPath != null){
            canvas.drawPath(bezierPath,mFixationPaint);
        }

        if(this.mDragBitmap != null){
            canvas.drawBitmap(mDragBitmap,mDragPointF.x - mDragBitmap.getWidth()/2,mDragPointF.y-mDragBitmap.getHeight()/2,null);
        }
    }

5.绘制贝塞尔曲线的path

/**
    * 获取贝塞尔曲线的 path
    * @return
    */
   private Path getBezierPath(){

       double distance = calculateDistance();

       //比例 这个数可以自己随便定义一个数 比如 14 主要是看效果是否符合自己的效果 测试就知道了
       int bili = mDragRadius;
       mFixationRadius = (int) (mFixationMaxRadius - distance / bili);
       if (mFixationRadius < mFixationMinRadius) {
           return null;
       }
       Path bezierPath = new Path();

       float dy = mDragPointF.y - mFixationPointF.y;
       float dx = mDragPointF.x - mFixationPointF.x;

       float tanA = dy / dx;
       //求角A 反tanA
       double arcTanA = Math.atan(tanA);
       //p0
       float p0x = (float) (mFixationPointF.x + mFixationRadius * Math.sin(arcTanA));
       float p0y = (float)(mFixationPointF.y - mFixationRadius * Math.cos(arcTanA));

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

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

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

       //拼装 贝塞尔曲线
       bezierPath.moveTo(p0x,p0y);
       //获取控制点座标 定在 两点的中心点
       PointF controllerPointF = getControllerPointF();
       //画第一条
       bezierPath.quadTo(controllerPointF.x,controllerPointF.y,p1x,p1y);
       //画第二条
       bezierPath.lineTo(p2x,p2y);
       bezierPath.quadTo(controllerPointF.x,controllerPointF.y,p3x,p3y);

       bezierPath.close();

       return bezierPath;
   }

6.拖拽view的回弹动画

/**
     * 处理手指松开
     */
    public void handleActionUp() {
        //回弹
        if(mFixationRadius > mFixationMinRadius){
            ValueAnimator animator = ObjectAnimator.ofFloat(1);
            animator.setDuration(350);
            final PointF start = new PointF(mDragPointF.x,mDragPointF.y);
            final PointF end = new PointF(mFixationPointF.x,mFixationPointF.y);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float animatedValue = (float) animation.getAnimatedValue();
                    PointF pointByPercent = getPointByPercent(start, end, animatedValue);
                    updatePoint(pointByPercent.x,pointByPercent.y);
                }
            });
            //差值器 回到原来的位置都向前甩 然后回到原来位置
            animator.setInterpolator(new OvershootInterpolator(3f));
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    if(mDragViewCallBackListener!=null){
                        mDragViewCallBackListener.restore();
                    }
                }
            });
            animator.start();
        }else {
            //爆炸
            if(mDragViewCallBackListener!=null){
                mDragViewCallBackListener.dismiss();
            }
        }
    }

根据三角函数的算法,已知两个点的座标根据移动百分比计算第三个点的座标

 /**
     * 根据百分比计算点的座标
     * @param start
     * @param end
     * @param percent
     * @return
     */
    private PointF getPointByPercent(PointF start,PointF end,float percent){
        return new PointF(evaluateValue(start.x,end.x,percent),evaluateValue(start.y,end.y,percent));
    }
    private float evaluateValue(Number start,Number end,float fraction){
        return start.floatValue() + (end.floatValue() - start.floatValue()) * fraction;
    }

这个可以作为一个工具栏单独拿出来坐记录。

难点也就这么些。有问题随时提问

源码地址:
https://github.com/panshimu/MessageDragView

上一篇 下一篇

猜你喜欢

热点阅读