Android 动态设置Shape

来源:互联网 发布:wnba赛果数据 编辑:程序博客网 时间:2024/04/28 00:15

引言:之前涉及到设置view背景的地方几乎都是通过写<shape>标签的方式实现的。慢慢的,项目里的xml越来越多,命名都成问题了!于是就想用动态设置shape的方式来替换静态配置shape标签。

静态配置shape

这个不多说了,梯子备好了,自行前往!
https://developer.android.google.cn/guide/topics/resources/drawable-resource.html#Shape
里面有非常详细的介绍。包括shape 等标签参数介绍、使用规则、映射的Drawaable。
Android 动态设置shape
Android shape标签对应的对象

这里对形状可绘制对象的描述感觉有点出入,写的是创建ShapeDrawable,但是进入对应的条目后发现是GradientDrawable.


动态设置shape

想要动态配置,首先需要知道在xml中写的<shape><selector><level-list> 等标签的映射对象是什么。这里有个插曲,最开始我也以为<shape>标签对应的就是ShapeDrawable呢,写的时候发现没法描边(Stroke), 试了两种方案 1.通过设置描边画笔,给paint设置宽度和颜色;2.用LevelListDrawable 通过设置多个层一个填充层,一个描边层来组合。结果这两种方法都不行,后来才去翻的源码。从view.setBackgroundResource()开始追。

