滑动 ViewDragHelper DrawerLayout

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了滑动 ViewDragHelper DrawerLayout相关的知识,希望对你有一定的参考价值。


概述

官方在v4的支持包中提供了ViewDragHelper这样一个类来帮助我们方便的编写自定义ViewGroup
  1. ViewDragHelper is a utility class for writing custom ViewGroups. 
  2. It offers a number of useful operations and state tracking 
  3. 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.

最基础的使用

  1. /**
  2. * 最基础的使用,不做任何特殊处理
  3. * @author 白乾涛
  4. */
  5. public class VDHLayout extends LinearLayout {
  6. private ViewDragHelper mDragger;
  7. public VDHLayout(Context context, AttributeSet attrs) {
  8. super(context, attrs);
  9. mDragger = ViewDragHelper.create(this, //当前的ViewGroup
  10. 1.0f, //越大,mTouchSlop的值就会越小,主要用于设置 helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));
  11. new ViewDragHelper.Callback() {//在用户的触摸过程中会回调相关方法
  12. @Override
  13. public boolean tryCaptureView(View child, int pointerId) {//返回ture则表示可以捕获该view,你可以根据传入的第一个view参数决定哪些可以捕获
  14. return true;
  15. }
  16. @Override
  17. public int clampViewPositionHorizontal(View child, int left, int dx) {//在该方法中对child移动的边界进行控制,left 为即将移动到的位置
  18. return left;
  19. }
  20. @Override
  21. public int clampViewPositionVertical(View child, int top, int dy) {
  22. return top;
  23. }
  24. });
  25. }
  26. @Override
  27. public boolean onInterceptTouchEvent(MotionEvent event) {
  28. return mDragger.shouldInterceptTouchEvent(event);//使用mDragger来决定我们是否应该拦截当前的事件
  29. }
  30. @Override
  31. public boolean onTouchEvent(MotionEvent event) {
  32. mDragger.processTouchEvent(event);//通过mDragger.processTouchEvent(event)处理事件。必须返回true
  33. return true;
  34. }
  35. }

功能增强

松手时自动返回到原本的位置,边界检测,控制在父控件中移动的区域
  1. public class DragLayout2 extends LinearLayout {
  2. private ViewDragHelper mDragger;
  3. private View mDragView;//第一个View,就是演示简单的移动
  4. private View mAutoBackView;//第二个View,移动后,松手自动返回到原本的位置(注意你拖动的越快,返回的越快)
  5. private View mEdgeTrackerView;//第三个View,边界移动时对View进行捕获
  6. private Point mAutoBackOriginPos = new Point();
  7. public DragLayout2(Context context, AttributeSet attrs) {
  8. super(context, attrs);
  9. mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
  10. @Override
  11. public boolean tryCaptureView(View child, int pointerId) {//可以通过ID或pointerId判断
  12. if (new Random().nextBoolean()) return child == mDragView || child == mAutoBackView;//mEdgeTrackerView禁止直接移动
  13. else return pointerId == 0 || pointerId == 1;
  14. }
  15. @Override
  16. public int clampViewPositionHorizontal(View child, int left, int dx) {
  17. final int leftBound = getPaddingLeft();
  18. final int rightBound = getWidth() - child.getWidth() - leftBound;
  19. return Math.min(Math.max(left, leftBound), rightBound);//不超过父控件的区域
  20. }
  21. public int clampViewPositionVertical(View child, int top, int dy) {
  22. final int topBound = getPaddingTop();
  23. final int bottomBound = getHeight() - child.getHeight() - topBound;
  24. return Math.min(Math.max(top, topBound + 5), bottomBound - 10);//距离父控件上面的距离不超过5,距离父控件下面的距离不超过10
  25. }
  26. @Override
  27. public void onViewReleased(View releasedChild, float xvel, float yvel) {//手指释放的时候回调
  28. if (releasedChild == mAutoBackView) {
  29. mDragger.settleCapturedViewAt(mAutoBackOriginPos.x, mAutoBackOriginPos.y);//mAutoBackView在手指释放时可以自动回去
  30. invalidate();
  31. }
  32. }
  33. @Override
  34. public void onEdgeDragStarted(int edgeFlags, int pointerId) {//在边界拖动时回调
  35. //该方法可以绕过tryCaptureView,所以我们的tryCaptureView虽然并为返回true,但却不影响
  36. mDragger.captureChildView(mEdgeTrackerView, pointerId);
  37. }
  38. });
  39. mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);//设置边界检测区域
  40. }
  41. @Override
  42. public boolean onInterceptTouchEvent(MotionEvent event) {
  43. return mDragger.shouldInterceptTouchEvent(event);
  44. }
  45. @Override
  46. public boolean onTouchEvent(MotionEvent event) {
  47. mDragger.processTouchEvent(event);
  48. return true;
  49. }
  50. @Override
  51. public void computeScroll() {//settleCapturedViewAt方法内部使用的是mScroller.startScroll,所以需要结合computeScroll方法一起使用
  52. if (mDragger.continueSettling(true)) invalidate();
  53. }
  54. @Override
  55. protected void onLayout(boolean changed, int l, int t, int r, int b) {//在onLayout之后保存最开始的位置信息
  56. super.onLayout(changed, l, t, r, b);
  57. mAutoBackOriginPos.x = mAutoBackView.getLeft();
  58. mAutoBackOriginPos.y = mAutoBackView.getTop();
  59. }
  60. @Override
  61. protected void onFinishInflate() {
  62. super.onFinishInflate();
  63. mAutoBackView = getChildAt(1);
  64. mEdgeTrackerView = getChildAt(2);
  65. }
  66. }

