You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用ViewPager实现横向带动画的页面指示器

实现横向滑动页面指示器(View系统版)

方案一:自定义View实现完整动画效果

如果现有库满足不了动画需求,直接自定义View是最灵活的方式,以下是针对ViewPager2的实现思路和代码:

1. 自定义IndicatorView代码

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import androidx.viewpager2.widget.ViewPager2

class HorizontalPagerIndicator @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    // 指示器核心参数
    private var indicatorCount = 0
    private var currentPosition = 0
    private var scrollOffset = 0f

    // 绘制画笔
    private val normalPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#CCCCCC")
        style = Paint.Style.FILL
    }
    private val selectedPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#000000")
        style = Paint.Style.FILL
    }

    // 指示器尺寸配置
    private val indicatorRadius = dpToPx(4f)
    private val selectedIndicatorRadius = dpToPx(6f)
    private val indicatorSpacing = dpToPx(8f)

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val desiredWidth = (indicatorCount * (indicatorRadius * 2) + (indicatorCount - 1) * indicatorSpacing).toInt()
        val desiredHeight = (selectedIndicatorRadius * 2).toInt()
        setMeasuredDimension(
            resolveSize(desiredWidth, widthMeasureSpec),
            resolveSize(desiredHeight, heightMeasureSpec)
        )
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (indicatorCount == 0) return

        // 绘制普通状态指示器
        for (i in 0 until indicatorCount) {
            val centerX = getIndicatorCenterX(i)
            canvas.drawCircle(centerX, height / 2f, indicatorRadius, normalPaint)
        }

        // 绘制带滑动动画的选中指示器
        val selectedCenterX = getIndicatorCenterX(currentPosition) + scrollOffset * (indicatorRadius * 2 + indicatorSpacing)
        canvas.drawCircle(selectedCenterX, height / 2f, selectedIndicatorRadius, selectedPaint)
    }

    // 计算第i个指示器的中心X坐标
    private fun getIndicatorCenterX(position: Int): Float {
        return indicatorRadius + position * (indicatorRadius * 2 + indicatorSpacing)
    }

    // 绑定ViewPager2
    fun attachToViewPager(viewPager: ViewPager2) {
        indicatorCount = viewPager.adapter?.itemCount ?: 0
        viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageScrolled(
                position: Int,
                positionOffset: Float,
                positionOffsetPixels: Int
            ) {
                currentPosition = position
                scrollOffset = positionOffset
                invalidate()
            }

            override fun onPageSelected(position: Int) {
                currentPosition = position
                scrollOffset = 0f
                invalidate()
            }

            override fun onPageScrollStateChanged(state: Int) {}
        })
        requestLayout()
    }

    private fun dpToPx(dp: Float): Float {
        return dp * resources.displayMetrics.density
    }
}

2. 布局中使用

在XML布局里搭配ViewPager2添加该自定义指示器:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="200dp"/>

    <com.yourpackage.HorizontalPagerIndicator
        android:id="@+id/indicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="16dp"/>
</LinearLayout>

3. 代码中绑定关联

在Activity/Fragment中完成ViewPager2和指示器的绑定:

val viewPager = findViewById<ViewPager2>(R.id.viewPager)
val indicator = findViewById<HorizontalPagerIndicator>(R.id.indicator)
viewPager.adapter = YourPagerAdapter() // 替换为你的PagerAdapter实现
indicator.attachToViewPager(viewPager)

方案二:改造现有Android-ViewPagerIndicator库添加动画

如果想继续使用已引入的库,可通过修改源码添加滑动跟随动画:

  • 找到库中的CirclePageIndicator类
  • 添加mScrollOffset变量记录页面滚动的偏移比例
  • 在onPageScrolled回调中更新mScrollOffset = positionOffset,并调用invalidate()触发重绘
  • 修改onDraw方法,计算选中指示器的动态中心坐标:selectedCenterX = mCenters[mCurrentPage] + mScrollOffset * (mRadius * 2 + mSpacing),用该坐标绘制选中圆形,实现跟随滑动的动画效果

内容的提问来源于stack exchange,提问作者Luther

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 15:11:16