文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android高手进阶之ViewDragHelper使用详解以及拖动上下滑卡片实现

2024-12-02 21:04

关注

今天我们就来讲解下ViewDragHelper;

这几天项目比较忙,文章更新会慢,各位老铁可以看历史记录;

一、viewDragHleper详解

ViewDragHelper是针对 ViewGroup 中的拖拽和重新定位 views 操作时提供了一系列非常有用的方法和状态追踪;

1、ViewDragHelper初始化

  1. public class ViewDragTest extends LinearLayout  { 
  2.     ViewDragHelper mViewDragHelper; 
  3.     @Override 
  4.     protected void onAttachedToWindow() { 
  5.         super.onAttachedToWindow(); 
  6.         //中间参数表示灵敏度,比如滑动了多少像素才视为触发了滑动.值越大越灵敏. 
  7.         mViewDragHelper = ViewDragHelper.create(this, 1f, new DragCallback()); 
  8.     } 
  9.     @Override 
  10.     public boolean onInterceptTouchEvent(MotionEvent ev) { 
  11.         //固定写法 
  12.         int action = MotionEventCompat.getActionMasked(ev); 
  13.         if (action == MotionEvent.ACTION_CANCEL  
  14.         || action == MotionEvent.ACTION_UP) { 
  15.             mViewDragHelper.cancel(); 
  16.             return false
  17.         } 
  18.         return mViewDragHelper.shouldInterceptTouchEvent(ev); 
  19.     } 
  20.     @Override 
  21.     public boolean onTouchEvent(MotionEvent event) { 
  22.         //固定写法 
  23.         mViewDragHelper.processTouchEvent(event); 
  24.         return true
  25.     } 
  26.     @Override 
  27.     public void computeScroll() { 
  28.         //固定写法 
  29.         //此方法用于自动滚动,比如自动回滚到默认位置. 
  30.         if (mViewDragHelper.continueSettling(true)) { 
  31.             ViewCompat.postInvalidateOnAnimation(this); 
  32.         } 
  33.     } 

2、ViewDragHelper.Callback

  1. //这个类的回调方法,才是ViewDragHelper的重点 
  2. private class ViewDragCallback extends ViewDragHelper.Callback{ 
  3.    @Override 
  4.    public boolean tryCaptureView(View child, int pointerId) { 
  5.        //child 表示想要滑动的view 
  6.        //pointerId 表示触摸点的id, 比如多点按压的那个id 
  7.        //返回值表示,是否可以capture,也就是是否可以滑动.可以根据不同的child决定是否可以滑动 
  8.        return true
  9.    } 
  10.    @Override 
  11.    public int clampViewPositionHorizontal(View child, int leftint dx) { 
  12.        //child 表示当前正在移动的view 
  13.        //left 表示当前的view正要移动到左边距为left的地方 
  14.        //dx 表示和上一次滑动的距离间隔 
  15.        //返回值就是child要移动的目标位置.可以通过控制返回值,从而控制child只能在ViewGroup的范围中移动. 
  16.        return left
  17.    } 
  18.    @Override 
  19.    public int clampViewPositionVertical(View child, int topint dy) { 
  20.        //child 表示当前正在移动的view 
  21.        //top 表示当前的view正要移动到上边距为top的地方 
  22.        //dx 表示和上一次滑动的距离间隔 
  23.        return top
  24.    } 

重写以上3个方法,可以正常工作了.子View就可以被任意拖动了;

3、控制child的移动范围在父view中

  1. //控制child只能在ViewGroup的横向中移动 
  2. @Override   
  3. public int clampViewPositionHorizontal(View child, int leftint dx) { 
  4.   final int leftBound = getPaddingLeft();   
  5.   final int rightBound = getWidth() - mDragView.getWidth();   
  6.   final int newLeft = Math.min(Math.max(left, leftBound), rightBound);   
  7.   return newLeft;   
  8. }  
  9. //控制child只能在ViewGroup的纵向中移动 
  10. @Override   
  11. public int clampViewPositionVertical(View child, int topint dy) {   
  12.   final int topBound = getPaddingTop();   
  13.   final int bottomBound = getHeight() - mDragView.getHeight();   
  14.   final int newTop = Math.min(Math.max(top, topBound), bottomBound);   
  15.   return newTop;   
  16. }  

4、开启边界滑动

  1. //开启4个边 
  2. mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL); 
  3. //各个边 
  4. public static final int EDGE_LEFT = 1 << 0; 
  5. public static final int EDGE_RIGHT = 1 << 1; 
  6. public static final int EDGE_TOP = 1 << 2; 
  7. public static final int EDGE_BOTTOM = 1 << 3; 
  8. //当开启边界滑动之后, 此方法就会回调 
  9. @Override 
  10. public void onEdgeTouched(int edgeFlags, int pointerId) { 
  11.     //通常开启边界之后, 都需要手动capture view.之后就可以滑动view了. 
  12.     mViewDragHelper.captureChildView(getChildAt(1), pointerId); 
  13. @Override 
  14. public boolean tryCaptureView(View child, int pointerId) { 
  15.     //开启边界之后, 这个方法的返回值可能需要进一步处理.要不然开边界就没啥意思了. 
  16.     return false

5、释放后的回弹效果

有些时候, 当释放的时候, 需要将View回到原来的位置;

  1. //释放的时候, 会回调下面的方法 
  2. @Override 
  3. public void onViewReleased(View releasedChild, float xvel, float yvel) { 
  4.     //调用这个方法,就可以设置releasedChild回弹得位置. 
  5.     mViewDragHelper.settleCapturedViewAt(0, 100);//参数就是x,y的坐标 
  6.     postInvalidate();//注意一定要调用这个方法,否则没效果. 
  7. //以下2个方法最终调用的都是forceSettleCapturedViewAt(). 
  8. mViewDragHelper.settleCapturedViewAt(0, 100); 
  9. mViewDragHelper.smoothSlideViewTo(getChildAt(1), 0, 100); 
  10. //所以...发挥你的想象力,看看有什么妙用!!! 
  11. //如果你还没有忘记的话...前文应该有说过,涉及到scroll,需要重写view的此方法. 
  12. //此方法一定要重写,否则没效果 
  13. @Override 
  14. public void computeScroll() { 
  15.   //固定写法 
  16.   if (mViewDragHelper.continueSettling(true)) { 
  17.       postInvalidate();//注意此处. 
  18.   } 
  19. 通过上面2个方法的设置, 当手指释放的时候, View就会自动滑动到指定的位置...(不是一下子就到指定的位置哦,有一个滑动的过程.) 
  20. 注意:如果需要滑动的View,会消耗touch事件,比如:Button,那么需要重写以下方法. 
  21. @Override 
  22. public int getViewHorizontalDragRange(View child) { 
  23.     return child.getMeasuredWidth();//只要返回大于0的值就行 
  24. @Override 
  25. public int getViewVerticalDragRange(View child) { 
  26.     return child.getMeasuredHeight();//只要返回大于0的值就行 

6、简单api介绍

ViewDragHelper的API

  1. ViewDragHelper create(ViewGroup forParent, Callback cb); 
  1. shouldInterceptTouchEvent(MotionEvent ev)  

ViewDragHelper.Callback的API

  1. tryCaptureView(View child, int pointerId)  
  1. onViewDragStateChanged(int state)  
  1. onViewPositionChanged(View changedView, int leftint topint dx, int dy)  
  1. onViewCaptured(View capturedChild, int activePointerId) 
  1. onViewReleased(View releasedChild, float xvel, float yvel)  
  1. clampViewPositionVertical(View child, int topint dy)  
  1. clampViewPositionHorizontal(View child, int leftint dx)  

二、简单的实现demo

下面是简单实现的demo,可以直接复制使用的

1、BottomView的ViewDragHelper实现

  1. public class BottomView extends LinearLayout { 
  2.     private ViewDragHelper mDragHelper; 
  3.     private View view
  4.     private int mDragBorder, verticalRange, mDragState, peekHeight, mDragHeight; 
  5.     private final double AUTO_OPEN_SPEED_LIMIT = 800.0; 
  6.     private boolean inflate = false, isExpanded = false, isDragHeightSet = false
  7.     private MotionEvent globalEvent; 
  8.     View try_view; 
  9.     public BottomView(Context context) { 
  10.         super(context); 
  11.     } 
  12.     public BottomView(Context context, @Nullable AttributeSet attrs) { 
  13.         super(context, attrs); 
  14.         initView(context, attrs); 
  15.     } 
  16.     public BottomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 
  17.         super(context, attrs, defStyleAttr); 
  18.         initView(context, attrs); 
  19.     } 
  20.     void initView(Context context, AttributeSet attrs) { 
  21.         peekHeight = 300; 
  22.     } 
  23.     @Override 
  24.     protected void onAttachedToWindow() { 
  25.         super.onAttachedToWindow(); 
  26.     } 
  27.     @Override 
  28.     protected void onFinishInflate() { 
  29.         super.onFinishInflate(); 
  30.         mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback()); 
  31.         view = getChildAt(0); 
  32.         try_view = findViewById(R.id.ll_try_view); 
  33.     } 
  34.     @Override 
  35.     protected void onLayout(boolean b, int leftint topint rightint bottom) { 
  36.         verticalRange = getMeasuredHeight() - peekHeight; 
  37.         if (!inflate) { 
  38.             mDragBorder = verticalRange; 
  39.             inflate = true
  40.         } 
  41.         view.layout(left, mDragBorder, right, bottom + mDragBorder); 
  42.     } 
  43.     @Override 
  44.     public boolean onInterceptTouchEvent(MotionEvent ev) { 
  45.         int action = MotionEventCompat.getActionMasked(ev); 
  46.         if ((action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) && !isDraggingAllowed(ev)) { 
  47.             mDragHelper.cancel(); 
  48.             return false
  49.         } 
  50.         return mDragHelper.shouldInterceptTouchEvent(ev); 
  51.     } 
  52.     @Override 
  53.     public boolean onTouchEvent(MotionEvent event) { 
  54.         if (isDraggingAllowed(event) || isMoving()) { 
  55.             mDragHelper.processTouchEvent(event); 
  56.             return true
  57.         } 
  58.         return super.onTouchEvent(event); 
  59.     } 
  60.     @Override 
  61.     public boolean dispatchTouchEvent(MotionEvent ev) { 
  62.         globalEvent = ev; 
  63.         return super.dispatchTouchEvent(ev); 
  64.     } 
  65.     boolean isDraggingAllowed(MotionEvent event) { 
  66.         int[] viewLocations = new int[2]; 
  67.         view.getLocationOnScreen(viewLocations); 
  68.         int upperLimit = viewLocations[1] + (isDragHeightSet ? mDragHeight : peekHeight); 
  69.         int lowerLimit = viewLocations[1]; 
  70.         int y = (int) event.getRawY(); 
  71.         return (y > lowerLimit && y < upperLimit); 
  72.     } 
  73.     boolean isMoving() { 
  74.         return (mDragState == ViewDragHelper.STATE_DRAGGING || 
  75.                 mDragState == ViewDragHelper.STATE_SETTLING); 
  76.     } 
  77.     class DragHelperCallback extends ViewDragHelper.Callback { 
  78.         @Override 
  79.         public boolean tryCaptureView(View child, int pointerId) { 
  80.             return true
  81.         } 
  82.         @Override 
  83.         public int clampViewPositionVertical(View child, int topint dy) { 
  84.             return top
  85.         } 
  86.         @Override 
  87.         public void onViewDragStateChanged(int state) { 
  88.             super.onViewDragStateChanged(state); 
  89.             mDragState = state; 
  90.         } 
  91.         @Override 
  92.         public int getViewVerticalDragRange(View child) { 
  93.             return verticalRange; 
  94.         } 
  95.         @Override 
  96.         public void onViewReleased(View releasedChild, float xvel, float yvel) { 
  97.             super.onViewReleased(releasedChild, xvel, yvel); 
  98.             boolean settleToOpen = false
  99.             if (yvel > AUTO_OPEN_SPEED_LIMIT && xvel < yvel) { 
  100.                 settleToOpen = true
  101.             } else if (yvel < -AUTO_OPEN_SPEED_LIMIT && xvel > yvel) { 
  102.                 settleToOpen = false
  103.             } else if (mDragBorder > (2 * verticalRange / 3)) { 
  104.                 settleToOpen = true
  105.             } else if (mDragBorder < (verticalRange / 3)) { 
  106.                 settleToOpen = false
  107.             } 
  108.             final int settleDestY = settleToOpen ? verticalRange : 0; 
  109.             isExpanded = settleToOpen ? false : true
  110.             if (mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), settleDestY)) { 
  111.                 ViewCompat.postInvalidateOnAnimation(BottomView.this); 
  112.             } 
  113.         } 
  114.         @Override 
  115.         public void onViewPositionChanged(View changedView, int leftint topint dx, int dy) { 
  116.             super.onViewPositionChanged(changedView, lefttop, dx, dy); 
  117.             mDragBorder = top < 0 ? 0 : top > verticalRange ? verticalRange : top
  118.             float offset = 1 - ((float) mDragBorder / verticalRange); 
  119.            // if (listener != null) listener.onDrag(offset); 
  120.             requestLayout(); 
  121.         } 
  122.     } 
  123.     @Override 
  124.     public void computeScroll() { 
  125.         super.computeScroll(); 
  126.         if (mDragHelper.continueSettling(true)) { 
  127.             ViewCompat.postInvalidateOnAnimation(this); 
  128.         } 
  129.     } 
  130.     public boolean expandOnTouchView() { 
  131.         if (isDraggingAllowed(globalEvent) && mDragHelper.smoothSlideViewTo(view, 0, 0)) { 
  132.             isExpanded = true
  133.             ViewCompat.postInvalidateOnAnimation(BottomView.this); 
  134.             return true
  135.         } 
  136.         return false
  137.     } 
  138.     public boolean expandView() { 
  139.         if (mDragHelper.smoothSlideViewTo(view, 0, 0)) { 
  140.             isExpanded = true
  141.             ViewCompat.postInvalidateOnAnimation(BottomView.this); 
  142.             return true
  143.         } 
  144.         return false
  145.     } 
  146.     public boolean collapseOnTouchView() { 
  147.         if (isDraggingAllowed(globalEvent) && mDragHelper.smoothSlideViewTo(view, 0, verticalRange)) { 
  148.             isExpanded = false
  149.             ViewCompat.postInvalidateOnAnimation(BottomView.this); 
  150.             return true
  151.         } 
  152.         return false
  153.     } 
  154.     public boolean collapseView() { 
  155.         if (mDragHelper.smoothSlideViewTo(view, 0, verticalRange)) { 
  156.             isExpanded = false
  157.             ViewCompat.postInvalidateOnAnimation(BottomView.this); 
  158.             return true
  159.         } 
  160.         return false
  161.     } 
  162.     public boolean isViewExpanded() { 
  163.         return isExpanded; 
  164.     } 
  165.     public void setPeekHeight(int peekHeight) { 
  166.         this.peekHeight = peekHeight; 
  167.         requestLayout(); 
  168.     } 
  169.     public void dragHeight(int mDragHeight) { 
  170.         isDragHeightSet = true
  171.         this.mDragHeight = mDragHeight; 
  172.     } 

2、布局文件

总结

面对不懂的知识点,不要害怕,勇敢面对;一起加油

本文转载自微信公众号「Android开发编程」

 

来源:Android开发编程内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