一. 背景知识

2013年谷歌i/o大会上介绍了两个新的layout: SlidingPaneLayoutDrawerLayout,现在这俩个类被广泛的运用,其实研究他们的源码你会发现这两个类都运用了ViewDragHelper来处理拖动。ViewDragHelper是framework中不为人知却非常有用的一个工具。

ViewDragHelper解决了android中手势处理过于复杂的问题,在DrawerLayout出现之前,侧滑菜单都是由第三方开源代码实现的,其中著名的当属 MenuDrawer ,MenuDrawer重写onTouchEvent方法来实现侧滑效果,代码量很大,实现逻辑也需要很大的耐心才能看懂。如果每个开发人员都从这么原始的步奏开始做起,那对于安卓生态是相当不利的。所以说ViewDragHelper等的出现反映了安卓开发框架已经开始向成熟的方向迈进。

二. 解决的问题

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主要可以用来拖拽和设置ViewGroup中子View的位置。

三. 可以实现的效果

1. View的拖动效果

ViewDragHelper: ViewDragHelper的使用

摘自:https://blog.csdn.net/lmj623565791/article/details/46858663

2. 仿微信语音通知的悬浮窗效果(悬浮窗可以拖动,并且在手指释放的时候,悬浮窗会自动停靠在屏幕边缘)

ViewDragHelper: ViewDragHelper的使用

摘自:https://www.jianshu.com/p/a9e0a98e4d42

3. 抽屉效果

ViewDragHelper: ViewDragHelper的使用

摘自:https://cloud.tencent.com/developer/article/1035828

四. 基本使用

1. ViewDragHelper的初始化

ViewDragHelper一般用在一个自定义ViewGroup的内部,比如下面自定义了一个继承于LinearLayout的DragLayout,DragLayout内部有一个子view mDragView作为成员变量:

public class DragLayout extends LinearLayout {

    private ViewDragHelper mDragHelper;

    private View mDragView;

    public DragLayout(Context context) {
        this(context, null);
    }

    public DragLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

}

创建一个带有回调接口的 ViewDragHelper 

public DragLayout(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
}

其中1.0f是灵敏度系数,系数越大越敏感。第一个参数为this,表示要拖动子View所在的Parent View,必须为ViewGroup

要让ViewDragHelper能够处理拖动,还需要将触摸事件传递给ViewDragHelper,这点和 Gesturedetector 是一样的:

 @Override
 public boolean onInterceptTouchEvent(MotionEvent event) {
    return mDragHelper.shouldInterceptTouchEvent(event);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
    mDragHelper.processTouchEvent(event);
    return true;
 }

接下来,你就可以在刚才传入的回调中处理各种拖动行为了。

2. 拖动行为的处理

处理横向的拖动:

在DragHelperCallback中实现 clampViewPositionHorizontal 方法, 并且返回一个适当的数值就能实现横向拖动效果,clampViewPositionHorizontal的第二个参数是指当前拖动子view应该到达的x坐标。所以按照常理这个方法原封返回第二个参数就可以了,但为了让被拖动的view遇到边界之后就不在拖动,对返回的值做了更多的考虑。

@Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            Log.d("DragLayout", "clampViewPositionHorizontal " + left + "," + dx);
            // 最小x坐标值不能小于leftBound
            final int leftBound = getPaddingLeft();
            // 最大x坐标值不能大于rightBound
            final int rightBound = getWidth() - mDragView.getWidth() - getPaddingRight();
            final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
            return newLeft;
        }

ViewDragHelper: ViewDragHelper的使用

同上,处理纵向的拖动:

在DragHelperCallback中实现clampViewPositionVertical方法,实现过程同 clampViewPositionHorizontal 

@Override
        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
            Log.d("DragLayout", "top=" + top + "; dy=" + dy);
            // 最小 y 坐标值不能小于 topBound
            final int topBound = getPaddingTop();
            // 最大 y 坐标值不能大于 bottomBound
            final int bottomBound = getHeight() - child.getHeight() - getPaddingBottom();
            final int newTop = Math.min(Math.max(top, topBound), bottomBound);
            return newTop;
        }

ViewDragHelper: ViewDragHelper的使用 

clampViewPositionHorizontal 和 clampViewPositionVertical必须要重写,因为默认它返回的是0。事实上我们在这两个方法中所能做的事情很有限。 个人觉得这两个方法的作用就是给了我们重新定义目的坐标的机会。

完整code 参考:

activity_test_view.xml

<?xml version="1.0" encoding="utf-8"?>
<com.yongdaimi.android.androidapitest.view.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:context=".ListViewActivity">

    <TextView
        android:id="@+id/content_view"
        android:layout_width="100dip"
        android:layout_height="100dip"
        android:background="@android:color/holo_blue_light"
        android:text="内容区域"
        android:gravity="center"
        />

</com.yongdaimi.android.androidapitest.view.DragLayout>
View Code

相关文章:

  • 2022-01-21
  • 2021-05-10
  • 2022-12-23
  • 2021-10-06
  • 2021-08-06
  • 2022-12-23
  • 2021-09-10
猜你喜欢
  • 2021-12-06
  • 2022-12-23
  • 2021-08-16
  • 2022-03-08
  • 2021-11-21
相关资源
相似解决方案