• ViewDragHelper让你处理View拖动时,代码减半!


    出处:ViewDragHelper是V4包下的一个文件。

    我们在自定义ViewGroup的时候,有时候觉得很头疼,其中很大一部分原因就是因为事件处理太麻烦,需要记录大量的成员变量,还有各种判断等等。 
    Google也感觉到了这个麻烦,所以ViewDragHelper就出现了,ViewDragHelper功能到底是什么呢?从字面意思上看是View拖拽的帮助类,简而言之就是,在简化View拖拽的时候的代码量。我们先来看一看到底这个类的帮助有多大? 
    先来看一个测拉菜单效果 
    这里写图片描述 
    先来分析一下,如果我们不借助这个帮助类实现情况: 
    1、重写一个RelativeLayout; 
    2、重写其中的onInterceptTouchEvent(做相应的事件拦截操作) 
    2、重写其中的onTouchEvent方法(这里面做大量的代码) 
    3、定义一个Scroller变量,用来控制手指松开以后的操作 
    这里我就不去写代码了,代码量肯定很大! 
    再来看看借助ViewDragHelper类实现的代码

    /**
     * Created by gyzhong on 15/4/8.
     */
    public class VdhLayout01 extends RelativeLayout {
    
        private ViewDragHelper mViewDragHelper;
        private View mCaptureView;
        private float mInitialMotionX;
        private float mInitialMotionY;
        private boolean mIsUnableToDrag;
        private int mSlideRange;
        private float mSlideOffset;
    
        public VdhLayout01(Context context) {
            this(context, null);
        }
    
        public VdhLayout01(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public VdhLayout01(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context);
        }
    
        private void initView(Context context) {
            mViewDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCall());
        }
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            mCaptureView = findViewById(R.id.id_capture_view);
            TextView textView = (TextView) findViewById(R.id.id_text);
            textView.setText(Shakespeare.DIALOGUE[0]);
        }
        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            mCaptureView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    mCaptureView.getViewTreeObserver().removeOnPreDrawListener(this);
                    mSlideRange = mCaptureView.getMeasuredWidth();
                    return false;
                }
            });
        }
    
        private class DragHelperCall extends ViewDragHelper.Callback {
            @Override
            public boolean tryCaptureView(View child, int pointerId) {
                return child == mCaptureView;
            }
            @Override
            public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
                super.onViewPositionChanged(changedView, left, top, dx, dy);
                mSlideOffset = left * 1.0f / mSlideRange*2;
            }
            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
    
                return clamp(left, 0, mSlideRange / 2);
            }
            @Override
            public int getViewHorizontalDragRange(View child) {
                return mSlideRange/2;
            }
    
            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                int finalLeft;
                if (xvel > 0 || xvel == 0 && mSlideOffset > .5f) {
                    finalLeft = mSlideRange/2  ;
                }else {
                    finalLeft = 0 ;
                }
                mViewDragHelper.settleCapturedViewAt( finalLeft, mCaptureView.getTop());
                invalidate();
            }
        }
        @Override
        public void computeScroll() {
            if (mViewDragHelper.continueSettling(true)){
                ViewCompat.postInvalidateOnAnimation(this);
            }
        }
    
        private int clamp(int value, int min, int max) {
            return Math.min(max, Math.max(min, value));
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            final int action = MotionEventCompat.getActionMasked(ev);
            if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
                mViewDragHelper.cancel();
                return false;
            }
            if (!isEnabled() || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) {
                mViewDragHelper.cancel();
                return super.onInterceptTouchEvent(ev);
            }
            int index = MotionEventCompat.getActionIndex(ev) ;
            switch (action) {
                case MotionEvent.ACTION_DOWN: {
                    final float x = ev.getX();
                    final float y = ev.getY();
                    mInitialMotionX = x;
                    mInitialMotionY = y;
                    mIsUnableToDrag = false;
                    break;
                }
                case MotionEvent.ACTION_MOVE: {
                    final float x = ev.getX();
                    final float y = ev.getY();
                    final float adx = Math.abs(x - mInitialMotionX);
                    final float ady = Math.abs(y - mInitialMotionY);
                    int slop = mViewDragHelper.getTouchSlop();
                    if (adx > slop && adx < ady) {
                        mIsUnableToDrag = true;
                        mViewDragHelper.cancel();
                        return false;
                    }
                    break;
                }
            }
            return mViewDragHelper.shouldInterceptTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            mViewDragHelper.processTouchEvent(event);
            return true;
        }
    }

    可以看到这里我们只是处理了事件拦截的操作,因为这里涉及到了ScrollView,如果没有涉及到事件拦截的话,代码量更简单!而最最复杂的onTouchEvent方法,我们什么都没做,只是让他交给ViewDragHelper类去处理。 
    如果看了DrawerLayout和SlidingPaneLayout源码的朋友应该知道,这两个控件就使用了这个帮助类。 
    看了ViewDragHelper的使用效果,我们再来看看它的用法,

    /**
     * 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.
     */

    上面那段话是ViewDragHelper类的一个说明。大致意思实说ViewDragHelper是自定义ViewGroup的一个工具类,在我们对子View拖拽或者复位的时候它提供了一系列有用的操作。 
    从这段话中我们可以知道一个信息,这个类大部分用于自定义ViewGroup中,当然像上面的例子,重写RelativeLayout其实也相当于自定义ViewGroup。 
    实例化ViewDragHelper,ViewDragHelper的构造方法私有化了,所以我们不能直接new,需要通过

    public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb) {
            //...
            return helper;
    }

    public static ViewDragHelper create(ViewGroup forParent, Callback cb) {
            return new ViewDragHelper(forParent.getContext(), forParent, cb);
    }

    来实例化,这列有三个参数,分别代表什么意思呢? 
    ViewGroup forParent 就是我们自定义的ViewGroup 
    float sensitivity 是一个拖拽的灵敏度 
    Callback cb 是ViewDragHelper中定义的一个抽象类,需要我们在自定义的ViewGroup中重写它,而核心的操作也就在在各类中,了解了这个类中的方法,就基本掌握这个Helper的运用。

    public static abstract class Callback {
            //但拖拽状态改变的时候会触发这个方法,比如:从一开始不能拖拽,到拖拽
            public void onViewDragStateChanged(int state) {}
    
            //关键方法:顾名思义,这里就是记录了一些值得变化,可用于我们处理其他的操作,比如,改变背景颜色
            public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {}
    
            //这个方法很少用到,意义不大,可忽略
            public void onViewCaptured(View capturedChild, int activePointerId) {}
    
            //关键方法,手指松开会触发这个方法,做复位操作就在此方法中实现
            public void onViewReleased(View releasedChild, float xvel, float yvel) {}
    
            //当边缘被触摸的时候调用
            public void onEdgeTouched(int edgeFlags, int pointerId) {}
    
            //边缘设置不可用,
            public boolean onEdgeLock(int edgeFlags) {
                return false;
            }
    
            //这个方法也很少用到
            public void onEdgeDragStarted(int edgeFlags, int pointerId) {}
    
            //给自定义的ViewGroup中的字View 从新排序
            public int getOrderedChildIndex(int index) {
                return index;
            }
    
            //关键方法:设置水平拖动的距离
            public int getViewHorizontalDragRange(View child) {
                return 0;
            }
    
            //关键方法:设置垂直拖动的距离 0 表示不可拖动
            public int getViewVerticalDragRange(View child) {
                return 0;
            }
    
            //关键方法:返回true表示可以拖动
            public abstract boolean tryCaptureView(View child, int pointerId);
    
            //关键方法:重新定位水平移动的位置,返回left表示不受限制
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                return 0;
            }
            //关键方法:重新定位垂直移动的位置,返回top表示不受限制
            public int clampViewPositionVertical(View child, int top, int dy) {
                return 0;
            }
    }

    看了以上注释,再回过头来看上面的例子是不是觉得很简单。

    总结:

    1、ViewDragHelper的作用是一个简化View拖动的帮助类 
    2、ViewDragHelper大部分用在自定义ViewGroup中 
    3、ViewDragHelper的实例化通过

    create(ViewGroup forParent, float sensitivity, Callback cb) 

    方法创建 
    4、在自定义的ViewGroup中的onInterceptTouchEvent方法中别忘记调用ViewDragHelper中的shouldInterceptTouchEvent(ev), 
    同理onTouchEvent(MotionEvent event)中需要调用 
    ViewDragHelper.processTouchEvent(event);

    源码下载

    http://blog.csdn.net/jxxfzgy/article/details/44954705

  • 相关阅读:
    HDU 1495 非常可乐
    ja
    Codeforces Good Bye 2016 E. New Year and Old Subsequence
    The 2019 Asia Nanchang First Round Online Programming Contest
    Educational Codeforces Round 72 (Rated for Div. 2)
    Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises)
    AtCoder Regular Contest 102
    AtCoder Regular Contest 103
    POJ1741 Tree(点分治)
    洛谷P2634 [国家集训队]聪聪可可(点分治)
  • 原文地址:https://www.cnblogs.com/shanzei/p/4654213.html
Copyright © 2020-2023  润新知