• 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

  • 相关阅读:
    COMPUTE BY 子句 cube 子句
    性能计数器 可接受值 或者说参考范围
    我们也来hold住优化SQL SERVER锁的使用
    反编译工具源代码
    图片居中,图片垂直居中,图片水平居中
    SessionStateTempDataProvider类需要启用会话状态(2)
    是我Out了,还是IT市场真井喷了?
    四天玩转 Windows Phone 开发
    从物理结构说sql server 优化
    SEO和百度
  • 原文地址:https://www.cnblogs.com/shanzei/p/4654213.html
Copyright © 2020-2023  润新知