ViewDragHelper 容易忽略的工具类

在Android中避免不了自定义ViewGroup,来实现我们原生控件所不能满足的需求。尤其是复杂的ViewGroup实现,手势的处理是避免不了的。我们要针对不同的ViewGroup来实现不同的onInterceptTouchEventonTouchEvent事件等。

ViewDragHelper可以帮助我们解决负责的手势操作。它是官方所提供的一个专门为自定义ViewGroup处理拖拽的手势类。下面是官方的原文引用说明

ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.

Purpose

通过这篇文章你将会掌握以下几个知识点:

  1. ViewDragHelper的简单入门
  2. ViewDragHelper的关键API用途
  3. 使用ViewDragHelper实现view的拖拽
  4. 子view交换位置

use

首先需要构建ViewDragHelper的实例,通过它的静态create方法生成

 mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
            @Override
            public boolean tryCaptureView(View child, int pointerId) {
//                return child == mDragView ;
                return true;

            }

 主要参数为ViewGroupViewDragHelper.Callback。Callback是对view操作的回调,绝对多数手势操作都是在这个回调中完成。tryCaptureView方法是它唯一的抽象方法,默认需要实现。根据参数child判断用户触摸的view是否可以进行后续操作。

为了让ViewDragHelper帮助我们简化手势操作,所以还需为它传入相关的MotionEvent

override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
    return mViewDragHelper.shouldInterceptTouchEvent(ev)
}
 
override fun onTouchEvent(event: MotionEvent?): Boolean {
    mViewDragHelper.processTouchEvent(event)
    return true
}

如果要处理惯性滑动,再重写computeScroll方法

override fun computeScroll() {
    if (mViewDragHelper.continueSettling(true)) {
        invalidate()
    }
}

Demo

 完整代码

package com.example.tt;

import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;


public class ViewDragLayout extends LinearLayout {
    private static final String TAG = "ViewDragLayout";
    private ViewDragHelper mDragger;


    private OnLongClickListener onLongClickListenner = new OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            scaleCapture(true, v);
            return true;
        }
    };

    public ViewDragLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
            @Override
            public boolean tryCaptureView(View child, int pointerId) {
//                return child == mDragView ;
                return true;

            }

            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                int paddingleft = getPaddingLeft();
                if (left < paddingleft) {
                    return paddingleft;
                }
                int pos = getWidth() - child.getWidth() - getPaddingRight();
                if (left > pos) {
                    return pos;
                }
                return left;


            }

            @Override
            public int clampViewPositionVertical(View child, int top, int dy) {
                int paddingTop = getPaddingTop();
                if (top < paddingTop) {
                    return paddingTop;
                }
                int pos = getHeight() - child.getHeight() - getPaddingBottom();
                if (top > pos) {
                    return pos;
                }

                return top;
            }

            @Override
            public void onViewDragStateChanged(int state) {
                super.onViewDragStateChanged(state);
                if (state == ViewDragHelper.STATE_IDLE) {

                } else if (state == ViewDragHelper.STATE_DRAGGING) {

                } else if (state == ViewDragHelper.STATE_SETTLING) {

                }
            }

            @Override
            public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
                super.onViewPositionChanged(changedView, left, top, dx, dy);
            }

            @Override
            public void onViewCaptured(View capturedChild, int activePointerId) {
                super.onViewCaptured(capturedChild, activePointerId);
                Log.d(TAG, "onViewCaptured:" + capturedChild.getId());
//                scaleCapture(true);
            }

            @Override
            public void onEdgeTouched(int edgeFlags, int pointerId) {
                super.onEdgeTouched(edgeFlags, pointerId);
            }

            @Override
            public boolean onEdgeLock(int edgeFlags) {
                return super.onEdgeLock(edgeFlags);
            }

            @Override
            public int getOrderedChildIndex(int index) {
                return super.getOrderedChildIndex(index);
            }

            //手指释放的时候回调
            @TargetApi(Build.VERSION_CODES.HONEYCOMB)
            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                scaleCapture(false);
                //交换位置
                int[] p = new int[2];
                releasedChild.getLocationInWindow(p);
                DragView target = getCoverView(releasedChild, p[0], p[1]);
                swipeViewIfNeed((DragView) releasedChild, target);


            }

            /**
             * 交换视图
             * @param releasedChild
             * @param target
             */
            @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
            private void swipeViewIfNeed(DragView releasedChild, final DragView target) {
                if (null != releasedChild && null != target) {
                    //place capturedView
                    mDragger.settleCapturedViewAt(target.getLeft(), target.getTop());
//                    place targetView
                    PropertyValuesHolder x = PropertyValuesHolder.ofFloat("x", target.getLeft(), releasedChild.getStartX());
                    PropertyValuesHolder y = PropertyValuesHolder.ofFloat("y", target.getTop(), releasedChild.getStartY());
                    ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(x, y).setDuration(300);
                    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            int x = ((Float) animation.getAnimatedValue("x")).intValue();
                            int y = ((Float) animation.getAnimatedValue("y")).intValue();
                            Log.d(TAG, "x," + x + ",y:" + y);
                            dragTo(target, x, y);
                        }
                    });
                    animator.start();
                    //延时
