ViewPager迁移至ViewPager2后,如何实现PagerTitleStrip功能?
如何在ViewPager2中实现类似PagerTitleStrip的效果
嘿,我懂你的困扰——ViewPager2是基于RecyclerView完全重构的,原ViewPager里的PagerTitleStrip确实没法直接适配。不过我们有两种方式可以复刻它的核心效果(就是那种跟随页面滑动时标题条位移、选中标题高亮的交互),下面给你详细拆解:
方案一:自定义View实现核心效果
这种方式可以1:1还原PagerTitleStrip的行为,完全由你掌控样式和交互逻辑,步骤如下:
1. 自定义TitleStrip组件
创建一个继承自LinearLayout的自定义View,用来管理标题的展示和滑动位移:
class ViewPager2TitleStrip @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : LinearLayout(context, attrs, defStyleAttr) { private var viewPager2: ViewPager2? = null private val titleViews = mutableListOf<TextView>() private var currentPage = 0 private var scrollOffset = 0f init { orientation = HORIZONTAL gravity = CENTER_VERTICAL setPadding(dp2px(16), 0, dp2px(16), 0) } // 绑定ViewPager2并监听滑动事件 fun attachToViewPager(pager: ViewPager2) { viewPager2 = pager pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageScrolled( position: Int, positionOffset: Float, positionOffsetPixels: Int ) { currentPage = position scrollOffset = positionOffset updateTitleScrollPosition() } override fun onPageSelected(position: Int) { updateTitleStyle() } }) initTitles() } // 从Adapter获取标题并生成TextView private fun initTitles() { removeAllViews() titleViews.clear() val adapter = viewPager2?.adapter ?: return for (i in 0 until adapter.itemCount) { val title = (adapter as? PageTitleProvider)?.getPageTitle(i) ?: "" val tv = TextView(context).apply { text = title textSize = 16f setTextColor(Color.GRAY) layoutParams = LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT ).apply { marginStart = dp2px(16) marginEnd = dp2px(16) } } addView(tv) titleViews.add(tv) } updateTitleStyle() } // 跟随页面滑动,调整标题条的滚动位置 private fun updateTitleScrollPosition() { if (titleViews.size <= 1) return // 计算偏移量,让选中标题始终靠近屏幕中心 val scrollX = (width / 2 - titleViews[currentPage].width / 2) * scrollOffset scrollTo(scrollX.toInt(), 0) } // 更新选中/未选中标题的样式(颜色、大小) private fun updateTitleStyle() { titleViews.forEachIndexed { index, tv -> if (index == currentPage) { tv.setTextColor(Color.BLACK) tv.textSize = 20f } else { tv.setTextColor(Color.GRAY) tv.textSize = 16f } } } private fun dp2px(dp: Int): Int { return (dp * resources.displayMetrics.density).toInt() } // 定义接口,让Adapter提供页面标题 interface PageTitleProvider { fun getPageTitle(position: Int): CharSequence } }
2. 让PagerAdapter实现标题提供接口
你的ViewPager2适配器需要实现上面的PageTitleProvider接口,用来给标题条提供每个页面的标题:
class MyPagerAdapter : RecyclerView.Adapter<MyPagerAdapter.ViewHolder>(), ViewPager2TitleStrip.PageTitleProvider { private val pageTitles = listOf("首页", "发现", "我的", "设置") override fun getPageTitle(position: Int): CharSequence { return pageTitles[position] } // 以下是Adapter常规实现 inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_page, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { // 绑定页面内容 } override fun getItemCount(): Int = pageTitles.size }
3. 在布局中使用自定义标题条
把ViewPager2TitleStrip放在ViewPager2的上方/下方,和原PagerTitleStrip的布局方式一致:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.yourpackage.ViewPager2TitleStrip android:id="@+id/titleStrip" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ffffff"/> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewPager2" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout>
4. 在代码中绑定组件
在Activity/Fragment里完成标题条和ViewPager2的绑定:
val viewPager2 = findViewById<ViewPager2>(R.id.viewPager2) val titleStrip = findViewById<ViewPager2TitleStrip>(R.id.titleStrip) viewPager2.adapter = MyPagerAdapter() titleStrip.attachToViewPager(viewPager2)
方案二:使用第三方适配库
有些第三方库已经封装好了ViewPager2的TitleStrip实现,比如ViewPager2Extensions这类库,它们直接提供了和原PagerTitleStrip用法几乎一致的组件,省去自定义的麻烦。不过要注意选择维护活跃的库,避免版本兼容问题。
关键注意点
- 原
PagerTitleStrip的核心是跟随页面滑动的位移效果,自定义实现时重点要处理onPageScrolled回调中的偏移量计算,让标题条整体滚动来匹配页面位置。 - 如果只是需要固定顶部的标签切换,
TabLayout配合TabLayoutMediator确实更简单,但这和PagerTitleStrip的交互逻辑完全不同——所以如果你要的是滑动时标题跟着移动的效果,还是自定义或用第三方库更合适。
内容的提问来源于stack exchange,提问作者Neoh
相关产品推荐
相关产品推荐

