开发中,我们经常会有拖动View的一些效果,比如
- 1.跟着手指移动的小球
- 2.抽屉效果
- 3.手指拨动后就返回上一个页面的效果
这些效果如果完全靠自己自定义ViewGroup,然后重写onTouchEvent以及onInterceptTouchEvent来完成会非常的麻烦,挑战性很大。但是假如有了ViewGragHelper的帮助,会变得简单很多。所以,可以看到ViewDragHelper一般出现在自定义ViewGroup中,帮助我们快速的处理一些手势相关的效果。
先上图看这个例子:
- 第一个View,就是演示简单的移动
- 第二个View,演示除了移动后,松手自动返回到原本的位置。(注意你拖动的越快,返回的越快)
- 第三个View,边界移动时对View进行捕获。
实现步骤
1.自定义ViewGroup,继承LinearLayout
public class VDHLayout extends LinearLayout
2.在构造方法中创建ViewGragHelper对象,同时设置屏幕左边缘可以被追踪:
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
.... //这里面会覆写很多方法
....//这里面会覆写很多方法
....//这里面会覆写很多方法
....//这里面会覆写很多方法
});
mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
这里的第二参数是1.0f代表sensitivity, 主要用于设置touchslop
3.要想把一系列的事件交给ViewDragHelper处理的话,需要重写以下方法:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mDragger.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragger.processTouchEvent(event);
return true;
}
4.这一步就是要override callback方法,来实现对ViewGroup下的子View的控制。
4.1获取子View的对象
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mDragView = getChildAt(0);
mAutoBackView = getChildAt(1);
mEdgeTrackerView = getChildAt(2);
}
4.2由于第二个View当被拖动以后需要回到原来的位置,所以需要记录当前的位置, 覆写onLayout方法
private Point mAutoBackOriginPos = new Point();
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mAutoBackOriginPos.x = mAutoBackView.getLeft();
mAutoBackOriginPos.y = mAutoBackView.getTop();
}
4.3由于第三个View在手指触摸它的时候不会移动,所以
@Override
public boolean tryCaptureView(View child, int pointerId) {
//mEdgeTrackerView禁止直接移动
return child == mDragView || child == mAutoBackView;
}
也就是需要触发touch事件的view需要覆写这个方法,并且要指定childview
4.4手指释放后,弹回原来的位置
//手指释放的时候回调
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//mAutoBackView手指释放时可以自动回去
if (releasedChild == mAutoBackView) {
mDragger.settleCapturedViewAt(mAutoBackOriginPos.x, mAutoBackOriginPos.y);
invalidate();
}
}
注意由于这里使用的是invalidate方法,所以需要重写
@Override
public void computeScroll() {
if (mDragger.continueSettling(true)) {
invalidate();
}
}
4.5指定边界能拖动的View
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
mDragger.captureChildView(mEdgeTrackerView, pointerId);
}
4.6当想要控制View的移动边界的时候,可以:
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//只在ViewGroup的内部移动
final int leftBound = getPaddingLeft();
final int rightBound = getWidth() - child.getWidth() - getPaddingRight();
//这个写法不懂
//我希望只在ViewGroup的内部移动,
// 即:最小>=paddingleft,最大<=ViewGroup.getWidth()-paddingright-child.getWidth。
final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
到此,我们列一下所有的Callback方法,看看还有哪些没用过的:
- onViewDragStateChanged
当ViewDragHelper状态发生变化时回调(IDLE,DRAGGING,SETTING[自动滚动时])
- onViewPositionChanged
当captureview的位置发生改变时回调
- onViewCaptured
当captureview被捕获时回调
onViewReleased 已用
- onEdgeTouched
当触摸到边界时回调。
- onEdgeLock
true的时候会锁住当前的边界,false则unLock。
onEdgeDragStarted 已用
- getOrderedChildIndex
改变同一个坐标(x,y)去寻找captureView位置的方法。(具体在:findTopChildUnder方法中)
getViewHorizontalDragRange 已用
getViewVerticalDragRange 已用
tryCaptureView 已用
clampViewPositionHorizontal 已用
clampViewPositionVertical 已用
ok,至此所有的回调方法都有了一定的认识。
5.项目源码
源码参考我的github地址
参考文章:
http://blog.csdn.net/lmj623565791/article/details/46858663