如何自定义Drawable

269 阅读1分钟

1.Android将xml解析为Drawable流程分析

大致时序图如下:

sequenceDiagram
Resource ->> Resource: getDrawable()
Resource ->> Resource: getDrawableForDensity()
Resource ->> Resource: loadDrawable()
Resource ->> ResourcesImpl: loadDrawable()
ResourcesImpl ->> ResourcesImpl: loadDrawableForCookie()
ResourcesImpl ->> ResourcesImpl: loadXmlDrawable()
ResourcesImpl ->> Drawable: createFromXmlForDensity()
ResourcesImpl ->> Drawable: createFromXmlForDensity()
Drawable ->> DrawableInflater: inflateFromXmlForDensity()
DrawableInflater ->> DrawableInflater: inflateFromTag()/inflateFromClass()

着重看下DrawableInflater#inflateFromXmlForDensity()方法

Drawable inflateFromXmlForDensity(@NonNull String name, @NonNull XmlPullParser parser,
        @NonNull AttributeSet attrs, int density, @Nullable Theme theme)
        throws XmlPullParserException, IOException {
    // ...省略部分代码
    Drawable drawable = inflateFromTag(name);
    if (drawable == null) {
        drawable = inflateFromClass(name);
    }
    drawable.setSrcDensityOverride(density);
    drawable.inflate(mRes, parser, attrs, theme);
    return drawable;
}

其中inflateFromTag是用于解析类似于shape gradient等:

private Drawable inflateFromTag(@NonNull String name) {
    switch (name) {
        case "selector":
            return new StateListDrawable();
        case "animated-selector":
            return new AnimatedStateListDrawable();
        case "level-list":
            return new LevelListDrawable();
        case "layer-list":
            return new LayerDrawable();
        case "transition":
            return new TransitionDrawable();
        case "ripple":
            return new RippleDrawable();
        case "adaptive-icon":
            return new AdaptiveIconDrawable();
        case "color":
            return new ColorDrawable();
        case "shape":
            return new GradientDrawable();
        case "vector":
            return new VectorDrawable();
        case "animated-vector":
            return new AnimatedVectorDrawable();
        case "scale":
            return new ScaleDrawable();
        case "clip":
            return new ClipDrawable();
        case "rotate":
            return new RotateDrawable();
        case "animated-rotate":
            return new AnimatedRotateDrawable();
        case "animation-list":
            return new AnimationDrawable();
        case "inset":
            return new InsetDrawable();
        case "bitmap":
            return new BitmapDrawable();
        case "nine-patch":
            return new NinePatchDrawable();
        case "animated-image":
            return new AnimatedImageDrawable();
        default:
            return null;
    }
}

如果inflateFromTag返回为空,则走到inflateFromClass:

private Drawable inflateFromClass(@NonNull String className) {
    try {
        Constructor<? extends Drawable> constructor;
        synchronized (CONSTRUCTOR_MAP) {
            constructor = CONSTRUCTOR_MAP.get(className);
            if (constructor == null) {
                final Class<? extends Drawable> clazz =
                        mClassLoader.loadClass(className).asSubclass(Drawable.class);
                constructor = clazz.getConstructor();
                CONSTRUCTOR_MAP.put(className, constructor);
            }
        }
        return constructor.newInstance();
    } 
    // ... 
}

故我们可以通过类名自定义Drawable

2.自定义Drawable实例

2.1 java/kotlin 声明自定义Drawable

@Keep
public class TestDrawable extends GradientDrawable {

    public TestDrawable() {
        super();
    }
}

2.2 xml中使用自定义Drawable

drawable_test.xml

<?xml version="1.0" encoding="utf-8"?>
<com.test.drawable.TestDrawable
    xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 设置圆角 -->
<!--    <corners android:radius="10dp"/>-->
</com.test.drawable.TestDrawable>

2.3 xml/java文件中使用

xml/java中与android原生drawable.xml使用方法一致

前人栽树后人乘凉,感谢大佬