点击冲突与回调方法

  1. public class DragLayout3 extends RelativeLayout {
  2. private ViewDragHelper mDragger;
  3. private View mDragView;//仅此View支持移动
  4. public DragLayout3(Context context, AttributeSet attrs) {
  5. super(context, attrs);
  6. mDragger = ViewDragHelper.create(this, 10.0f, new ViewDragHelper.Callback() {
  7. @Override
  8. public boolean tryCaptureView(View child, int pointerId) {
  9. Log.i("bqt", "【tryCaptureView】");
  10. return mDragView != null && child == mDragView;//仅支持mDragView移动
  11. }
  12. @Override
  13. public int clampViewPositionHorizontal(View child, int left, int dx) {
  14. Log.i("bqt", "【clampViewPositionHorizontal】");
  15. final int leftBound = getPaddingLeft();
  16. final int rightBound = getWidth() - child.getWidth() - leftBound;
  17. return Math.min(Math.max(left, leftBound + 5), rightBound - 5);//自定义区域
  18. }
  19. public int clampViewPositionVertical(View child, int top, int dy) {
  20. Log.i("bqt", "【clampViewPositionVertical】");
  21. final int topBound = getPaddingTop();
  22. final int bottomBound = getHeight() - child.getHeight() - topBound;
  23. return Math.min(Math.max(top, topBound + 5), bottomBound - 5);//自定义区域
  24. }
  25. //*********************************************get回调*********************************************
  26. @Override
  27. public int getViewHorizontalDragRange(View child) {//方法的返回值是该childView横向或者纵向的移动的范围,当前如果只需要一个方向移动,可以只复写一个
  28. Log.i("bqt", "【getViewHorizontalDragRange】");
  29. return getMeasuredWidth() - child.getMeasuredWidth();
  30. }
  31. @Override
  32. public int getViewVerticalDragRange(View child) {
  33. Log.i("bqt", "【getViewVerticalDragRange】");
  34. return getMeasuredHeight() - child.getMeasuredHeight();
  35. }
  36. @Override
  37. public int getOrderedChildIndex(int index) {//改变同一个坐标去寻找captureView位置
  38. Log.i("bqt", "【getOrderedChildIndex】");
  39. return super.getOrderedChildIndex(index);
  40. }
  41. //*********************************************on回调*********************************************
  42. @Override
  43. public boolean onEdgeLock(int edgeFlags) {//true的时候会锁住当前的边界,false则unLock。
  44. Log.i("bqt", "【onEdgeLock】");
  45. return super.onEdgeLock(edgeFlags);
  46. }
  47. @Override
  48. public void onEdgeDragStarted(int edgeFlags, int pointerId) {//在边界拖动时回调
  49. super.onEdgeDragStarted(edgeFlags, pointerId);
  50. Log.i("bqt", "【onEdgeDragStarted】");
  51. }
  52. @Override
  53. public void onEdgeTouched(int edgeFlags, int pointerId) {//当触摸到边界时回调
  54. super.onEdgeTouched(edgeFlags, pointerId);
  55. Log.i("bqt", "【onEdgeTouched】");
  56. }
  57. @Override
  58. public void onViewCaptured(View capturedChild, int activePointerId) {//当captureview被捕获时回调
  59. super.onViewCaptured(capturedChild, activePointerId);
  60. Log.i("bqt", "【onViewCaptured】");
  61. }
  62. @Override
  63. public void onViewDragStateChanged(int state) {//当ViewDragHelper状态发生变化时回调(IDLE,DRAGGING,SETTING[自动滚动时])
  64. super.onViewDragStateChanged(state);
  65. Log.i("bqt", "【onViewDragStateChanged】");
  66. }
  67. @Override
  68. public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {//当captureview的位置发生改变时回调
  69. super.onViewPositionChanged(changedView, left, top, dx, dy);
  70. Log.i("bqt", "【onViewPositionChanged】");
  71. }
  72. @Override
  73. public void onViewReleased(View releasedChild, float xvel, float yvel) {//手指释放的时候回调
  74. super.onViewReleased(releasedChild, xvel, yvel);
  75. Log.i("bqt", "【onViewReleased】");
  76. }
  77. });
  78. }
  79. @Override
  80. public boolean onInterceptTouchEvent(MotionEvent event) {
  81. return mDragger.shouldInterceptTouchEvent(event);
  82. }
  83. @Override
  84. public boolean onTouchEvent(MotionEvent event) {
  85. mDragger.processTouchEvent(event);
  86. return true;
  87. }
  88. @Override
  89. protected void onFinishInflate() {
  90. super.onFinishInflate();
  91. mDragView = findViewById(R.id.dragview);
  92. }
  93. }

