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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 09:33:20