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

如何在Drawable XML中自定义Shape类?加载ExtOvalShape时崩溃

问题分析与解决方案

核心问题

  1. XML结构错误:Selector的每个<item>只能包含一个Drawable,你在单个<item>里放了两个ExtOvalShape元素,不符合Drawable XML语法规范。
  2. 自定义Shape使用方式错误:ExtOvalShape继承的OvalShape属于android.graphics.drawable.shapes.Shape,它不是Drawable子类,不能直接在Drawable XML中作为节点声明,必须通过ShapeDrawable(或自定义Drawable子类)包装使用。

正确实现步骤

步骤1:修正Selector XML结构

每个状态对应独立的<item>,每个<item>内只放一个自定义Drawable:

<selector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:state_selected="true">
        <com.xxx.ui.widget.drawable.ExtOvalDrawable
            app:solid_color="@color/theme_accent_1"
            app:stroke_width="1dp"
            app:stroke_color="@color/background_4"
            app:shape_width="@dimen/part_number_value"
            app:shape_height="@dimen/part_number_value" />
    </item>
    <item android:state_selected="false">
        <com.xxx.ui.widget.drawable.ExtOvalDrawable
            app:solid_color="@android:color/transparent"
            app:stroke_width="1dp"
            app:stroke_color="@color/background_4"
            app:shape_width="@dimen/part_number_value"
            app:shape_height="@dimen/part_number_value" />
    </item>
</selector>

步骤2:定义自定义Drawable包装Shape

创建ExtOvalDrawable继承ShapeDrawable,内部使用你的ExtOvalShape,同时处理XML属性解析:

public class ExtOvalDrawable extends ShapeDrawable {

    public ExtOvalDrawable() {
        super(new ExtOvalShape());
    }

    // 必须实现带AttributeSet的构造方法,用于XML加载
    public ExtOvalDrawable(AttributeSet attrs) {
        super(new ExtOvalShape());
        Context context = ContextCompat.getDrawableContext(attrs);
        if (context == null) {
            return;
        }

        // 解析自定义属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExtOvalDrawable);
        
        // 解析填充色
        int solidColor = typedArray.getColor(R.styleable.ExtOvalDrawable_solid_color, Color.TRANSPARENT);
        getPaint().setColor(solidColor);
        
        // 解析描边
        int strokeWidth = typedArray.getDimensionPixelSize(R.styleable.ExtOvalDrawable_stroke_width, 0);
        int strokeColor = typedArray.getColor(R.styleable.ExtOvalDrawable_stroke_color, Color.TRANSPARENT);
        if (strokeWidth > 0) {
            getPaint().setStrokeWidth(strokeWidth);
            // 同时支持填充和描边
            Paint paint = getPaint();
            paint.setStyle(Paint.Style.FILL_AND_STROKE);
            // 先画填充再画描边,需要保存原填充色
            int originalColor = paint.getColor();
            paint.setColor(solidColor);
            super.draw(canvas);
            paint.setColor(strokeColor);
            super.draw(canvas);
            paint.setColor(originalColor);
            paint.setStyle(Paint.Style.FILL);
        }

        // 解析尺寸
        int width = typedArray.getDimensionPixelSize(R.styleable.ExtOvalDrawable_shape_width, 0);
        int height = typedArray.getDimensionPixelSize(R.styleable.ExtOvalDrawable_shape_height, 0);
        if (width > 0 && height > 0) {
            setIntrinsicWidth(width);
            setIntrinsicHeight(height);
        }

        typedArray.recycle();
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        // 这里可以添加自定义绘制逻辑,比如在椭圆上额外绘制标记
    }
}

步骤3:定义自定义属性

在res/values/attrs.xml中添加对应的属性,让XML可以配置填充、描边、尺寸:

<resources>
    <declare-styleable name="ExtOvalDrawable">
        <attr name="solid_color" format="color|reference" />
        <attr name="stroke_width" format="dimension|reference" />
        <attr name="stroke_color" format="color|reference" />
        <attr name="shape_width" format="dimension|reference" />
        <attr name="shape_height" format="dimension|reference" />
    </declare-styleable>
</resources>

步骤4:保留自定义Shape类

你的ExtOvalShape可以继续使用,如需修改椭圆绘制逻辑,直接在draw方法扩展:

public class ExtOvalShape extends OvalShape {

    @Override
    public void draw(Canvas canvas, Paint paint) {          
        super.draw(canvas, paint);
        // 这里可以添加自定义逻辑,比如调整椭圆的绘制范围、添加额外图形等
    }
}

关键说明

  • Shape是纯绘制逻辑类,不具备Drawable的生命周期和属性解析能力,必须通过ShapeDrawable或自定义Drawable封装后才能在XML中使用。
  • 自定义Drawable必须实现带AttributeSet的构造方法,否则XML无法加载该类。
  • 同时使用填充和描边时,需要分两次绘制(先填充后描边),确保两者都能正确显示。

内容的提问来源于stack exchange,提问作者anthony

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 06:43:16