Android -- ViewDragHelper
ViewDragHelper
SlidingPaneLayout和DrawerLayout,现在这俩个类被广泛的运用,其实研究他们的源码你会发现这两个类都运用了ViewDragHelper来处理拖动。
ViewDragHelper并不是第一个用于分析手势处理的类,gesturedetector也是,但是在和拖动相关的手势分析方面gesturedetector只能说是勉为其难。
好的特点
-
ViewDragHelper.Callback是连接ViewDragHelper与view之间的桥梁(这个view一般是指拥子view的容器即parentView)
-
ViewDragHelper可以检测到是否触及到边缘
-
ViewDragHelper并不是直接作用于要被拖动的View,而是使其控制的视图容器中的子View可以被拖动,如果要指定某个子view的行为,需要在Callback中想办法;
-
ViewDragHelper的本质其实是分析onInterceptTouchEvent和onTouchEvent的MotionEvent参数,然后根据分析的结果去改变一个容器中被拖动子View的位置( 通过offsetTopAndBottom(int offset)和offsetLeftAndRight(int offset)方法 ),他能在触摸的时候判断当前拖动的是哪个子View;
虽然ViewDragHelper的实例方法 ViewDragHelper.create(ViewGroup forParent, Callback cb) 可以指定一个被ViewDragHelper处理拖动事件的对象 ,但ViewDragHelper类的设计决定了其适用于被包含在一个自定义ViewGroup之中,而不是对任意一个布局上的视图容器使用ViewDragHelper。
使用
public class VDHLayout extends LinearLayout{
private ViewDragHelper mDragger;
public VDHLayout(Context context, AttributeSet attrs){
super(context, attrs);
//第二个参数就是滑动灵敏度的意思
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback(){
//这个地方实际上函数返回值为true就代表可以滑动 为false 则不能滑动
@Override
public boolean tryCaptureView(View child, int pointerId){
return true;
}
//这个地方实际上left就代表 你将要移动到的位置的坐标。返回值就是最终确定的移动的位置。
// 我们要让view滑动的范围在我们的layout之内
//实际上就是判断如果这个坐标在layout之内 那我们就返回这个坐标值。
//如果这个坐标在layout的边界处 那我们就只能返回边界的坐标给他。不能让他超出这个范围
//除此之外就是如果你的layout设置了padding的话,也可以让子view的活动范围在padding之内的.
@Override
public int clampViewPositionHorizontal(View child, int left, int dx){
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy){
return top;
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event){
return mDragger.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event){
mDragger.processTouchEvent(event);
return true;
}
}
触摸相关
onInterceptTouchEvent中通过使用mDragger.shouldInterceptTouchEvent(event)来决定我们是否应该拦截当前的事件。onTouchEvent中通过mDragger.processTouchEvent(event)处理事件。
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
final int leftBound = getPaddingLeft();
final int rightBound = getWidth() - mDragView.getWidth();
final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}

@Override
public int clampViewPositionVertical(View child, int top, int dy) {
final int topBound = getPaddingTop();
final int bottomBound = getHeight() - mDragView.getHeight();
final int newTop = Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}

Code
public class VDHLayout extends LinearLayout{
private ViewDragHelper mDragger;
private View mDragView;
private View mAutoBackView;
private View mEdgeTrackerView;
private Point mAutoBackOriginPos = new Point();
public VDHLayout(Context context, AttributeSet attrs){
super(context, attrs);
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback(){
@Override
public boolean tryCaptureView(View child, int pointerId){
//mEdgeTrackerView禁止直接移动
return child == mDragView || child == mAutoBackView;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx){
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy){
return top;
}
//手指释放的时候回调
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel){
//mAutoBackView手指释放时可以自动回去
if (releasedChild == mAutoBackView){
mDragger.settleCapturedViewAt(mAutoBackOriginPos.x, mAutoBackOriginPos.y);
invalidate();
}
}
//在边界拖动时回调
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId){
mDragger.captureChildView(mEdgeTrackerView, pointerId);
}
});
mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event){
return mDragger.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event){
mDragger.processTouchEvent(event);
return true;
}
@Override
public void computeScroll(){
if(mDragger.continueSettling(true)){
invalidate();
}
}
@Override
protected
