零侵入让你的ScrollView ListView 拥有阻尼拉动回弹

import android.animation.ObjectAnimator
import android.util.SparseArray
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.DecelerateInterpolator
import com.xiaolei.library.Exts.onAnim
import java.util.LinkedHashMap

/**
 * @param view 需要阻尼滑动的控件
 * @param damping 阻尼值,越小越难拉动
 */
class DampTouchListener(val view: ViewGroup, var damping: Float = 10f) : View.OnTouchListener
{
    private val juli = SparseArray<Float>()
    private var translationCount = 0.0f
    private val childAnimMap = LinkedHashMap<View, ObjectAnimator>()

    override fun onTouch(v: View, ev: MotionEvent): Boolean
    {
        // 没有子节点还阻尼滚动个毛啊
        if (view.childCount == 0)
        {
            return false
        }

        // 子控件数量不对,肯定要清空重新添加一次啊
        if (view.childCount != childAnimMap.size)
        {
            childAnimMap.clear()
            for (index in 0 until view.childCount)
            {
                val childView = view.getChildAt(index)
                childAnimMap[childView] = generateViewAnim(childView).apply {
                    onAnim(onStart = {
                        v.setOnTouchListener(null)
                    }, onEnd = {
                        v.setOnTouchListener(this@DampTouchListener)
                    })
                }
            }
        }
        // 能不能往上推了
        val canUp = view.canScrollVertically(1)
        // 能不能往下拉了
        val canDown = view.canScrollVertically(-1)

        when (ev.actionMasked)
        {
            // 每个手指按下,记录下每个手指ID,以及记录每个手指的位置
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN ->
            {
                val index = ev.actionIndex
                val id = ev.getPointerId(index)
                for (entry in childAnimMap)
                {
                    entry.value.cancel()
                }
                juli.put(id, ev.getY(index))
            }
            MotionEvent.ACTION_MOVE ->
            {
                if (!canDown || !canUp)
                {
                    var tmpCount = translationCount
                    /**
                     * 在屏幕上滑动的时候,
                     * 记录下每个手指的位置,
                     * 算出每个手指一动的距离juli,
                     * 并且所有子控件都往那边移动
                     * 考虑到两个手指爬来爬去
                     */
                    for (index in 0 until ev.pointerCount) // 屏幕上指头的数量
                    {
                        val pointerId = ev.getPointerId(index) // 每个指头的ID
                        val lastY = juli.get(pointerId) // 根据每个指头ID,获取它上一个位置
                        tmpCount += ev.getY(index) - lastY // 需要移动总数,把所有指头移动的距离加起来
                        if (Math.abs(tmpCount) <= Math.abs(translationCount))
                        {
                            continue
                        }
                        for (entry in childAnimMap) // 循环所有子控件
                        {
                            val childView = entry.key
                            // 将所有子控件往那边移动,距离通过阻尼计算出值
                            childView.translationY = calDampValue(tmpCount)
                        }
                        // 更新所有的指头的现在的值
                        juli.put(pointerId, ev.getY(index))
                    }

                    return if (Math.abs(tmpCount) > Math.abs(translationCount))
                    {
                        translationCount = tmpCount
                        false
                    } else
                    {
                        true
                    }
                }
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL ->
            {
                // 手指一松开,或者移动开位置(取消),则将使用属性动画,将所有控件位置还原
                translationCount = 0f
                // juli.clear()
                for (entry in childAnimMap)
                {
                    val childView = entry.key
                    val animator = entry.value
                    animator.setFloatValues(childView.translationY, 0f)
                    animator.start()
                }

            }
        }
        return false
    }


    /**
     * 计算阻尼值
     *
     * @param value
     * @return
     */
    private fun calDampValue(value: Float): Float
    {
        return if (value < 0)
            -Math.sqrt((2f * damping * Math.abs(value)).toDouble()).toFloat()
        else
            Math.sqrt((2f * damping * value).toDouble()).toFloat()
    }

    /**
     * 生成一个动画
     */
    private fun generateViewAnim(view: View) = ObjectAnimator().apply {
        duration = 200
        propertyName = "translationY"
        target = view
        interpolator = DecelerateInterpolator()
    }

}

转载于:https://my.oschina.net/xiaolei123/blog/2999706

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值