最简单实用的案例

  1. /**
  2. * 仅id为dragview的可以移动
  3. * @author 白乾涛
  4. */
  5. public class MyDragLayout extends RelativeLayout {
  6. private ViewDragHelper mDragger;
  7. private View mDragView;//仅此View支持移动
  8. public MyDragLayout(Context context, AttributeSet attrs) {
  9. super(context, attrs);
  10. mDragger = ViewDragHelper.create(this, 10.0f, new ViewDragHelper.Callback() {
  11. @Override
  12. public boolean tryCaptureView(View child, int pointerId) {
  13. Log.i("bqt", "【tryCaptureView】");
  14. return mDragView != null && child == mDragView;//仅支持mDragView移动
  15. }
  16. @Override
  17. public int clampViewPositionHorizontal(View child, int left, int dx) {
  18. Log.i("bqt", "【clampViewPositionHorizontal】");
  19. final int leftBound = getPaddingLeft();
  20. final int rightBound = getWidth() - child.getWidth() - leftBound;
  21. return Math.min(Math.max(left, leftBound + 5), rightBound - 5);//自定义区域
  22. }
  23. public int clampViewPositionVertical(View child, int top, int dy) {
  24. Log.i("bqt", "【clampViewPositionVertical】");
  25. final int topBound = getPaddingTop();
  26. final int bottomBound = getHeight() - child.getHeight() - topBound;
  27. return Math.min(Math.max(top, topBound + 5), bottomBound - 5);//自定义区域
  28. }
  29. //*********************************************get回调*********************************************
  30. @Override
  31. public int getViewHorizontalDragRange(View child) {//方法的返回值是该childView横向或者纵向的移动的范围,当前如果只需要一个方向移动,可以只复写一个
  32. Log.i("bqt", "【getViewHorizontalDragRange】");
  33. return getMeasuredWidth() - child.getMeasuredWidth();
  34. }
  35. @Override
  36. public int getViewVerticalDragRange(View child) {
  37. Log.i("bqt", "【getViewVerticalDragRange】");
  38. return getMeasuredHeight() - child.getMeasuredHeight();
  39. }
  40. });
  41. }
  42. @Override
  43. public boolean onInterceptTouchEvent(MotionEvent event) {
  44. return mDragger.shouldInterceptTouchEvent(event);
  45. }
  46. @Override
  47. public boolean onTouchEvent(MotionEvent event) {
  48. mDragger.processTouchEvent(event);
  49. return true;
  50. }
  51. @Override
  52. protected void onFinishInflate() {
  53. super.onFinishInflate();
  54. mDragView = findViewById(R.id.dragview);
  55. }
  56. }






以上是关于滑动 ViewDragHelper DrawerLayout的主要内容,如果未能解决你的问题,请参考以下文章

(转)ViewDragHelper实现QQ5.0侧滑并处理与ViewPager的滑动冲突

HorizontalDragLayout-模仿QQclient的Item滑动删除

ViewDragHelper

scrollTo + Scroller + ViewDragHelper

Android拖拽助手ViewDragHelper

Android拖拽助手ViewDragHelper