如何在RecyclerView列表项末尾添加带计数的通知徽章?
针对RecyclerView列表项末尾通知徽章的实现思路
方案一:自定义自适应尺寸的徽章View
直接写个继承TextView的自定义View,自动根据计数文本调整形状和大小:
- 重写
onMeasure方法,测量文本宽度后动态设置View宽高:- 计数≤9时,强制宽高相等(圆形),取文本宽高中的较大值加内边距;
- 计数≥10时,用圆角矩形,宽度取文本宽度加左右内边距,高度取文本高度加上下内边距。
- 用
ShapeDrawable或XML Drawable作为背景,动态切换圆形/圆角矩形样式。 - 在RecyclerView的item布局里把这个自定义View放在末尾,绑定数据时直接设置计数文本即可。
核心代码示例:
public class BadgeTextView extends AppCompatTextView { private static final int CORNER_RADIUS = 12; private static final int PADDING = 8; public BadgeTextView(Context context) { super(context); init(); } private void init() { setTextColor(Color.WHITE); setGravity(Gravity.CENTER); setPadding(PADDING, PADDING, PADDING, PADDING); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int contentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); int contentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); if (getText().length() <= 1) { int size = Math.max(contentWidth, contentHeight) + PADDING * 2; setMeasuredDimension(size, size); GradientDrawable drawable = new GradientDrawable(); drawable.setColor(Color.RED); drawable.setCornerRadius(size / 2f); setBackground(drawable); } else { int height = Math.max(contentWidth, contentHeight) + PADDING * 2; setMeasuredDimension(getMeasuredWidth(), height); GradientDrawable drawable = new GradientDrawable(); drawable.setColor(Color.RED); drawable.setCornerRadius(height / 2f); setBackground(drawable); } } }
方案二:用ConstraintLayout+BadgeDrawable实现居中徽章
解决之前用不可见按钮无法居中的问题:
- 在RecyclerView的item布局中添加一个透明辅助View(宽高设为徽章最小圆形尺寸,比如24dp);
- 用ConstraintLayout将辅助View定位到列表项末尾(比如和右侧元素对齐、设置margin);
- 创建BadgeDrawable并设置
setGravity(Gravity.CENTER),绑定到辅助View上; - 更新计数时直接调用
badgeDrawable.setNumber(count)即可。
布局片段示例:
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- 列表项主体内容 --> <TextView android:id="@+id/tv_item_content" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@+id/helper_view"/> <!-- 透明辅助View --> <View android:id="@+id/helper_view" android:layout_width="24dp" android:layout_height="24dp" android:background="@android:color/transparent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="@id/tv_item_content" app:layout_constraintBottom_toBottomOf="@id/tv_item_content"/> </androidx.constraintlayout.widget.ConstraintLayout>
代码绑定徽章:
View helperView = itemView.findViewById(R.id.helper_view); BadgeDrawable badge = BadgeDrawable.create(context); badge.setGravity(Gravity.CENTER); badge.setNumber(5); BadgeUtils.attachBadgeDrawable(badge, helperView);
方案三:动态调整TextView背景实现
用普通TextView配合动态背景,无需自定义View:
- 准备两个XML Drawable:圆形背景(
badge_circle.xml)、圆角矩形背景(badge_rect.xml); - 绑定数据时根据计数长度切换背景:
- 计数≤9:设置圆形背景,TextView宽高设为固定值(比如24dp);
- 计数≥10:设置圆角矩形背景,TextView宽高设为
wrap_content,添加适当左右内边距;
- 确保TextView的
gravity设为center,文本居中显示。
代码示例:
TextView badgeTv = itemView.findViewById(R.id.tv_badge); int count = data.getCount(); badgeTv.setText(String.valueOf(count)); if (count <= 9) { badgeTv.setBackgroundResource(R.drawable.badge_circle); ViewGroup.LayoutParams params = badgeTv.getLayoutParams(); params.width = dp2px(24); params.height = dp2px(24); badgeTv.setLayoutParams(params); badgeTv.setPadding(dp2px(0), dp2px(0), dp2px(0), dp2px(0)); } else { badgeTv.setBackgroundResource(R.drawable.badge_rect); ViewGroup.LayoutParams params = badgeTv.getLayoutParams(); params.width = ViewGroup.LayoutParams.WRAP_CONTENT; params.height = dp2px(24); badgeTv.setLayoutParams(params); badgeTv.setPadding(dp2px(8), dp2px(0), dp2px(8), dp2px(0)); } // 辅助方法:dp转px private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); }
内容的提问来源于stack exchange,提问作者gureisu
相关产品推荐
相关产品推荐