Resources.java

  public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)            throws NotFoundException {        final TypedValue value = obtainTempTypedValue();        try {            final ResourcesImpl impl = mResourcesImpl;            impl.getValue(id, value, true);            // 从这里开始加载Drawable            return impl.loadDrawable(this, value, id, theme, true);        } finally {            releaseTempTypedValue(value);        }    }

ResourcesImpl.java

  @Nullable  Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,            boolean useCache) throws NotFoundException {        try {            ......            final Drawable.ConstantState cs;            if (isColorDrawable) {                cs = sPreloadedColorDrawables.get(key);            } else {                cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);            }            Drawable dr;            if (cs != null) {                dr = cs.newDrawable(wrapper);            } else if (isColorDrawable) {                dr = new ColorDrawable(value.data);            } else {                // 从xml或者资源流中加载Drawable                dr = loadDrawableForCookie(wrapper, value, id, null);            }            return dr;        } catch (Exception e) {            ......        }    }
    /**     * Loads a drawable from XML or resources stream.     * 从xml或者数据流中加载Drawable     */    private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id, Resources.Theme theme) {        final String file = value.string.toString();        ......        final Drawable dr;        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);        try {            if (file.endsWith(".xml")) {            // ok,我们看下这里,通过解析器把xml文件解析成Drawable对象                final XmlResourceParser rp = loadXmlResourceParser(                        file, id, value.assetCookie, "drawable");                dr = Drawable.createFromXml(wrapper, rp, theme);                rp.close();            } else {            // 流的解析                final InputStream is = mAssets.openNonAsset(                        value.assetCookie, file, AssetManager.ACCESS_STREAMING);                dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);                is.close();            }        } catch (Exception e) {            ......        }        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);        return dr;    }

Drawable.java

    // 使用可选的theme从XML文档中创建drawable对象。    public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)            throws XmlPullParserException, IOException {        AttributeSet attrs = Xml.asAttributeSet(parser);        ......        // 从xml内部以指定主题创建一个Drawable对象        Drawable drawable = createFromXmlInner(r, parser, attrs, theme);        if (drawable == null) {            throw new RuntimeException("Unknown initial tag: " + parser.getName());        }        return drawable;    }
    // 从xml内部以指定主题创建一个Drawable对象    public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,            Theme theme) throws XmlPullParserException, IOException {        return r.getDrawableInflater().inflateFromXml(parser.getName(), parser, attrs, theme);    }

追到这里发现是从Resources 中调用了一个方法。在Resources.java 中搜索getDrawableInflater()这个方法,出现了DrawableInflater这个类。
Resources.java

    // 该inflater用于创建Drawable对象    public final DrawableInflater getDrawableInflater() {        if (mDrawableInflater == null) {            mDrawableInflater = new DrawableInflater(this, mClassLoader);        }        return mDrawableInflater;    }

没法直接追下去了,既然这样我们在源码中直接搜索DrawableInflater.java这个类,然后查看它里面的inflateFromXml() 方法。(注:我这里的源码版本是android-25)
DrawableInflater.java

    @NonNull    @SuppressWarnings("deprecation")    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 "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();            default:                return null;        }    }

到这里总该明白与xml中的标签(shape等标签)相对应的对象是哪些了吧。项目中用的最多的就是圆角矩形背景了,然后有的会有描边,还有选择器的效果。我们就用GradientDrawable 来实现。GradientDrawable可以用来设置shape类型、shape填充色、描边色和矩形的边角弧度。


动态设置shape代码

以圆角矩形为例:
android 动态设置shape

    /**     * 获得一个指定填充色,边框宽度、颜色的圆角矩形drawable。     * Android 中 在xml中写的"shape"标签映射对象就是GradientDrawable。     * 通过设置solidColors 和strokeColors 可实现选择器的效果     *     * @param solidColors  填充色     * @param strokeColors 描边色     * @param strokeWidth  描边线宽度     * @param dashWidth    虚线(破折线)的长度(以像素为单位)     * @param dashGap      虚线(破折线)间距,当dashGap=0dp时,为实线     * @param radius       圆角角度     * @return GradientDrawable     */    public static Drawable getShapeDrawable(ColorStateList solidColors,                                            ColorStateList strokeColors, int strokeWidth,                                             float dashWidth, float dashGap,                                            float radius) {        GradientDrawable gradientDrawable = new GradientDrawable();        gradientDrawable.setShape(GradientDrawable.RECTANGLE);        gradientDrawable.setCornerRadius(radius);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {            gradientDrawable.setColor(solidColors);            //显示一条虚线,破折线的宽度为dashWith,破折线之间的空隙的宽度为dashGap,当dashGap=0dp时,为实线            gradientDrawable.setStroke(strokeWidth, strokeColors, dashWidth, dashGap);        } else {            gradientDrawable.setColor(solidColors.getDefaultColor());            //显示一条虚线,破折线的宽度为dashWith,破折线之间的空隙的宽度为dashGap,当dashGap=0dp时,为实线            gradientDrawable.setStroke(strokeWidth, strokeColors.getDefaultColor(), dashWidth, dashGap);        }        return gradientDrawable;    }  /**     * 获得一个指定填充色,指定描边色的圆角矩形drawable     *     * @param solidColor  填充色     * @param strokeColor 描边色     * @param strokeWidth 描边线宽度     * @param dashWidth   虚线(破折线)宽度     * @param dashGap     虚线(破折线)间距,当dashGap=0dp时,为实线     * @param radius      圆角角度     * @return GradientDrawable     */    public static Drawable getShapeDrawable(@ColorInt int solidColor,                                            @ColorInt int strokeColor, int strokeWidth,                                            float dashWidth, float dashGap,                                            float radius) {        return getShapeDrawable(ColorStateList.valueOf(solidColor),                ColorStateList.valueOf(strokeColor), strokeWidth, dashWidth, dashGap,                radius);    }    /**     * 获得一个选择器Drawable.     * Android 中 在xml中写的"selector"标签映射对象就是StateListDrawable 对象     *     * @param defaultDrawable 默认时显示的Drawable     * @param pressedDrawable 按下时显示的Drawable     * @return 选择器Drawable     */    public static StateListDrawable getSelectorDrawable(Drawable defaultDrawable, Drawable pressedDrawable) {        if (defaultDrawable == null) return null;        if (pressedDrawable == null) pressedDrawable = defaultDrawable;        int[][] state = {{-android.R.attr.state_pressed}, {android.R.attr.state_pressed}};        StateListDrawable stateListDrawable = new StateListDrawable();        stateListDrawable.addState(state[0], defaultDrawable);        stateListDrawable.addState(state[1], pressedDrawable);        return stateListDrawable;    }    /**     * 获得一个选择器Drawable.     * Android 中 在xml中写的"selector"标签映射对象就是StateListDrawable 对象     *     * @param defaultColor 默认时显示的颜色     * @param pressedColor 按下时显示的颜色     * @return 选择器Drawable     */    public static StateListDrawable getSelectorDrawable(int defaultColor, int pressedColor, float radius) {        Drawable defaultDrawable = getSolidShapeDrawable(defaultColor, radius);        Drawable pressedDrawable = getSolidShapeDrawable(pressedColor, radius);        return getSelectorDrawable(defaultDrawable, pressedDrawable);    }

更多功能参考Drawable工具类:http://blog.csdn.net/JM_beizi/article/details/77433558

原创粉丝点击