Android能否实现异形圆角Drawable/Canvas?用于TabLayout指示器
Android实现异形圆角TabLayout指示器方案
完全可以实现这种异形圆角的TabLayout指示器,以下是两种实用的实现方式:
方案一:自定义Drawable实现(推荐)
通过自定义Drawable绘制异形圆角路径,直接设置给TabLayout作为指示器,无需修改TabLayout的继承关系,复用性强。
1. 自定义Indicator Drawable类
public class CustomTabIndicatorDrawable extends Drawable { private final Paint mPaint; private final Path mPath; private final int mCornerRadius; private final int mLeftMargin; private final int mTopMargin; private final int mRightMargin; public CustomTabIndicatorDrawable(int color, int cornerRadius, int leftMargin, int topMargin, int rightMargin) { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(color); mPaint.setStyle(Paint.Style.FILL); mCornerRadius = cornerRadius; mLeftMargin = leftMargin; mTopMargin = topMargin; mRightMargin = rightMargin; mPath = new Path(); } @Override public void draw(@NonNull Canvas canvas) { Rect bounds = getBounds(); // 计算实际绘制区域,扣除设置的间距 int drawLeft = bounds.left + mLeftMargin; int drawTop = bounds.top + mTopMargin; int drawRight = bounds.right - mRightMargin; int drawBottom = bounds.bottom; // 构建异形圆角路径:仅顶部左右两个圆角,底部为直角 mPath.reset(); mPath.moveTo(drawLeft + mCornerRadius, drawTop); // 绘制右上角圆角 mPath.arcTo(drawRight - 2 * mCornerRadius, drawTop, drawRight, drawTop + 2 * mCornerRadius, 270, 90, false); mPath.lineTo(drawRight, drawBottom); mPath.lineTo(drawLeft, drawBottom); // 绘制左上角圆角 mPath.arcTo(drawLeft, drawTop, drawLeft + 2 * mCornerRadius, drawTop + 2 * mCornerRadius, 180, 90, false); mPath.close(); canvas.drawPath(mPath, mPaint); } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); invalidateSelf(); } @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { mPaint.setColorFilter(colorFilter); invalidateSelf(); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } }
2. 给TabLayout设置自定义指示器
在Activity/Fragment中配置参数并设置:
TabLayout tabLayout = findViewById(R.id.tabLayout); // 保持你已设置的全屏宽度模式 tabLayout.setTabIndicatorGravity(TabLayout.INDICATOR_GRAVITY_STRETCH); // 转换dp为px,适配不同屏幕 int cornerRadius = dpToPx(12); int leftMargin = dpToPx(16); int topMargin = dpToPx(4); int rightMargin = dpToPx(16); int indicatorColor = getResources().getColor(R.color.tab_indicator_color); // 创建自定义指示器Drawable CustomTabIndicatorDrawable indicatorDrawable = new CustomTabIndicatorDrawable( indicatorColor, cornerRadius, leftMargin, topMargin, rightMargin ); // 设置指示器及高度 tabLayout.setSelectedTabIndicator(indicatorDrawable); tabLayout.setSelectedTabIndicatorHeight(dpToPx(8));
3. 工具方法:dp转px
private int dpToPx(int dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics() ); }
方案二:自定义TabLayout重写绘制逻辑
如果需要更灵活的控制,可以自定义TabLayout,直接在onDraw中绘制指示器:
1. 自定义TabLayout类
public class CustomTabLayout extends TabLayout { private final Paint mIndicatorPaint; private final int mCornerRadius; private final int mLeftMargin; private final int mTopMargin; private final int mRightMargin; private final int mIndicatorHeight; public CustomTabLayout(Context context) { this(context, null); } public CustomTabLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomTabLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initParams(); } private void initParams() { mIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mIndicatorPaint.setColor(getResources().getColor(R.color.tab_indicator_color)); mIndicatorPaint.setStyle(Paint.Style.FILL); // 可通过自定义属性配置这些参数,这里直接示例数值 mCornerRadius = dpToPx(12); mLeftMargin = dpToPx(16); mTopMargin = dpToPx(4); mRightMargin = dpToPx(16); mIndicatorHeight = dpToPx(8); // 强制设置全屏宽度模式 setTabIndicatorGravity(INDICATOR_GRAVITY_STRETCH); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int selectedTabPos = getSelectedTabPosition(); if (selectedTabPos == -1) return; Tab selectedTab = getTabAt(selectedTabPos); if (selectedTab == null) return; Rect tabBounds = selectedTab.getView().getBounds(); int drawLeft = tabBounds.left + mLeftMargin; int drawTop = tabBounds.top + mTopMargin; int drawRight = tabBounds.right - mRightMargin; int drawBottom = drawTop + mIndicatorHeight; // 绘制异形圆角路径 Path path = new Path(); path.moveTo(drawLeft + mCornerRadius, drawTop); path.arcTo(drawRight - 2 * mCornerRadius, drawTop, drawRight, drawTop + 2 * mCornerRadius, 270, 90, false); path.lineTo(drawRight, drawBottom); path.lineTo(drawLeft, drawBottom); path.arcTo(drawLeft, drawTop, drawLeft + 2 * mCornerRadius, drawTop + 2 * mCornerRadius, 180, 90, false); path.close(); canvas.drawPath(path, mIndicatorPaint); } private int dpToPx(int dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics() ); } }
2. 布局中使用自定义TabLayout
<com.yourpackage.CustomTabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="fixed" app:tabGravity="fill"/>
注意事项
- 圆角半径、间距、指示器高度等数值可根据需求自由调整,推荐通过自定义属性配置,方便在布局中直接修改。
- 如果使用Material Components库的TabLayout,确保依赖版本兼容,避免默认样式冲突。
内容的提问来源于stack exchange,提问作者Sue007
相关产品推荐
相关产品推荐

