Android 自定义 svg 颜色

2018-10-18 11:41:33 浏览数 (1)

1、XML 设定颜色

代码语言:javascript复制
  <vector android:height="24dp" android:viewportHeight="1024"
    android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#FFF" android:pathData="M395.22,513.6l323.14,-312.37c19.05,-18.42 19.05,-48.27 0,-66.66 -19.05,-18.42 -49.91,-18.42 -68.96,0L291.75,480.29c-19.05,18.42 -19.05,48.27 0,66.66l357.63,345.69c9.53,9.21 22.01,13.8 34.5,13.8 12.49,0 24.97,-4.59 34.47,-13.83 19.05,-18.42 19.05,-48.24 0,-66.66L395.22,513.6z"/>
  </vector>

xml设定颜色很简单,fillColor 这个attr即可设定。

2、kotlin代码动态设定颜色

先构造一个适用的对象

代码语言:javascript复制
/**
 * svg 图片需要构建的对象
 */
data class InitImgRes(
        @DrawableRes val imgRes: Int,
        @ColorRes val colorRes: Int,
        val imageView: ImageView,
        val context: Context
)

一般很多博客是这样写的:

代码语言:javascript复制
   /**
     * 给imageView 的svg初始化颜色
     */
    fun initSvgColor(initImgRes: InitImgRes){
        // 获取该image的资源
        val res  = initImgRes.context.resources
        // 获取该image的主题对象
        val theme = initImgRes.context.theme
        //创造vectorDrawable工具对象,影响vectorView绘制
        val vectorDrawableCompat = VectorDrawableCompat.create(res,initImgRes.imgRes,theme) ?: return
        // 使用Tint上色
        if (Build.VERSION.SDK_INT>22)
        vectorDrawableCompat.setTint(res.getColor(initImgRes.colorRes,theme))
        else vectorDrawableCompat.setTint(res.getColor(initImgRes.colorRes))
        initImgRes.imageView.setImageDrawable(vectorDrawableCompat)
    }

VectorDrawableCompat create源码如下:

代码语言:javascript复制
   @Nullable
    public static VectorDrawableCompat create(@NonNull Resources res, @DrawableRes int resId, @Nullable Theme theme) {
        if (VERSION.SDK_INT >= 24) {
            VectorDrawableCompat drawable = new VectorDrawableCompat();
            drawable.mDelegateDrawable = ResourcesCompat.getDrawable(res, resId, theme);
            drawable.mCachedConstantStateDelegate = new VectorDrawableCompat.VectorDrawableDelegateState(drawable.mDelegateDrawable.getConstantState());
            return drawable;
        } else {
            try {
                XmlPullParser parser = res.getXml(resId);
                AttributeSet attrs = Xml.asAttributeSet(parser);

                int type;
                while((type = parser.next()) != 2 && type != 1) {
                    ;
                }

                if (type != 2) {
                    throw new XmlPullParserException("No start tag found");
                }

                return createFromXmlInner(res, parser, attrs, theme);
            } catch (XmlPullParserException var6) {
                Log.e("VectorDrawableCompat", "parser error", var6);
            } catch (IOException var7) {
                Log.e("VectorDrawableCompat", "parser error", var7);
            }

            return null;
        }
    }

调用影响state方法.png

根据源码,我们不难看出在24之前,通过drawable的xml解析,来上色,这样效率非常低,再通过24之后的版本,自建了一个drawable对象,在此对象中运行影响VectorView的state这样造成的后果是,VectorView的state永远赋值,当前xml下的svg永远上色为最后一个颜色。故抛弃此类写法


正确代码写法:

代码语言:javascript复制
    /**
     * 给imageView 的svg初始化颜色
     */
    fun initSvgColor(initImgRes: InitImgRes){
        //利用ContextCompat工具类获取drawable图片资源
        val drawable = ContextCompat.getDrawable(initImgRes.context, initImgRes.imgRes)?:return
        //简单的使用tint改变drawable颜色
        val drawableResult = tintDrawable(drawable,ContextCompat.getColor(initImgRes.context, initImgRes.colorRes))
        initImgRes.imageView.setImageDrawable(drawableResult)
    }

    /**
     * 给drawable上色 
     */
     private fun  tintDrawable( drawable:Drawable, colors:Int): Drawable {
        val wrap = DrawableCompat.wrap(drawable).mutate()
        DrawableCompat.setTint(wrap,colors)
        return wrap
    }

再查看源码:

mutate方法源码解释.png

源码注释告诉了我们:此获取的drawable不与其他drawable 共享,简而言之,就是构建单独的内存模块来存储此drawable达到相互不影响的状态。

此种写法代码量减少的很明显,很能理解,先直接获取svg 的drawable 对象,然后通过预设资源,获取颜色进而给当前对象上色即可,不需要影响vectorView绘制。推荐使用。

0 人点赞