拖拽控件

作者: 我的阿福 | 来源:发表于2020-06-07 13:37 被阅读0次
    • 为了复习View的位置坐标,今天做一个可以在父布局内随意拖拽的小控件,值得一提的是,这个可以根据父布局来调整整体的效果,,如图所示:

      p3o9v-v4wdh.gif
    • 整个控件很简单,但能帮助回忆View的位置关系。

    • 还是说一下整体思路
      1.监听触摸事件,计算手指移动的距离。
      2.让view在x方向和y方向移动和手指滑动相同的距离。
      3.加上贴边动画。.

    • 获取滑动位置
      当手指按下时,记住当前手指位置,滑动事件产生时用新的坐标减去旧的坐标就是滑动的距离。
    case MotionEvent.ACTION_DOWN:
                    mLastTouchX = event.getRawX();//这里要注意,不能用getX,后面有解释
                    mLastTouchY = event.getRawY();
    
     case MotionEvent.ACTION_MOVE:
                    float nowX = event.getRawX();
                    float nowY = event.getRawY();
                    float delX = nowX - mLastTouchX;//x方向滑动距离
                    float delY = nowY - mLastTouchY;//y方向滑动距离
    
    • 移动和手指相同的距离
      这里移动用setTranslationX和setTranslationY的方法来移动,这两个方法是指左顶点相对于父布局一开始的位置的偏移量。需要注意的是,视图钢加载出来的时候,不论view在父布局的什么位置,这两个偏移量都是0。
     float tranY = getTranslationY() + delY;
                    float nowReY = getY();
                  //这几句判断是为了不让自己垂直方向超出父视图的边界
                    if (nowReY <= 0 && delY < 0) {
                        tranY = -getTop();
                    } else if (nowReY + mHeight >= mParentHeight && delY > 0) {
                        tranY = mParentHeight - mHeight - getTop();
                    }
                    float tranX = getTranslationX() + delX;
                    float nowReX = getX();
                    //同理,不让自己水平方向超出父视图的边界
                    if (nowReX <= 0 && delX < 0) {
                        tranX = -getLeft();
                    } else if (nowReX + mWidth >= mParentWidth && delX > 0) {
                        tranX = mParentWidth - getRight();
                    }
    
                    setTranslationX(tranX);
                    setTranslationY(tranY);
    
    • 手指离开屏幕,up事件时,判断边界距离,贴到边界。
    if (getX() + (mWidth / 2) > (mParentWidth / 2)) {
                int rightMoveDistance = (int) (mParentWidth - getX() - mWidth / 2);
                //貼右边
                startSideAnimation(RIGTHSIDE, getTranslationX(), rightMoveDistance);
            } else {
                //貼左边
                int leftMoveDistance = (int) (getX() + mWidth / 2);
                startSideAnimation(LEFTSIDE, getTranslationX(), leftMoveDistance);
            } 
    private void startSideAnimation(final int side, final float origTX, int moveDistance) {
            mSideAnimation = ValueAnimator.ofFloat(moveDistance);
            mSideAnimation.setDuration(200);
            mSideAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
            mSideAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    switch (side) {
                        case LEFTSIDE:
                            setTranslationX(origTX - value);
                            break;
                        default:
                            setTranslationX(origTX + value);
                    }
                }
            });
            mSideAnimation.start();
        } 
    

    整个过程其实很简单,但值得注意的地方有两个:
    1.获取手指位置用的getRawX而不是getX,这两个的区别:getRawX是手指接触view的位置距离整个屏幕边界的距离,getX是手指触摸view的位置距离父布局边界的距离。如果这里用getX的话,当view移动的时候,getX也会相应改变,无法达到想要的效果。
    2.移动view的位置,直接用setTranslationX,setTranslationY的方法。

    • 整个代码
    package com.anthony.widgets;
    
    import android.animation.ValueAnimator;
    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.ViewParent;
    import android.view.animation.AccelerateDecelerateInterpolator;
    
    /**
     * Created by H.Anthony on 2020/06/06.
     */
    public class MoveSpirit extends View {
    
        private static final String TAG = "MoveSpirit";
        private static final int LEFTSIDE = 0x1;
        private static final int RIGTHSIDE = 0x2;
        private float mLastTouchX, mLastTouchY;
        private int mParentWidth, mParentHeight, mWidth, mHeight;
        private ValueAnimator mSideAnimation;
    
        public MoveSpirit(Context context) {
            super(context);
        }
    
        public MoveSpirit(Context context, @androidx.annotation.Nullable AttributeSet attrs) {
            super(context, attrs);
    
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            ViewParent viewParent = getParent();
            if (viewParent != null) {
                if (viewParent instanceof ViewGroup) {
                    ViewGroup viewGroup = (ViewGroup) viewParent;
                    mParentWidth = viewGroup.getWidth();
                    mParentHeight = viewGroup.getHeight();
                    mWidth = getWidth();
                    mHeight = getHeight();
                    Log.d(TAG, "onSizeChanged: " + mParentWidth + ";" + mParentHeight + ";" + mWidth + ";" + mHeight);
                }
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            closeAnimation();
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mLastTouchX = event.getRawX();
                    mLastTouchY = event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float nowX = event.getRawX();
                    float nowY = event.getRawY();
                    float delX = nowX - mLastTouchX;
                    float delY = nowY - mLastTouchY;
    
                    float tranY = getTranslationY() + delY;
                    float nowReY = getY();
                    if (nowReY <= 0 && delY < 0) {
                        tranY = -getTop();
                    } else if (nowReY + mHeight >= mParentHeight && delY > 0) {
                        tranY = mParentHeight - mHeight - getTop();
                    }
                    float tranX = getTranslationX() + delX;
                    float nowReX = getX();
                    if (nowReX <= 0 && delX < 0) {
                        tranX = -getLeft();
                    } else if (nowReX + mWidth >= mParentWidth && delX > 0) {
                        tranX = mParentWidth - getRight();
                    }
    
                    setTranslationX(tranX);
                    setTranslationY(tranY);
    
                    mLastTouchX = nowX;
                    mLastTouchY = nowY;
                    break;
                case MotionEvent.ACTION_UP:
                    autoTouchSide();
                    break;
            }
    
            return true;
        }
    
    
        /**
         * 左右動畫
         */
        private void autoTouchSide() {
            if (getX() + (mWidth / 2) > (mParentWidth / 2)) {
                int rightMoveDistance = (int) (mParentWidth - getX() - mWidth / 2);
                //貼右边
                startSideAnimation(RIGTHSIDE, getTranslationX(), rightMoveDistance);
            } else {
                //貼左边
                int leftMoveDistance = (int) (getX() + mWidth / 2);
                startSideAnimation(LEFTSIDE, getTranslationX(), leftMoveDistance);
            }
    
        }
    
        private void startSideAnimation(final int side, final float origTX, int moveDistance) {
            mSideAnimation = ValueAnimator.ofFloat(moveDistance);
            mSideAnimation.setDuration(200);
            mSideAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
            mSideAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    switch (side) {
                        case LEFTSIDE:
                            setTranslationX(origTX - value);
                            break;
                        default:
                            setTranslationX(origTX + value);
                    }
                }
            });
            mSideAnimation.start();
        }
    
        private void closeAnimation() {
            if (mSideAnimation != null && mSideAnimation.isRunning()) {
                mSideAnimation.removeAllUpdateListeners();
                mSideAnimation.cancel();
            }
        }
    }
    
    
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <com.anthony.widgets.MoveSpirit
            android:layout_width="160dp"
            android:layout_height="160dp"
            android:background="@mipmap/panda"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    相关文章

      网友评论

        本文标题:拖拽控件

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