简单的TowPager实现

2021-04-29  本文已影响0人  王灵

这就是一个没啥用的ViewGroup,纯粹是用来学习实践的例子demo
效果图:


towpager.gif

步骤一:布局

1、创建类名为TowPagerView的类,使它继承ViewPager
2、重写onLayout函数,使它的子view横向排列,铺满

class TowPagerView(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        var childLeft = 0
        var childTop = 0
        var childRight = width
        var childBottom = height

        for (child in children) {
            child.layout(childLeft, childTop, childRight, childBottom)
            childLeft += width
            childRight += width
        }
    }
}

步骤二:滑动

1、重写onInterceptTouchEvent返回true.拦截事件
2、重写onTouchEventMotionEvent.ACTION_DOWNl里保存开始位置的x,yscrollX
3、在MotionEvent.ACTION_MOVE里根据手指的位置计算出滑动的位置

class TowPagerView(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {
    private var downX = 0f
    private var downY = 0f
    private var downScrollX = 0f
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        var childLeft = 0
        var childTop = 0
        var childRight = width
        var childBottom = height

        for (child in children) {
            child.layout(childLeft, childTop, childRight, childBottom)
            childLeft += width
            childRight += width
        }
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        return true
    }

    override fun onTouchEvent(ev: MotionEvent): Boolean {
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                downX = ev.x
                downY = ev.y
                downScrollX = scrollX.toFloat()
            }
            MotionEvent.ACTION_MOVE -> {
                val dx = (downX - ev.x+downScrollX).toInt()
                    .coerceAtLeast(0)
                    .coerceAtMost(width)
                scrollTo(dx, 0)
            }
        }
        return true
    }
}

步骤三、事件拦截判断
当出现左右滑动时,拦截子view的事件,并且阻止父view拦截事件

    private var scrolling = false //是否是开始滑动了
    private val viewConfiguration: ViewConfiguration = ViewConfiguration.get(context)
    private var pagingSlop = viewConfiguration.scaledPagingTouchSlop//最小的滑动距离

    ...

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        var result = false
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                scrolling = false
                downX = ev.x
                downY = ev.y
                downScrollX = scrollX.toFloat()
            }
            MotionEvent.ACTION_MOVE -> {
                if (!scrolling) {
                    val dx = downX - ev.x
                    if (abs(dx) > pagingSlop) {
                        scrolling = true
                        parent.requestDisallowInterceptTouchEvent(true)//父控件不拦截
                        result = true//自己拦截了
                    }
                }
            }
        }
        return result
    }

步骤四、吸附
在松开手指之后根据停留位置判断最终的位置
MotionEvent.ACTION_UP里判断当前的滑动距离是否超过了屏幕的一半

         val targetPage = if (scrollX > width / 2) 1 else 0
         val scrollDistance = if (targetPage == 1) width - scrollX else -scrollX

在最终的移动中使用OverScroller来实现弹性滑动

    private val overScroller: OverScroller = OverScroller(context)
...
    override fun onTouchEvent(ev: MotionEvent): Boolean {
        when (ev.action) {
           ...
            MotionEvent.ACTION_UP -> {
                val targetPage = if (scrollX > width / 2) 1 else 0
                val scrollDistance = if (targetPage == 1) width - scrollX else -scrollX
                overScroller.startScroll(scrollX, 0, scrollDistance, 0)
                postInvalidateOnAnimation()
            }
        }
        return true
    }

    override fun computeScroll() {
        if (overScroller.computeScrollOffset()) {
            scrollTo(overScroller.currX, overScroller.currY)
            postInvalidateOnAnimation()
        }
    }

步骤五、惯性滑动

当手指滑动速度大于阀值是,直接判定用户意图
使用VelocityTracker.obtain()获取VelocityTracker对象来获得滑动速度的相关数据
注意:需要在onInterceptTouchEventonTouchEventMotionEvent.ACTION_DOWN事件中清空数据。还有添加所有事件的数据

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
            velocityTracker.clear()
        }
        velocityTracker.addMovement(ev)
      ...
     }
    override fun onTouchEvent(ev: MotionEvent): Boolean {
        if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
            velocityTracker.clear()
        }
        velocityTracker.addMovement(ev)
      ...
     }

就是一摸一样的代码
接着在onTouchEventMotionEvent.ACTION_UP事件中获取速度信息并逻辑计算

            MotionEvent.ACTION_UP -> {
                velocityTracker.computeCurrentVelocity(1000, maxVelocity.toFloat())
                val vx = velocityTracker.xVelocity


                val targetPage = if (abs(vx) < minVelocity) {
                    if (scrollX > width / 2) 1 else 0
                } else {
                    if (vx < 0) 1 else 0
                }

...
            }

基本的都介绍完了,上完整代码

class TowPagerView(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {
    private var downX = 0f
    private var downY = 0f
    private var downScrollX = 0f
    private var scrolling = false //是否是开始滑动了
    private val overScroller: OverScroller = OverScroller(context)
    private val viewConfiguration: ViewConfiguration = ViewConfiguration.get(context)
    private var pagingSlop = viewConfiguration.scaledPagingTouchSlop//最小的滑动距离

    private val velocityTracker = VelocityTracker.obtain()
    private var minVelocity = viewConfiguration.scaledMinimumFlingVelocity
    private var maxVelocity = viewConfiguration.scaledMaximumFlingVelocity

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        var childLeft = 0
        var childTop = 0
        var childRight = width
        var childBottom = height

        for (child in children) {
            child.layout(childLeft, childTop, childRight, childBottom)
            childLeft += width
            childRight += width
        }
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
            velocityTracker.clear()
        }
        velocityTracker.addMovement(ev)

        var result = false
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                scrolling = false
                downX = ev.x
                downY = ev.y
                downScrollX = scrollX.toFloat()
            }
            MotionEvent.ACTION_MOVE -> {
                if (!scrolling) {
                    val dx = downX - ev.x
                    if (abs(dx) > pagingSlop) {
                        scrolling = true
                        parent.requestDisallowInterceptTouchEvent(true)//父控件不拦截
                        result = true//自己拦截了
                    }
                }
            }
        }
        return result
    }

    override fun onTouchEvent(ev: MotionEvent): Boolean {
        if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
            velocityTracker.clear()
        }
        velocityTracker.addMovement(ev)

        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                downX = ev.x
                downY = ev.y
                downScrollX = scrollX.toFloat()
            }
            MotionEvent.ACTION_MOVE -> {
                val dx = (downX - ev.x + downScrollX).toInt()
                    .coerceAtLeast(0)
                    .coerceAtMost(width)
                scrollTo(dx, 0)
            }
            MotionEvent.ACTION_UP -> {
                velocityTracker.computeCurrentVelocity(1000, maxVelocity.toFloat())
                val vx = velocityTracker.xVelocity


                val targetPage = if (abs(vx) < minVelocity) {
                    if (scrollX > width / 2) 1 else 0
                } else {
                    if (vx < 0) 1 else 0
                }

                val scrollDistance = if (targetPage == 1) width - scrollX else -scrollX
                overScroller.startScroll(scrollX, 0, scrollDistance, 0)
                postInvalidateOnAnimation()
            }
        }
        return true
    }

    override fun computeScroll() {
        if (overScroller.computeScrollOffset()) {
            scrollTo(overScroller.currX, overScroller.currY)
            postInvalidateOnAnimation()
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读