Android收藏集

ImageView动态着色以及兼容性问题

2020-01-08  本文已影响0人  dlihasa

前言

最近看第三方库的demo发现ImageView使用了一个tint的属性,可以改变图片的颜色,这样在部分需求前可以减少项目中图片的数量,从而减少apk安装包大小。

应用

示例中原图片如下:


ic_func.png
1.根据业务逻辑,不同状态下,图片的颜色不同

(1)xml中可以设置tint属性,如下:

<ImageView
        android:id="@+id/iv_one"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:tint="@color/colorPrimary"
        android:src="@drawable/ic_func"/>
<ImageView
        android:id="@+id/iv_two"
        android:layout_below="@+id/iv_one"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:src="@drawable/ic_func"/>

效果如下:


效果图一

(2)根据业务动态修改,还得看代码中的方式(xml写法去除上面iv_one的tint属性):

代码实现一:

//Drawable drawable = getResources().getDrawable(R.drawable.ic_func);这种获取drawable效果是一样的
Drawable drawable = iv_one.getDrawable();//
Drawable wrap = DrawableCompat.wrap(drawable);
DrawableCompat.setTint(wrap, ContextCompat.getColor(this,android.R.color.holo_red_dark));
iv_one.setImageDrawable(wrap);
iv_two.setImageResource(R.drawable.ic_func);

效果如下:


20200107112227.png

代码实现二:

//Drawable drawable = getResources().getDrawable(R.drawable.ic_func).mutate();这种获取drawable效果是一样的
Drawable drawable = iv_one.getDrawable().mutate();//
Drawable wrap = DrawableCompat.wrap(drawable);
DrawableCompat.setTint(wrap, ContextCompat.getColor(this,android.R.color.holo_red_dark));
iv_one.setImageDrawable(wrap);
iv_two.setImageResource(R.drawable.ic_func);

效果如下:


20200107112204.png

代码一和代码二的区别:

在xml中都不添加tint属性的情况下,代码二多了一个mutate()方法,具体作用后面在说。只需要记住,如果你不想影响到该图片其他地方的使用,要添加mutate()方法

代码实现三:

//Drawable drawable = getResources().getDrawable(R.drawable.ic_func).mutate();这种获取drawable效果是一样的
Drawable drawable = iv_one.getDrawable().mutate();//
Drawable wrap = DrawableCompat.wrap(drawable);
DrawableCompat.setTint(wrap, ContextCompat.getColor(this,android.R.color.holo_red_dark));
iv_one.setImageDrawable(wrap);
iv_two.setImageDrawable(drawable);

这种写法效果和代码一相同。

2.设置图片的点击效果

先看一看一般的实现步骤:
(1)首先在res下创建color资源目录,在color目录下创建一个color resource文件,如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/colorPrimary" android:state_pressed="true"/>
    <item android:color="@android:color/holo_red_dark"/>
</selector>

(2)代码中如下:

//Drawable drawable = getResources().getDrawable(R.drawable.ic_func);
Drawable drawable = iv_one.getDrawable();
Drawable wrap = DrawableCompat.wrap(drawable).mutate();
DrawableCompat.setTintList(wrap,ContextCompat.getColorStateList(this,R.color.click_style_color));
iv_one.setImageDrawable(wrap);
iv_one.setOnClickListener(new View.OnClickListener() {
    @Override
     public void onClick(View view) {
         Toast.makeText(MainActivity.this,"点击了",Toast.LENGTH_SHORT).show();
     }
});

效果如下:


单击效果.gif

适配兼容性问题:

方案一:

/**
 * 支持tintList着色ImageView
 */

public class TintableImageView extends AppCompatImageView {

    private ColorStateList tint;

    public TintableImageView(Context context) {

        super(context);

    }

    public TintableImageView(Context context, AttributeSet attrs) {

        super(context, attrs);

        init(context, attrs, 0);

    }

    private void init(Context context, AttributeSet attrs, int defStyle) {

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TintableImageView, defStyle, 0);

        tint = a.getColorStateList(R.styleable.TintableImageView_iv_tint);

        a.recycle();

    }

    public TintableImageView(Context context, AttributeSet attrs, int defStyle) {

        super(context, attrs, defStyle);

        init(context, attrs, defStyle);

    }

    public void setColorFilter(ColorStateList tint) {

        this.tint = tint;

        super.setColorFilter(tint.getColorForState(getDrawableState(), 0));

    }

    public void setTintList(ColorStateList colorList) {

        if (colorList != null) {

            tint = colorList;

            drawableStateChanged();

        }

    }

    @Override

    protected void drawableStateChanged() {

        super.drawableStateChanged();

        if (tint != null && tint.isStateful())

            updateTintColor();

    }

    private void updateTintColor() {

        int color = tint.getColorForState(getDrawableState(), 0);

        setColorFilter(color);

    }

}

attr.xml中声明属性如下:

<resources>
    <declare-styleable name="TintableImageView">
        <attr name="iv_tint" format="reference"/>
    </declare-styleable>
</resources>

代码中使用:

TintableImageView iv_one;
... ...
iv_one.setTintList(ContextCompat.getColorStateList(this,R.color.click_style_color));
iv_two.setImageResource(R.drawable.ic_func);

方案二:
Android 着色器(TintList) 兼容性问题
这里面也介绍了为啥会出现兼容性的问题,同时给出了一个解决方案,其实解决方案一也是从这个原理来解决的,就是updateTintFilter() 方法只是更新了tintFilter的color和mode,并没有触发Drawable的绘制,所以按下时的着色 tint color自然没有显示出来,这两个解决方案都是添加了重绘的方法。但是随着google官方的androidx替代support包之后,方案二中的android.support.v4.graphics.drawable.DrawableCompatLollipop已经不存在了,所以在使用androidx的项目中,方案二是不生效的。

注意:

关于DrawableCompat的wrap(Drawable drawable)方法

通过这个方法获取的Drawable对象,在使用DrawableCompat类中的染色一类的方法,可以在不同的API级别上应用着色。
因此想要着色就先把原先的Drawable对象wrap一下后回去到新的Drawable对象。

上文提到的mutate()方法

Android为了优化系统性能,同一张资源图片生成的Drawable实例在内存中只存在一份,在不使用mutate的情况下,修改任意Drawable都会全局发生变化。
使用mutate,Android系统也没有把Drawable实例又单独拷贝一份,仅仅是单独存放了状态值,很小的一部分数据,Drawable实例在内存中仍然保持1份,因而并不会影响系统的性能。

相关文章:
Android Drawable / DrawableCompat # setTintList( ) 使用时一个值得注意的问题
其实你不懂:Drawable着色(tint)的兼容方案 源码解析

上一篇下一篇

猜你喜欢

热点阅读