Android使用ViewDragHelper实现折叠拖拽效果

最终效果

Android使用ViewDragHelper实现折叠拖拽效果

源码实现

布局文件
  • 整体布局
<?xml version="1.0" encoding="utf-8"?>
<com.crystal.view.VerticalDragListView 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"
   tools:context=".MainActivity">

   <TextView
       android:layout_width="match_parent"
       android:layout_height="200dp"
       android:background="@color/red"
       android:gravity="center"
       android:text="TopView"
       android:textColor="@color/white"
       android:textSize="16sp" />

   <ListView
       android:id="@+id/listView"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:background="@color/blue" />
</com.crystal.view.VerticalDragListView>
  • ListView的item布局
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/tv"
   android:layout_width="match_parent"
   android:layout_height="60dp"
   android:gravity="center"
   android:textColor="@color/white"
   android:textSize="20sp">

</TextView>
Activity代码
private var lists = mutableListOf<String>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_slide_menu)
        for (i in 0..30) {
            lists.add("item -->$i")
        }
        val listView = findViewById<ListView>(R.id.listView)
        val arrayAdapter = ArrayAdapter(this, R.layout.layout_list_item, lists)
        listView.adapter = arrayAdapter
    }
自定义VerticalDragListView
package com.crystal.view

import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.widget.AbsListView
import android.widget.FrameLayout
import androidx.customview.widget.ViewDragHelper


/**
 * 垂直方向的滑动
 * on 2022/10/28
 */
class VerticalDragListView : FrameLayout {


    /**
     * 拖拽的listview
     */
    private lateinit var dragListView: View

    /**
     * 上方view对应的高度
     */
    private var topViewHeight: Int = 0

    /**
     * 当前最上层View是否为展开状态
     */
    private var isTopViewOpen: Boolean = false

    private var downY: Float = 0f

    /**
     * 创建ViewDragHelper对象用于实现拖拽效果
     */
    private val viewDragHelper by lazy {
        ViewDragHelper.create(this, DragHelperCallback())
    }

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context, attrs, defStyleAttr
    )


    private inner class DragHelperCallback : ViewDragHelper.Callback() {
        override fun tryCaptureView(child: View, pointerId: Int): Boolean {
            //用于判断是否为支持拖拽的对象,这里仅设置dragListView为可拖拽对象
            return child == dragListView
        }

        override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
            //设置列表垂直方向拖动的距离,本文仅需要垂直方向滑动,如果需要水平方向拖动,则重写clampViewPositionHorizontal方法
            var resultTop = top
            if (resultTop < 0) {
                resultTop = 0
            }
            if (resultTop > topViewHeight) {
                resultTop = topViewHeight
            }
            return resultTop
        }

        override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) {
            //松开时,判断如果拖动距离>topViewHeight/2,则滑动到[0,topViewHeight]位置,否则滑动到【0,0】位置
            isTopViewOpen = if (dragListView.top > topViewHeight / 2) {
                viewDragHelper.settleCapturedViewAt(0, topViewHeight)
                true
            } else {
                viewDragHelper.settleCapturedViewAt(0, 0)
                false
            }
            invalidate()

        }
    }


    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        if (isTopViewOpen) {
            //如果当前topView已展开,则事件交给ViewGroup处理
            return true
        }
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                downY = ev.y
                viewDragHelper.processTouchEvent(ev)
            }
            MotionEvent.ACTION_MOVE -> {
                if (ev.y - downY > 0 && !canChildScrollUp()) {
                    return true
                }
            }

        }
        return super.onInterceptTouchEvent(ev)
    }

    override fun onFinishInflate() {
        super.onFinishInflate()
        if (childCount != 2) {
            throw  IllegalArgumentException("The VerticalDragListView Child size must be 2!")
        }
        dragListView = getChildAt(1)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        if (changed) {
            //获取最上方View的测量高度
            topViewHeight = getChildAt(0).measuredHeight
        }
    }

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


    override fun computeScroll() {
        super.computeScroll()
        //判断拖拽是否完成,没有完成不断进行重绘
        if (viewDragHelper.continueSettling(true)) {
            invalidate()
        }
    }

    /**
     * 判断listview是否已滑动到顶部
     */
    private fun canChildScrollUp(): Boolean {
        return if (Build.VERSION.SDK_INT < 14) {
            if (dragListView is AbsListView) {
                val absListView = dragListView as AbsListView
                (absListView.childCount > 0 && (absListView.firstVisiblePosition > 0 || absListView.getChildAt(
                    0
                ).top < absListView.paddingTop))
            } else {
                dragListView.canScrollVertically(-1) || dragListView.scrollY > 0
            }
        } else {
            dragListView.canScrollVertically(-1)
        }
    }


}

总结

通过对ViewDragHelper工具类的使用,实现了拖拽效果,同时也通过事件分发处理了滑动冲突,对自定义View的学习很有帮助!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值