LayerDrawable
是一个特殊的Drawable
,它内部保持着一个Drawable
数组,其中每一个Drawable
都是视图中的一层。如果你不了解LayerDrawable
的机制,当程序出了问题后是很难去找到bug在哪里的。我发这些文章就是为了分享在使用LayerDrawable
与Drawable.Callback
时可能出现的一个bug。
在LayerDrawable
中,每层视图(Drawable
)都会将LayerDrawable
注册为它的Drawable.Callback
。这允许Drawable
能够在需要重绘自己的时候告知LayerDrawable
重绘它。我们可以在下面这个Callback.invalidateSelf()
函数中看到是由注册callback端(在此处为LayerDrawable
)来执行invalidateDrawable(Drawable drawable)
的。
public void invalidateSelf() {
/* 获取注册的Callback实例,如果无则返回null。 */
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
我们知道View
是实现了Drawable.Callback
接口的,所以当图片需要重绘的时候就能够告知View
。如果我们把View
的背景图片设置成了LayerDrawable
,在Drawable
需要更新的时候callback的调用将有一个传递的过程,首先会调用注册的LayerDrawable
的invalidateDrawable(Drawable drawable)
方法,LayerDrawable
又会调用View
的invalidateDrawable(Drawable drawable)
方法。如下图所示:
在View
的setBackgroundDrawable(Drawable background)
中有这么一段代码:
if (mBackground != null) {
mBackground.setCallback(null);
unscheduleDrawable(mBackground);
}
…
if (background != null) {
background.setCallback(this);
}
我们可以看出:当View
改变背景时将会无条件将原背景(如果原背景是Drawable的话)的Drawable.Callback
设置为null
。
有了上面这些知识,我们可以通过下面这个步骤产生一个Bug:
- 把
Drawable
A 设置成View
V的背景。现在A的callback指向V。- 将A设置成
LayerDrawable
L中的一层。现在A的callback指向L。- 现在为V设置另一个背景,V会把原背景(A)的callback强制设置成null,破坏了A与L之间的联系。
- BUG出现了:更新
Drawable
A不会让L更新了。
解决方法就是在更新V的背景之后再创造LayerDrawable
L。Bug发生与解决的例子可以在这里下载。
为了方便看官,我也贴了一部分关键代码到这边来,你可以通过注释理解这段代码。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn1 = (Button) findViewById(R.id.button1);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1. 将 launcherIconDrawable.callback 赋值给 actionBar
actionBar.setBackgroundDrawable(launcherIconDrawable);
animateActionBarWorking();
}
});
Button btn2 = (Button) findViewById(R.id.button2);
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1. 将 launcherIconDrawable.callback 赋值给 actionBar
actionBar.setBackgroundDrawable(launcherIconDrawable);
animateActionBarNotWorking();
}
});
actionBar = getSupportActionBar();
launcherIconDrawable = getResources().getDrawable(R.drawable.launcher_repeat);
colorLayer = new ColorDrawable(Color.rgb(0, 255, 0));
actionBar.setBackgroundDrawable(colorLayer);
}
/* 这个函数运行后ActionBar不会得到更新。 */
private void animateActionBarNotWorking() {
Drawable[] layers = new Drawable[] { colorLayer, launcherIconDrawable };
LayerDrawable layerDrawable = new LayerDrawable(layers);
actionBar.setBackgroundDrawable(layerDrawable);
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 255);
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 4. Updates launcherIconDrawable will not trigger action bar background to update
// as launcherIconDrawable.callback is null
launcherIconDrawable.setAlpha((Integer) animation.getAnimatedValue());
}
});
valueAnimator.start();
}
/* 由于先移除了launcherIconDrawable与ActionBar的联系,这个函数运行后会让ActionBar得到更新。
private void animateActionBarWorking() {
actionBar.setBackgroundDrawable(null);
animateActionBarNotWorking();
}
参考文章:
扫一扫
在手机上阅读