//                    mDragger.smoothSlideViewTo(target,releasedChild.getStartX(),releasedChild.getStartY());
                    invalidate();
                }
            }

            //在边界拖动时回调
            @Override
            public void onEdgeDragStarted(int edgeFlags, int pointerId) {
//                mDragger.captureChildView(mEdgeTrackerView, pointerId);
            }

            @Override
            public int getViewHorizontalDragRange(View child) {
                int ragnetH = getMeasuredWidth() - child.getMeasuredWidth();
                Log.d(TAG, "ragnetH:" + ragnetH + " child" + child.getMeasuredHeight());
                return ragnetH;
            }

            @Override
            public int getViewVerticalDragRange(View child) {
                int rangeV = getMeasuredHeight() - child.getMeasuredHeight();
                Log.d(TAG, "rangeV:" + rangeV);
                return rangeV;
            }
        });
        mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
    }

    private void dragTo(View mCapturedView, int left, int top) {
        int clampedX = left;
        int clampedY = top;
        final int oldLeft = mCapturedView.getLeft();
        final int oldTop = mCapturedView.getTop();
        ViewCompat.offsetLeftAndRight(mCapturedView, clampedX - oldLeft);
        ViewCompat.offsetTopAndBottom(mCapturedView, clampedY - oldTop);


    }

    /**
     * 简单写了下
     * @param releasedChild
     * @param xvel
     * @param yvel
     * @return
     */
    private DragView getCoverView(View releasedChild, float xvel, float yvel) {
        Rect r = new Rect();
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            if (view != releasedChild) {
                if (view.getGlobalVisibleRect(r)) {
                    if (r.contains((int) xvel, (int) yvel))
                        return (DragView) view;
                }
            }
        }
        return null;
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void scaleCapture(boolean scale) {
        View capturedView = mDragger.getCapturedView();
        if (null != capturedView) {
            Log.d(TAG, "scaleCapture:" + scale);
            capturedView.setScaleX(scale ? 0.8f : 1);
            capturedView.setScaleY(scale ? 0.8f : 1);
            capturedView.invalidate();
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void scaleCapture(boolean scale, View capturedView) {
        if (null != capturedView) {
            Log.d(TAG, "scaleCapture:" + scale);
            capturedView.setScaleX(scale ? 0.8f : 1);
            capturedView.setScaleY(scale ? 0.8f : 1);
            capturedView.invalidate();
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (null == mDragger)
            return false;
        boolean intercept = mDragger.shouldInterceptTouchEvent(event);
        Log.d(TAG, "onInterceptTouchEvent:" + intercept + " action:" + event.getAction());
        return intercept;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "onTouchEvent parent:" + " action:" + event.getAction());
        mDragger.processTouchEvent(event);
        return true;
    }

    @Override
    public void computeScroll() {
        if (null != mDragger)
            if (mDragger.continueSettling(true)) {
                invalidate();
            }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        Log.d(TAG, "onLayout:");

    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            view.setOnLongClickListener(onLongClickListenner);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/TyearLin/article/details/120370998