美文网首页
简单的TowPager实现

简单的TowPager实现

作者: 王灵 | 来源:发表于2021-04-29 17:18 被阅读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()
        }
    }
}

相关文章

  • 简单的TowPager实现

    这就是一个没啥用的ViewGroup,纯粹是用来学习实践的例子demo效果图: 步骤一:布局 1、创建类名为Tow...

  • JavaScript简单实现栈

    JavaScript简单实现栈主要是通过数组实现,以下是简单实现的代码

  • rxjs - i18n

    目的 实现网页多语言的切换 依赖 react,rxjs,useObservable感觉就是简单,简单,简单 实现 ...

  • 超简单实现iOS列表的索引功能

    超简单实现iOS列表的索引功能 超简单实现iOS列表的索引功能

  • 简单实现下拉图片放大④ + pageControl指示器

    传送门 : 简单实现下拉图片放大① - 全屏手势简单实现下拉图片放大② - 单张图简单实现下拉图片放大③ - 定时...

  • 实现简单的Promise

    参考文章:Promise不会??看这里!!!史上最通俗易懂的Promise!!!

  • 实现简单的弹幕

    第一步,首先要在Android的build.gradle文件中引入B站的项目: repositories { ...

  • sharedptr的简单实现

    介绍 标准库中的sharedptr简化了内存管理,其内部使用了引用计数,当计数为0的时候就释放内存。 当发生拷贝构...

  • 拖拽的简单实现

    前言:年初换工作,换住处弄了好长一段时间,终于全部搞定了,好久没写博客了,今天小更新一下,以示存在 ~.~ 话不多...

  • lazyload的简单实现

网友评论

      本文标题:简单的TowPager实现

      本文链接:https://www.haomeiwen.com/subject/crairltx.html