出處: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);
源碼下載