Android Material 3中如何将标签页徽章对齐至文本末尾?
解决Material 3 TabLayout无图标标签徽章定位到文本末尾的问题
针对无图标TabLayout标签,默认通过getOrCreateBadge()获取的徽章会贴在标签容器右上角,无法匹配Material 3规范中"徽章跟随文本末尾"的样式。以下是两种可行的解决方案:
方案一:通过TextView CompoundDrawable实现文本末尾徽章
直接利用Tab标题TextView的右侧复合Drawable来模拟徽章,自然跟随文本位置:
- 获取目标Tab的TextView控件:
val targetTab = tabLayout.getTabAt(yourTabIndex) val tabTextView = targetTab?.view?.findViewById<TextView>(com.google.android.material.R.id.tab_text)
- 创建符合M3风格的徽章Drawable:
fun createM3Badge(context: Context, badgeCount: Int): Drawable { val badgeSize = context.resources.getDimensionPixelSize(com.google.android.material.R.dimen.m3_badge_size) val badgeTextSize = context.resources.getDimensionPixelSize(com.google.android.material.R.dimen.m3_badge_text_size) // 徽章背景 val badgeBg = ShapeDrawable(OvalShape()).apply { paint.color = context.getColor(com.google.android.material.R.color.m3_sys_color_error) } // 徽章数字文本 val badgeText = object : TextDrawable() { init { text = badgeCount.toString() textSize = badgeTextSize.toFloat() color = context.getColor(com.google.android.material.R.color.m3_sys_color_on_error) gravity = Gravity.CENTER } } return LayerDrawable(arrayOf(badgeBg, badgeText)).apply { setLayerSize(0, badgeSize, badgeSize) setLayerSize(1, badgeSize, badgeSize) intrinsicWidth = badgeSize intrinsicHeight = badgeSize } }
- 将徽章设置到TextView右侧,并调整间距:
tabTextView?.let { tv -> val badgeDrawable = createM3Badge(tv.context, 3) // 传入徽章显示数字 val textBadgeSpacing = tv.context.resources.getDimensionPixelSize(com.google.android.material.R.dimen.m3_badge_text_spacing) tv.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, badgeDrawable, null) tv.compoundDrawablePadding = textBadgeSpacing }
方案二:调整默认BadgeDrawable的位置
通过计算文本宽度,修正默认徽章的偏移量,使其对齐文本末尾:
val targetTab = tabLayout.getTabAt(yourTabIndex) val tabTextView = targetTab?.view?.findViewById<TextView>(com.google.android.material.R.id.tab_text) // 需在布局完成后执行,避免容器宽度为0 tabLayout.post { targetTab?.getOrCreateBadge()?.let { badge -> badge.badgeGravity = BadgeDrawable.TOP_END val textWidth = tabTextView?.paint?.measureText(tabTextView.text.toString())?.toInt() ?: 0 val containerWidth = targetTab.view.width val badgeWidth = badge.intrinsicWidth // 水平偏移:让徽章中心对齐文本末尾 badge.horizontalOffset = (containerWidth - textWidth - badgeWidth / 2) * -1 // 垂直偏移:让徽章与文本垂直居中 val textLineHeight = tabTextView?.lineHeight ?: 0 badge.verticalOffset = (textLineHeight / 2 - badge.intrinsicHeight / 2) * -1 } }
方案对比
- 方案一:完全基于文本位置,不受容器尺寸影响,风格更贴合M3规范,推荐使用;
- 方案二:基于默认徽章调整,无需自定义Drawable,但需处理布局完成时机,适配性稍弱。
内容的提问来源于stack exchange,提问作者suue
相关产品推荐
相关产品推荐

