【Android 笔记】ViewDragHelper
ViewDragHelper
用于实现视图的拖揣、滑动等功能的类。
Demo
实现一个有侧滑菜单功能 ViewGroup, ViewGroup 当中包含 MenuView 和 MainView 两个子View。这里实现往右拖揣 MainView 出现 MenuView, 往左拖揣 MenuView 和 MainView 隐藏 MenuView。
步骤
- 初始化
ViewDragHelper.create(ViewGroup forParent, float sensitivity, Callback callback)
参数1 ViewGroup
参数2 拖动 View 的灵敏度,0~1范围,1最灵敏
参数3 需要实现的 callback - 将拦截事件及触摸事件传递给 ViewDragHelper
- 重写 computeScroll 保证滑动效果
- 实现 Callback 中的部分方法
这里实现
tryCaptureView(),决定哪些 View 可以拖揣
clampViewPositionHorizontal(),返回 View 的 left 坐标以实现水平滑动效果
onViewReleased(),释放 View 时调用
onViewPositionChanged(),View 位置变化时使用。在我的测试中只要在tryCaptureView 中允许某 View 可以被拖揣,则拖揣该 View 时就会调用此方法,不管 View 是否有位置变化。
效果
代码
/**
* @author Vecrates.
* @describe
*/
public class ViewDragHelperTest extends FrameLayout{
private ViewDragHelper mViewDragHelper;
private View mMenuView;
private View mMainView;
public ViewDragHelperTest(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ViewDragHelperTest(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mViewDragHelper = ViewDragHelper.create(this, 1f, new ViewDragCallBack());
mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMenuView = getChildAt(0);
mMainView = getChildAt(1);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mViewDragHelper.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
private class ViewDragCallBack extends ViewDragHelper.Callback{
/**
* 确定哪一个view可以被拖揣
* @param child
* @param pointerId
* @return
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
/**
* 控制水平方向的移动
* @param child
* @param left 控件的left
* @param dx 水平距离
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if(child == mMainView) {
//主页面不可以往左侧拉,能往右侧拉50%
if(left < getPaddingLeft()) {
left = getPaddingLeft();
}else if(left > getWidth() * 0.5f) {
left = (int) (getWidth() * 0.5f);
}
} else if(child == mMenuView) {
//菜单可以往左侧拉不可以往右侧拉
if(left > getPaddingLeft()) {
left = getPaddingLeft();
}
}
return left;
}
/**
* 释放view时
* @param releasedChild
* @param xvey
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvey, float yvel) {
if(mMainView.getLeft() < getWidth() * 0.5f) {
mViewDragHelper.smoothSlideViewTo(mMainView, getPaddingLeft(), getPaddingTop());
ViewCompat.postInvalidateOnAnimation(ViewDragHelperTest.this);
} else {
mViewDragHelper.smoothSlideViewTo(mMainView, (int) (getWidth() * 0.5f), getPaddingTop());
ViewCompat.postInvalidateOnAnimation(ViewDragHelperTest.this);
}
}
/**
* view位置变化时调用
* 亲测只要允许拖揣该view,在拖揣时就会调用此方法
* @param changedView
* @param left
* @param top
* @param dx
* @param dy
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
//这里实现拖动菜单向左可以把主页面拉回来
if(changedView == mMenuView && left < getPaddingLeft()) {
mViewDragHelper.smoothSlideViewTo(mMainView, getPaddingLeft(), getPaddingTop());
mMenuView.layout(getPaddingLeft(), getPaddingTop(), mMenuView.getWidth(), mMenuView.getHeight());
ViewCompat.postInvalidateOnAnimation(ViewDragHelperTest.this);
}
}
}
/**
* view onDraw时调用此方法
*/
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
//滚动未停止时刷新界面,保证滑动效果
ViewCompat.postInvalidateOnAnimation(ViewDragHelperTest.this);
}
}
}
补充:
tryCaptureView(View,int) ,传递当前触摸的子View实例,如果当前的子View需要进行拖拽移动返回true
clampViewPositionHorizontal,决定拖拽的View在水平方向上面能移动到的位置
clampViewPositionVertical,决定拖拽的View在垂直方向上面能移动到的位置
getViewHorizontalDragRange ,返回一个大于0的数,然后才会在水平方向移动(如果 ViewGroup 中有 Button,并且onClickable=true)
getViewVerticalDragRange,返回一个大于0的数,然后才会在垂直方向移动(如果 ViewGroup 中有 Button,并且onClickable=true)
onViewDragStateChanged,拖拽状态发生变化回调
onViewPositionChanged,当拖拽的View的位置发生变化的时候回调(特指capturedview)
onViewCaptured,捕获captureview的时候回调
onViewReleased,当拖拽的View手指释放的时候回调
onEdgeTouched,当触摸屏幕边界的时候回调
onEdgeLock,是否锁住边界
onEdgeDrageStarted,在边缘滑动的时候可以设置滑动另一个子View跟着滑动