观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言

  ViewDragHelper需要自定义ViewGroup实现,并且只是针对ViewGroup里的子View进行拖放,在拖放的过程中不能携带数据。也不能跨进程,甚至不能跨activity。所以ViewDragHelper本质上更像是一个ViewGroup里简单实现拖放效果的帮助类。

一个简单拖动的例子

  快速了解一下,有个概念

效果图

代码

自定义ViewGroup布局


class MyViewDragConstraintLayout(context: Context, attrs: AttributeSet?) : ConstraintLayout(context, attrs) {
    private lateinit var mViewDragHelper: ViewDragHelper

    init {
        /**
         * 第一个参数是实现拖动的ViewGroup父类布局,第二个参数是拖动时的灵敏度,拖动开始的敏感程度的乘数。值越大越敏感。1.0f是正常的。
         */
        mViewDragHelper = ViewDragHelper.create(this, 1.0f, object : ViewDragHelper.Callback() {
            /**
             * 是否允许view的拖动功能,返回true是允许拖动,返回false是不允许拖动
             */
            override fun tryCaptureView(child: View, pointerId: Int): Boolean {
                return true
            }

            /**
             * 控制垂直方向的拖动位移,如果不重写此方法默认是不允许垂直运动的,按照下面重写方法后可以允许垂直方向的拖动
             */
            override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
                return top
            }

            /**
             * 控制横向方向的拖动位移,如果不重写此方法默认是不允许横向运动的,按照下面重写方法后可以允许横向方向的拖动
             */
            override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int {
                return left
            }
        })
    }

    /**
     * 注意,你需要重写onInterceptTouchEvent方法并且将触摸拦截交予ViewDragHelper的shouldInterceptTouchEvent,使其可以重新分配触控事件
     */
    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        return mViewDragHelper.shouldInterceptTouchEvent(ev)
    }

    /**
     * 注意,你需要重写onTouchEvent,并且将mViewDragHelper的processTouchEvent实现,使其可以实现拖动子view的效果
     */
    override fun onTouchEvent(event: MotionEvent): Boolean {
        mViewDragHelper.processTouchEvent(event)
        return true
    }
}

xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.lwlx.mydragtestapp.MyViewDragConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/icon1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

    </com.lwlx.mydragtestapp.MyViewDragConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

限制拖拽方向的例子

下面的代码,我们假设需要限制拖拽的方向只能向下,其他方向都不行。

效果图

代码

class MyViewDragConstraintLayout(context: Context, attrs: AttributeSet?) : ConstraintLayout(context, attrs) {
    private lateinit var mViewDragHelper: ViewDragHelper
    private var mDefaultY = 0

    init {
        mViewDragHelper = ViewDragHelper.create(this, 1.0f, object : ViewDragHelper.Callback() {
            override fun tryCaptureView(child: View, pointerId: Int): Boolean {
                mDefaultY = child.top
                return true
            }

            /**
             * 控制垂直方向的拖动位移,如果不重写此方法默认是不允许垂直运动的,按照下面重写方法后可以允许垂直方向的拖动
             */
            override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
                Log.e("zh", "top = ${top}")
                //不允许top小于mDefaultY,这样就限制了Y轴的值不能变小
                if (mDefaultY > top){
                    return mDefaultY
                }
                return top
            }
        })
    }
    
    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        return mViewDragHelper.shouldInterceptTouchEvent(ev)
    }
    
    override fun onTouchEvent(event: MotionEvent): Boolean {
        mViewDragHelper.processTouchEvent(event)
        return true
    }
}

拖拽回弹效果

效果图

代码

class MyViewDragConstraintLayout(context: Context, attrs: AttributeSet?) :
    ConstraintLayout(context, attrs) {
    private lateinit var mViewDragHelper: ViewDragHelper
    private var mDefaultY = 0
    private var mDefaultX = 0
    private lateinit var mIconView: ImageView

    init {
        mViewDragHelper = ViewDragHelper.create(this, 1.0f, object : ViewDragHelper.Callback() {
            override fun tryCaptureView(child: View, pointerId: Int): Boolean {
                return child == mIconView
            }

            override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
                return top
            }

            override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int {
                return left
            }

            override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) {
                super.onViewPositionChanged(changedView, left, top, dx, dy)
            }

            /**
             * 当子视图不再被主动拖动时调用。
             * releasedChild – 捕获的子视图现在被释放
             * xvel – 指针离开屏幕时的X速度,单位为像素每秒。
             * yvel – 指针离开屏幕时的Y速度,单位为像素/秒。
             */
            override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) {
                if (mIconView == releasedChild) {
                    //回弹默认位置
                    val result = mViewDragHelper.settleCapturedViewAt(mDefaultX, mDefaultY)
                    invalidate()
                } else {
                    super.onViewReleased(releasedChild, xvel, yvel)
                }
            }
        })
    }

    /**
     * 注意这个重写的computeScroll与设置的continueSettling是关键,如果不重写此方法,settleCapturedViewAt方法就没有效果
     */
    override fun computeScroll() {
        super.computeScroll()
        if (mViewDragHelper.continueSettling(true)) {
            invalidate()
        }
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        return mViewDragHelper.shouldInterceptTouchEvent(ev)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        mViewDragHelper.processTouchEvent(event)
        return true
    }

    override fun onFinishInflate() {
        super.onFinishInflate()
        mIconView = findViewById(R.id.icon1)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        mDefaultX  = mIconView.left
        mDefaultY  = mIconView.top
    }
}

边缘拖拽监听

代码

class MyViewDragConstraintLayout(context: Context, attrs: AttributeSet?) :
    ConstraintLayout(context, attrs) {
    private lateinit var mViewDragHelper: ViewDragHelper
    private lateinit var mIconView: ImageView

    init {
        mViewDragHelper = ViewDragHelper.create(this, 1.0f, object : ViewDragHelper.Callback() {
            override fun tryCaptureView(child: View, pointerId: Int): Boolean {
                return child == mIconView
            }

            override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
                return top
            }

            override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int {
                return left
            }

            override fun onEdgeDragStarted(edgeFlags: Int, pointerId: Int) {
                super.onEdgeDragStarted(edgeFlags, pointerId)
                Log.e("zh", "onEdgeDragStarted: ${edgeFlags}", )
                when(edgeFlags){
                    ViewDragHelper.EDGE_LEFT->{
                        Toast.makeText(context, "已经移动到最左边了 指针id${pointerId}", Toast.LENGTH_SHORT).show()
                    }
                    ViewDragHelper.EDGE_RIGHT->{
                        Toast.makeText(context, "已经移动到最右边了 指针id${pointerId}", Toast.LENGTH_SHORT).show()
                    }
                    ViewDragHelper.EDGE_TOP->{
                        Toast.makeText(context, "已经移动到最上边了", Toast.LENGTH_SHORT).show()
                    }
                    ViewDragHelper.EDGE_BOTTOM->{
                        Toast.makeText(context, "已经移动到最下边了", Toast.LENGTH_SHORT).show()
                    }

                }
                mViewDragHelper.captureChildView(mIconView, pointerId)
            }
        })
        //设置需要监听的四个方向的编译,只能设置一个方向,并且在上面的onEdgeDragStarted里回调触发
        mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT)
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        return mViewDragHelper.shouldInterceptTouchEvent(ev)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        mViewDragHelper.processTouchEvent(event)
        return true
    }
}

end

posted on 2023-06-07 14:46  观心静  阅读(2875)  评论(0编辑  收藏  举报