现在的位置: 首页 > 移动开发 > 正文

Android中BitmapDrawable的一个bug

2019年07月29日 移动开发 ⁄ 共 2195字 ⁄ 字号 评论关闭

经常使用BitmapDrawable的人要小心了,BitmapDrawable放在StateListDrawable中的时候使用enterFadeDuration/exitFadeDuration可能会导致图片显示不出来的问题。

问题重现:

1. 定义一个StateListDrawable,即用selector定义一个Drawable,并且使用enterFadeDuration:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:enterFadeDuration="@android:integer/config_shortAnimTime">
    <item android:state_enabled="false"
        android:drawable="@drawable/button_disabled" />
    <item android:drawable="@drawable/button_normal" />
</selector>

2. 在Activity中如下使用这个Button:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        LinearLayout view = new LinearLayout(this);
        final Button btn1 = new Button(this);
        final Button btn2 = new Button(this);
        btn1.setText("Button1");
        btn1.setEnabled(false);
        btn1.setBackgroundResource(R.drawable.button);
        btn2.setText("Button2");
        btn2.setBackgroundResource(R.drawable.button);
        view.addView(btn1);
        view.addView(btn2);
        setContentView(view);


        btn1.post(new Runnable() {
            @Override
            public void run() {
                btn1.setEnabled(true);
                StateListDrawable d = (StateListDrawable) btn1.getBackground();
                BitmapDrawable bd = (BitmapDrawable) d.getCurrent();
                Log.e("xxx", "alpha=" + bd.getPaint().getAlpha());
                btn1.setVisibility(View.GONE);
                btn2.invalidate();
            }
        });
    }

从Log中可以看到,btn1当前的Drawable的alpha值并不是255,而是0~255之间的一个数值。而btn2的显示状态显然不对,因为它用Drawable的alpha值跟btn1一样,也不是255。

出问题的界面

如果此时触摸一下btn2,就会让btn2恢复成正常的显示状态。

正常的界面

上面的代码仅仅是为了重现这个问题,一般来说,真实的代码远比这个逻辑复杂,更加不容易发现问题所在。问题往往出现这样的时候:

1)StateListDrawable里使用了BitmapDrawable,并且使用fadeDuration(或直接调用了BitmapDrawable的setAlpha()函数)。

2)某一个使用这个Drawable的元素在做alpha动画的时候被隐藏或其他原因导致动画停止,alpha并没有还原。

3)或者,另一个使用这个Drawable的元素在另一个使用该Drawable的元素动画时被重绘。

其实,这个问题的根本原因是BitmapDrawable的Alpha值是同一个进程中所有相同的Bitmap的BitmapDrawable共用的。同样共用的属性还有:ColorFilter, Gravity, AntiAlias, Filter, Dither, TileModeX, TileModeY等,所以,操作这些属性的时候需要特别考虑是不是有其他的BitmapDrawable还需要使用他们。

另外,NinePatchDrawable不会有这个问题,因为NinePatchDrawable的alpha属性并不共用。

当然,要解决alpha值导致显示有问题也比较容易,那就是在显示btn2的时候,多调用一次jumpDrawablesToCurrentState()即可,或者,在设置btn2的背景的时候,不要用resource id,而是load成drawable,先调用drawable的mutate()函数,再设置成btn2的背景。要真正解决这个问题,只能期望android在BitmapDrawable中对每一个实例不共用上面提到的信息。

抱歉!评论已关闭.