滑动 ViewDragHelper DrawerLayout
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了滑动 ViewDragHelper DrawerLayout相关的知识,希望对你有一定的参考价值。
概述
官方在v4的支持包中提供了ViewDragHelper这样一个类来帮助我们方便的编写自定义ViewGroup
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.Callback的回调
- int clampViewPositionHorizontal(View child, int left, int dx) Restrict the motion of the dragged child view along the horizontal axis.
- int clampViewPositionVertical(View child, int top, int dy) Restrict the motion of the dragged child view along the vertical axis.
- int getOrderedChildIndex(int index) Called to determine the Z-order of child views.
- int getViewHorizontalDragRange(View child) Return the magnitude of a draggable child view‘s horizontal range of motion in pixels.
- int getViewVerticalDragRange(View child) Return the magnitude of a draggable child view‘s vertical range of motion in pixels.
- void onEdgeDragStarted(int edgeFlags, int pointerId) Called when the user has started a deliberate drag away from one of the subscribed edges in the parent view while no child view is currently captured.
- boolean onEdgeLock(int edgeFlags) Called when the given edge may become locked.
- void onEdgeTouched(int edgeFlags, int pointerId) Called when one of the subscribed edges in the parent view has been touched by the user while no child view is currently captured.
- void onViewCaptured(View capturedChild, int activePointerId) Called when a child view is captured for dragging or settling.
- void onViewDragStateChanged(int state) Called when the drag state changes.
- void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) Called when the captured view‘s position changes as the result of a drag or settle.
- void onViewReleased(View releasedChild, float xvel, float yvel) Called when the child view is no longer being actively dragged.
- abstract boolean tryCaptureView(View child, int pointerId) Called when the user‘s input indicates that they want to capture the given child view with the pointer indicated by pointerId.
最基础的使用
/**
* 最基础的使用,不做任何特殊处理
* @author 白乾涛
*/
public class VDHLayout extends LinearLayout {
private ViewDragHelper mDragger;
public VDHLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mDragger = ViewDragHelper.create(this, //当前的ViewGroup
1.0f, //越大,mTouchSlop的值就会越小,主要用于设置 helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));
new ViewDragHelper.Callback() {//在用户的触摸过程中会回调相关方法
@Override
public boolean tryCaptureView(View child, int pointerId) {//返回ture则表示可以捕获该view,你可以根据传入的第一个view参数决定哪些可以捕获
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {//在该方法中对child移动的边界进行控制,left 为即将移动到的位置
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mDragger.shouldInterceptTouchEvent(event);//使用mDragger来决定我们是否应该拦截当前的事件
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragger.processTouchEvent(event);//通过mDragger.processTouchEvent(event)处理事件。必须返回true
return true;
}
}
功能增强
松手时自动返回到原本的位置,边界检测,控制在父控件中移动的区域
public class DragLayout2 extends LinearLayout {
private ViewDragHelper mDragger;
private View mDragView;//第一个View,就是演示简单的移动
private View mAutoBackView;//第二个View,移动后,松手自动返回到原本的位置(注意你拖动的越快,返回的越快)
private View mEdgeTrackerView;//第三个View,边界移动时对View进行捕获
private Point mAutoBackOriginPos = new Point();
public DragLayout2(Context context, AttributeSet attrs) {
super(context, attrs);
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {//可以通过ID或pointerId判断
if (new Random().nextBoolean()) return child == mDragView || child == mAutoBackView;//mEdgeTrackerView禁止直接移动
else return pointerId == 0 || pointerId == 1;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
final int leftBound = getPaddingLeft();
final int rightBound = getWidth() - child.getWidth() - leftBound;
return Math.min(Math.max(left, leftBound), rightBound);//不超过父控件的区域
}
public int clampViewPositionVertical(View child, int top, int dy) {
final int topBound = getPaddingTop();
final int bottomBound = getHeight() - child.getHeight() - topBound;
return Math.min(Math.max(top, topBound + 5), bottomBound - 10);//距离父控件上面的距离不超过5,距离父控件下面的距离不超过10
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {//手指释放的时候回调
if (releasedChild == mAutoBackView) {
mDragger.settleCapturedViewAt(mAutoBackOriginPos.x, mAutoBackOriginPos.y);//mAutoBackView在手指释放时可以自动回去
invalidate();
}
}
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {//在边界拖动时回调
//该方法可以绕过tryCaptureView,所以我们的tryCaptureView虽然并为返回true,但却不影响
mDragger.captureChildView(mEdgeTrackerView, pointerId);
}
});
mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);//设置边界检测区域
}
@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() {//settleCapturedViewAt方法内部使用的是mScroller.startScroll,所以需要结合computeScroll方法一起使用
if (mDragger.continueSettling(true)) invalidate();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {//在onLayout之后保存最开始的位置信息
super.onLayout(changed, l, t, r, b);
mAutoBackOriginPos.x = mAutoBackView.getLeft();
mAutoBackOriginPos.y = mAutoBackView.getTop();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mAutoBackView = getChildAt(1);
mEdgeTrackerView = getChildAt(2);
}
}
点击冲突与回调方法
public class DragLayout3 extends RelativeLayout {
private ViewDragHelper mDragger;
private View mDragView;//仅此View支持移动
public DragLayout3(Context context, AttributeSet attrs) {
super(context, attrs);
mDragger = ViewDragHelper.create(this, 10.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
Log.i("bqt", "【tryCaptureView】");
return mDragView != null && child == mDragView;//仅支持mDragView移动
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
Log.i("bqt", "【clampViewPositionHorizontal】");
final int leftBound = getPaddingLeft();
final int rightBound = getWidth() - child.getWidth() - leftBound;
return Math.min(Math.max(left, leftBound + 5), rightBound - 5);//自定义区域
}
public int clampViewPositionVertical(View child, int top, int dy) {
Log.i("bqt", "【clampViewPositionVertical】");
final int topBound = getPaddingTop();
final int bottomBound = getHeight() - child.getHeight() - topBound;
return Math.min(Math.max(top, topBound + 5), bottomBound - 5);//自定义区域
}
//*********************************************get回调*********************************************
@Override
public int getViewHorizontalDragRange(View child) {//方法的返回值是该childView横向或者纵向的移动的范围,当前如果只需要一个方向移动,可以只复写一个
Log.i("bqt", "【getViewHorizontalDragRange】");
return getMeasuredWidth() - child.getMeasuredWidth();
}
@Override
public int getViewVerticalDragRange(View child) {
Log.i("bqt", "【getViewVerticalDragRange】");
return getMeasuredHeight() - child.getMeasuredHeight();
}
@Override
public int getOrderedChildIndex(int index) {//改变同一个坐标去寻找captureView位置
Log.i("bqt", "【getOrderedChildIndex】");
return super.getOrderedChildIndex(index);
}
//*********************************************on回调*********************************************
@Override
public boolean onEdgeLock(int edgeFlags) {//true的时候会锁住当前的边界,false则unLock。
Log.i("bqt", "【onEdgeLock】");
return super.onEdgeLock(edgeFlags);
}
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {//在边界拖动时回调
super.onEdgeDragStarted(edgeFlags, pointerId);
Log.i("bqt", "【onEdgeDragStarted】");
}
@Override
public void onEdgeTouched(int edgeFlags, int pointerId) {//当触摸到边界时回调
super.onEdgeTouched(edgeFlags, pointerId);
Log.i("bqt", "【onEdgeTouched】");
}
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {//当captureview被捕获时回调
super.onViewCaptured(capturedChild, activePointerId);
Log.i("bqt", "【onViewCaptured】");
}
@Override
public void onViewDragStateChanged(int state) {//当ViewDragHelper状态发生变化时回调(IDLE,DRAGGING,SETTING[自动滚动时])
super.onViewDragStateChanged(state);
Log.i("bqt", "【onViewDragStateChanged】");
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {//当captureview的位置发生改变时回调
super.onViewPositionChanged(changedView, left, top, dx, dy);
Log.i("bqt", "【onViewPositionChanged】");
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {//手指释放的时候回调
super.onViewReleased(releasedChild, xvel, yvel);
Log.i("bqt", "【onViewReleased】");
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mDragger.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragger.processTouchEvent(event);
return true;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mDragView = findViewById(R.id.dragview);
}
}
最简单实用的案例
/**
* 仅id为dragview的可以移动
* @author 白乾涛
*/
public class MyDragLayout extends RelativeLayout {
private ViewDragHelper mDragger;
private View mDragView;//仅此View支持移动
public MyDragLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mDragger = ViewDragHelper.create(this, 10.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
Log.i("bqt", "【tryCaptureView】");
return mDragView != null && child == mDragView;//仅支持mDragView移动
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
Log.i("bqt", "【clampViewPositionHorizontal】");
final int leftBound = getPaddingLeft();
final int rightBound = getWidth() - child.getWidth() - leftBound;
return Math.min(Math.max(left, leftBound + 5), rightBound - 5);//自定义区域
}
public int clampViewPositionVertical(View child, int top, int dy) {
Log.i("bqt", "【clampViewPositionVertical】");
final int topBound = getPaddingTop();
final int bottomBound = getHeight() - child.getHeight() - topBound;
return Math.min(Math.max(top, topBound + 5), bottomBound - 5);//自定义区域
}
//*********************************************get回调*********************************************
@Override
public int getViewHorizontalDragRange(View child) {//方法的返回值是该childView横向或者纵向的移动的范围,当前如果只需要一个方向移动,可以只复写一个
Log.i("bqt", "【getViewHorizontalDragRange】");
return getMeasuredWidth() - child.getMeasuredWidth();
}
@Override
public int getViewVerticalDragRange(View child) {
Log.i("bqt", "【getViewVerticalDragRange】");
return getMeasuredHeight() - child.getMeasuredHeight();
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mDragger.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragger.processTouchEvent(event);
return true;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mDragView = findViewById(R.id.dragview);
}
}
以上是关于滑动 ViewDragHelper DrawerLayout的主要内容,如果未能解决你的问题,请参考以下文章
(转)ViewDragHelper实现QQ5.0侧滑并处理与ViewPager的滑动冲突
HorizontalDragLayout-模仿QQclient的Item滑动删除