美文网首页
贝塞尔曲线的应用-实现所有view的拖拽

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

作者: cao苗子 | 来源:发表于2019-08-27 15:59 被阅读0次

    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

    相关文章

      网友评论

          本文标题:贝塞尔曲线的应用-实现所有view的拖拽

          本文链接:https://www.haomeiwen.com/subject/mtbeectx.html