程序员人生 网站导航

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

栏目:综合技术时间:2015-04-27 08:27:19

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

我们在自定义ViewGroup的时候,有时候觉得很头疼,其中很大1部份缘由就是由于事件处理太麻烦,需要记录大量的成员变量,还有各种判断等等。
Google也感觉到了这个麻烦,所以ViewDragHelper就出现了,ViewDragHelper功能究竟是甚么呢?从字面意思上看是View拖拽的帮助类,简而言之就是,在简化View拖拽的时候的代码量。我们先来看1看到底这个类的帮助有多大?
先来看1个测拉菜单效果
这里写图片描述
先来分析1下,如果我们不借助这个帮助类实现情况:
1、重写1个RelativeLayout;
2、重写其中的onInterceptTouchEvent(做相应的事件拦截操作)
2、重写其中的onTouchEvent方法(这里面做大量的代码)
3、定义1个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类的1个说明。大致意思实说ViewDragHelper是自定义ViewGroup的1个工具类,在我们对子View拖拽或复位的时候它提供了1系列有用的操作。
从这段话中我们可以知道1个信息,这个类大部份用于自定义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); }

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

public static abstract class Callback { //但拖拽状态改变的时候会触发这个方法,比如:从1开始不能拖拽,到拖拽 public void onViewDragStateChanged(int state) {} //关键方法:顾名思义,这里就是记录了1些值得变化,可用于我们处理其他的操作,比如,改变背景色彩 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的作用是1个简化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);

源码下载

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