[关闭]
@act262 2017-04-08T03:17:18.000000Z 字数 1654 阅读 1903

由修改Drawable alpha引发问题的思考

Android


现象:

一个项目中很多地方公用了一个颜色值,然后有个地方会对这个控件的背景动态修改了,会出现其他引用这个颜色值的地方被跟随变化了,实际上这是不对的.

重现步骤:

  1. <color name="bg_toolbar">#222C57</color>
  1. <android.support.v7.widget.Toolbar
  2. android:id="@+id/toolbar"
  3. android:layout_width="match_parent"
  4. android:layout_height="@54dp"
  5. android:background="@color/bg_toolbar"
  6. android:minHeight="?attr/actionBarSize" />
  1. /**
  2. * 设置背景色的透明度
  3. *
  4. * @param alpha 0~255,0完全透明,255完全不透明
  5. */
  6. public void setBackgroundAlpha(@IntRange(from = 0, to = 255) int alpha) {
  7. if (currentAlpha != alpha) {
  8. getBackground().setAlpha(alpha);
  9. currentAlpha = alpha;
  10. }
  11. }
  1. setBackground(getBackground().mutate());

根源分析

从一个资源加载的地方找起,Resources

  1. //
  2. Drawable loadDrawable(TypedValue value, int id, Theme theme)
  3. {
  4. // 1.从缓存中找资源
  5. // 2.如果缓存中找到直接返回
  6. // 3.缓存中没有则是new一个,然后缓存下来
  7. }
  8. private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches, Theme theme, boolean usesTheme, long key, Drawable dr){
  9. }

这里会在加载时根据一定的情况从缓存中读写,所以没有一样的Drawable,每个Drawable都是唯一的,默认共享ConstantState(享元模式).

查看Drawable相关源码,
ConstantState所有相同一份资源都会共享其ConstantState,所以修改一个地方引用的Drawable就相当于修改了其他地方引用,使用mutate方法后生成一份新的ConstantState不再使用共享的ConstantState,即不会影响到其他的引用.

eg:使用color作为background对应的Drawable是ColorDrawable

  1. public Drawable mutate() {
  2. if (!mMutated && super.mutate() == this) {
  3. // 说明重新赋值了一个ConstantState给他
  4. mColorState = new ColorState(mColorState);
  5. mMutated = true;
  6. }
  7. return this;
  8. }

调用mutate后再修改其属性就不会影响到公共的属性状态了.


总结:

在使用Drawable系列的时候要特别注意修改Drawable的属性引起其他引用这个Drawable的属性同时变化,尤其是我们常用的ColorDrawable,BitmapDrawable.

同理在ImageView中设置透明度setAlpha方法也会有这样的情况.
还有设置各种tint时也要注意.

参考: android 里面的 Drawable 和 ConstantState

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注