出處:ViewDragHelper是V4包下的一個文件。java
咱們在自定義ViewGroup的時候,有時候以爲很頭疼,其中很大一部分緣由就是由於事件處理太麻煩,須要記錄大量的成員變量,還有各類判斷等等。
Google也感受到了這個麻煩,因此ViewDragHelper就出現了,ViewDragHelper功能究竟是什麼呢?從字面意思上看是View拖拽的幫助類,簡而言之就是,在簡化View拖拽的時候的代碼量。咱們先來看一看到底這個類的幫助有多大?
先來看一個測拉菜單效果
先來分析一下,若是咱們不借助這個幫助類實現狀況:
一、重寫一個RelativeLayout;
二、重寫其中的onInterceptTouchEvent(作相應的事件攔截操做)
二、重寫其中的onTouchEvent方法(這裏面作大量的代碼)
三、定義一個Scroller變量,用來控制手指鬆開之後的操做
這裏我就不去寫代碼了,代碼量確定很大!
再來看看藉助ViewDragHelper類實現的代碼sql
/** * 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的使用效果,咱們再來看看它的用法,ide
/** * 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; }
或post
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的運用。this
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; } }
看了以上註釋,再回過頭來看上面的例子是否是以爲很簡單。spa
總結:.net
一、ViewDragHelper的做用是一個簡化View拖動的幫助類
二、ViewDragHelper大部分用在自定義ViewGroup中
三、ViewDragHelper的實例化經過code
create(ViewGroup forParent, float sensitivity, Callback cb)
方法建立
四、在自定義的ViewGroup中的onInterceptTouchEvent方法中別忘記調用ViewDragHelper中的shouldInterceptTouchEvent(ev),
同理onTouchEvent(MotionEvent event)中須要調用
ViewDragHelper.processTouchEvent(event);server