可全局拖拽、点击的View

先上图

DragView.gif
  • 这个效果和IOS的Assistive Touch效果类似,可以实现全局拖拽,设置点击事件,并且点击和拖拽不冲突。
  • Demo的GitHub地址:https://github.com/icecreammmm/DragView

使用方法

  • FloatTouchListener里传入的第二个为父布局的Layout需要设置为FrameLayout。
    第三个参数,也就是我们需要拖拽的View,需要给这个View外面包上一层FrameLayout,即可实现我们所需要的效果。
    private void setTouchListener() {
        margin = (int) (10 * getResources().getDisplayMetrics().density + 0.5f);
        mFloatTouchListener = new FloatTouchListener(this, flParent, flChild, margin);
        flChild.setOnTouchListener(mFloatTouchListener);
        flChild.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "点击事件", Toast.LENGTH_SHORT).show();
            }
        });
    }

实现方法

从事件分发机制中我们知道,就优先级而言:onTouchListener>onClickListenr。

上面的拖拽事件已经消费了onTouchListener(即onTouch方法中返回true),那么就不会下发到onClickListenr,自然就不会产生点击事件。

也许你想让onTouchListener不消费,然后不就下发到onClickListenr了么?

确实这样可以实现点击事件,但是拖拽功能又实现不了了。

  • 通过上面的分析,最终的解决办法就是:
    onTouch方法中,在接收到ACTION_DOWN后,返回false,交给onClickListenr处理。剩下的ACTION_MOVE/ACTION_UP等事件,返回true,交给onTouchListener处理。这样自然就可以既实现拖拽效果又实现点击效果了。
public class FloatTouchListener implements View.OnTouchListener {
    private View mParentView;
    private View mFloatView;
    private FrameLayout.LayoutParams mFloatViewWindowParam;
    private float mPreviousX = -1;
    private float mPreviousY = -1;
    private boolean mHasMoved = false;
    private int mTouchSlop;
    private int mDownPointerId = -1;
    private Interpolator mInterpolator;
    private FloatAnimatorUpdateListener mUpdateListener;
    private int mMargin;

    public FloatTouchListener(Context context, View parentView, View floatView, int margin) {
        mParentView = parentView;
        mFloatView = floatView;
        mFloatViewWindowParam = (FrameLayout.LayoutParams) floatView.getLayoutParams();
        mInterpolator = new DecelerateInterpolator();
        ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
        mMargin = margin;
    }

    private boolean adjustMarginParams(View v, MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        float deltaX = x - mPreviousX;
        float deltaY = y - mPreviousY;
        if (!mHasMoved) {
            if (Math.abs(deltaX) < mTouchSlop && Math.abs(deltaY) < mTouchSlop) {
                return false;
            }
        }

        if ((mFloatViewWindowParam.gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
            mFloatViewWindowParam.topMargin = mParentView.getBottom() - mMargin - mFloatView
                    .getHeight();
        }

        if ((mFloatViewWindowParam.gravity & Gravity.RIGHT) == Gravity.RIGHT) {
            mFloatViewWindowParam.leftMargin = mParentView.getRight() - mMargin - mFloatView
                    .getWidth();
        }
        mFloatViewWindowParam.gravity = Gravity.NO_GRAVITY;

        //左上角位置
        int newX = (int) (mFloatViewWindowParam.leftMargin + deltaX);
        int newY = (int) (mFloatViewWindowParam.topMargin + deltaY);
        newX = Math.max(newX, mParentView.getLeft() + mMargin);
        newX = Math.min(newX, mParentView.getRight() - mMargin - mFloatView.getWidth());
        newY = Math.max(newY, mParentView.getTop() + mMargin);
        newY = Math.min(newY, mParentView.getBottom() - mMargin - mFloatView.getHeight());
        mFloatViewWindowParam.leftMargin = newX;
        mFloatViewWindowParam.topMargin = newY;

        return true;
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        int action = MotionEventCompat.getActionMasked(event);
        boolean result = false;
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                mDownPointerId = MotionEventCompat.getPointerId(event, 0);
                mPreviousX = event.getX();
                mPreviousY = event.getY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if (mDownPointerId >= 0) {
                    int index = MotionEventCompat.getActionIndex(event);
                    int id = MotionEventCompat.getPointerId(event, index);
                    if (id == mDownPointerId) {
                        boolean update = adjustMarginParams(view, event);
                        if (!update) {
                            break;
                        }
                        mFloatView.requestLayout();
                        mHasMoved = true;
                        result = true;
                    }
                }
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL: {
                if (mDownPointerId >= 0 && mHasMoved) {
                    event.setAction(MotionEvent.ACTION_CANCEL);
                    adjustMarginParams(view, event);
                    mFloatView.requestLayout();
                    int center = (mParentView.getWidth() - mFloatView.getWidth()) / 2;
                    int x = mFloatViewWindowParam.leftMargin;
                    int destX = 0;
                    if (x < center) {
                        destX = mParentView.getLeft() + mMargin;
                    } else {
                        destX = mParentView.getRight() - mMargin - mFloatView.getWidth();
                    }
                    int deltaHorizon = destX - x;
                    if (Math.abs(deltaHorizon) < 100) {
                        mFloatViewWindowParam.leftMargin = destX;
                        mFloatView.requestLayout();
                    } else {
                        ValueAnimator animator = ValueAnimator.ofInt(x, destX);
                        animator.setInterpolator(mInterpolator);
                        if (mUpdateListener == null) {
                            mUpdateListener = new FloatAnimatorUpdateListener();
                            mUpdateListener.setUpdateView(FloatTouchListener.this);
                        }
                        animator.addUpdateListener(mUpdateListener);
                        animator.setDuration(300);
                        animator.start();
                    }
                }
                resetStatus();
                break;
            }
            default:
                break;
        }
        return result;
    }

    private void resetStatus() {
        mDownPointerId = -1;
        mPreviousX = -1;
        mPreviousY = -1;
        mHasMoved = false;
    }

    private class FloatAnimatorUpdateListener implements ValueAnimator.AnimatorUpdateListener {

        private WeakReference mListener;

        public void setUpdateView(FloatTouchListener listener) {
            mListener = new WeakReference(listener);
        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer value = (Integer) animation.getAnimatedValue();
            FloatTouchListener listener = null;
            if (mListener == null || (listener = mListener.get()) == null) {
                return;
            }
            listener.mFloatViewWindowParam.leftMargin = value;
            mFloatView.requestLayout();
        }
    }
}

注:文章参考Ruheng写的:https://www.jianshu.com/p/cc4d3c53d476,并加以修改,更易于使用。

你可能感兴趣的:(可全局拖拽、点击的View)