美文网首页AndroidUI待写
高仿知乎日报无限轮播图+指示符切换动画效果

高仿知乎日报无限轮播图+指示符切换动画效果

作者: 孔鹏飞 | 来源:发表于2021-05-06 15:32 被阅读0次

    先放上效果图, 因简书不支持webp格式的图片,如上传gif图片太大而无法显示,可移步掘金GitHub查看动态效果图。

    仿知乎日报无限轮播图+指示符切换动画

    动画分析

    • 未选中页面对应的指示符宽度小,形状为圆形,选中页面对应的指示符宽度大,形状为椭圆。
    • 当页面A->B,A页面对应的指示符宽度由大->小,B页面对应的指示符宽度由小->大,指示符宽度和颜色随滑动而不断变化。
    • 当从最后一个页面手指向右滑动,页面向左时,最后一个页面对应的指示符宽度由大->小,第一个页面对应的指示符宽度由小->大,其余指示符向右平移,有联动效果。
    • 当从第一个页面手指向左滑动,页面向右时,第一个页面对应的指示符宽度由大->小,最后一个对应的指示符宽度由小->大,其余指示符向左平移,有联动效果。

    代码实现

    TKBanner

    实现无限轮播图功能,默认支持圆点和数字指示符。

    仿知乎日报APP轮播图 仿品玩APP轮播图 仿虎嗅APP轮播图

    CuteIndicator

    自定义View,实现ViewPager页面滑动过程中指示符切换动画效果。

    相关属性

    属性 说明 默认值
    IndicatorColor 未选中页面对应的指示符颜色 Color.GRAY
    IndicatorSelectedColor 选中页面对应的指示符颜色 Color.WHITE
    IndicatorWidth 未选中页面对应的指示符宽度 5dp
    IndicatorSelectedWidth 选中页面对应的指示符宽度 20dp
    IndicatorHeight 指示符高度 5dp
    IndicatorMargin 指示符之间的间隔 5dp
    IndicatorShowAnimation 是否显示指示符切换动画 true

    关键代码

    • 覆盖onMeasure方法,计算设置指示符宽度和高度。目前没有根据测量模式去判断计算,简单的计算指示符总宽度=(指示符数量-1)*(未选中指示符宽度+指示符之间的间隔)+选中指示符的宽度,此处还有很多优化空间。
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec)
         if(mIndicatorCount>0) {
            val width = ((mIndicatorCount - 1) * (mIndicatorMargin + mIndicatorWidth) + mIndicatorSelectedWidth).toInt()
            setMeasuredDimension(width, mIndicatorHeight.toInt())
         }
    }
    
    • 覆盖onDraw方法,绘制指示符,针对第一个页面和最后一个页面滑动时特别处理。
    override fun onDraw(canvas: Canvas) {
         super.onDraw(canvas)
         if (mIndicatorCount <= 0) {
             return
         }
         var left=0f
         var right=0f
         if (position == (mIndicatorCount - 1) && positionOffset > 0f) {
             for (i in 0 until mIndicatorCount) {
               if(i==0){
                  left=0f
                  right=left+mIndicatorWidth+(mIndicatorSelectedWidth - mIndicatorWidth) * positionOffset
                  mIndicatorPaint.color = ColorUtils.blendARGB(mIndicatorColor, mIndicatorSelectedColor, positionOffset)
               }
               else if(i<position){
                  right=left+mIndicatorWidth
                  mIndicatorPaint.color = mIndicatorColor
               }
               else if(i==position){
                  right=i*(mIndicatorWidth+mIndicatorMargin)+mIndicatorSelectedWidth
                  mIndicatorPaint.color = ColorUtils.blendARGB(mIndicatorColor, mIndicatorSelectedColor, 1-positionOffset)
               }
               canvas.drawRoundRect(RectF(left, 0f, right, mIndicatorHeight), mIndicatorWidth / 2, mIndicatorWidth / 2, mIndicatorPaint)
               left=right+mIndicatorMargin
            }
         } else {
            for (i in 0 until mIndicatorCount) {
                if (i < position) {
                    left = i * (mIndicatorWidth + mIndicatorMargin)
                    right = left + mIndicatorWidth
                    mIndicatorPaint.color = mIndicatorColor
                 } else if (i == position) {
                    left = i * (mIndicatorWidth + mIndicatorMargin)
                    right = left + mIndicatorWidth + (mIndicatorSelectedWidth - mIndicatorWidth) * (1 - positionOffset)
                        mIndicatorPaint.color = ColorUtils.blendARGB(mIndicatorColor, mIndicatorSelectedColor, 1 - positionOffset)
                 } else if (i == (position + 1)) {
                    left = (i - 1) * (mIndicatorMargin + mIndicatorWidth) + mIndicatorWidth + (mIndicatorSelectedWidth - mIndicatorWidth) * (1 - positionOffset) + mIndicatorMargin
                    right = i * (mIndicatorMargin + mIndicatorWidth) + mIndicatorSelectedWidth
                    mIndicatorPaint.color = ColorUtils.blendARGB(mIndicatorColor, mIndicatorSelectedColor, positionOffset)
                 } else {
                    left = (i - 1) * (mIndicatorWidth + mIndicatorMargin) + (mIndicatorSelectedWidth + mIndicatorMargin)
                    right = left + mIndicatorWidth
                    mIndicatorPaint.color = mIndicatorColor
                 }
                 canvas.drawRoundRect(RectF(left, 0f, right, mIndicatorHeight), mIndicatorWidth / 2, mIndicatorWidth / 2, mIndicatorPaint)
            }
        }
    }
    
    
    • setUp方法实现ViewPagerIndicator绑定
    fun setUp(count:Int) {
        mIndicatorCount = count
        requestLayout()
    }
    

    GitHub

    完整的代码可以在GitHub上获取,喜欢的可以考虑给我点个赞^

    https://github.com/kongpf8848/ViewWorld

    参考

    BGABanner

    CuteIndicator

    相关文章

      网友评论

        本文标题:高仿知乎日报无限轮播图+指示符切换动画效果

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