Android LayerDrawable 和 Drawable.Callback

转载 2015年11月20日 22:39:22


目录(?)[+]

  1. Android LayerDrawable 和 DrawableCallback
    1. Callback调用链
    2. View改变背景时移除原背景Callback
    3. 什么情况下会出现Bug

Android LayerDrawable 和 Drawable.Callback

LayerDrawable是一个特殊的Drawable,它内部保持着一个Drawable数组,其中每一个Drawable都是视图中的一层。如果你不了解LayerDrawable的机制,当程序出了问题后是很难去找到bug在哪里的。我发这些文章就是为了分享在使用LayerDrawableDrawable.Callback时可能出现的一个bug。

Callback调用链

LayerDrawable中,每层视图(Drawable)都会将LayerDrawable注册为它的Drawable.Callback。这允许Drawable能够在需要重绘自己的时候告知LayerDrawable重绘它。我们可以在下面这个Callback.invalidateSelf()函数中看到是由注册callback端(在此处为LayerDrawable)来执行invalidateDrawable(Drawable drawable)的。

<code class="language-java hljs  has-numbering" sizcache="23" sizset="30"><span style="font-size:18px;"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">invalidateSelf</span>() {
    <span class="hljs-comment">/* 获取注册的Callback实例,如果无则返回null。 */</span>
    <span class="hljs-keyword">final</span> Callback callback = getCallback();
    <span class="hljs-keyword">if</span> (callback != <span class="hljs-keyword">null</span>) {
        callback.invalidateDrawable(<span class="hljs-keyword">this</span>);
    }
}</span></code><ul class="pre-numbering" style="ZOOM: 1; FILTER:  "><li><span style="font-size:18px;">1</span></li></ul>

我们知道View是实现了Drawable.Callback接口的,所以当图片需要重绘的时候就能够告知View。如果我们把View的背景图片设置成了LayerDrawable,在Drawable需要更新的时候callback的调用将有一个传递的过程,首先会调用注册的LayerDrawableinvalidateDrawable(Drawable drawable)方法,LayerDrawable又会调用ViewinvalidateDrawable(Drawable drawable)方法。如下图所示:

Alt text

View改变背景时移除原背景Callback

ViewsetBackgroundDrawable(Drawable background)中有这么一段代码:

<code class="language-java hljs  has-numbering" sizcache="23" sizset="38"><span style="font-size:18px;">    <span class="hljs-keyword">if</span> (mBackground != <span class="hljs-keyword">null</span>) {
        mBackground.setCallback(<span class="hljs-keyword">null</span>);
        unscheduleDrawable(mBackground);
    }

    …
    <span class="hljs-keyword">if</span> (background != <span class="hljs-keyword">null</span>) {
        background.setCallback(<span class="hljs-keyword">this</span>);
    }</span></code><ul class="pre-numbering" style="ZOOM: 1; FILTER:  "><li><span style="font-size:18px;">1</span></li></ul>

我们可以看出:当View改变背景时将会无条件将原背景(如果原背景是Drawable的话)的Drawable.Callback设置为null

什么情况下会出现Bug?

有了上面这些知识,我们可以通过下面这个步骤产生一个Bug:

  1. DrawableA 设置成ViewV的背景。现在A的callback指向V
  2. 将A设置成LayerDrawable L中的一层。现在A的callback指向L
  3. 现在为V设置另一个背景,V会把原背景(A)的callback强制设置成null,破坏了A与L之间的联系。
  4. BUG出现了:更新DrawableA不会让L更新了。

解决方法就是在更新V的背景之后再创造LayerDrawableL。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();
}

相关文章推荐

【译】Android LayerDrawable 和 Drawable.Callback

Android LayerDrawable 和 Drawable.Callback 原文链接 : Android LayerDrawable and Drawable.Callback ...

Android-drawable资源-LayerDrawable

res/drawable中的layer-list标签。对应Android中的LayerDrawable;中可以填加。在layer-list中声明的drawable资源,会按照列表的顺序绘制,最后一个绘...

初学Android,使用Drawable资源之使用LayerDrawable资源(十四)

From: http://blog.csdn.net/lee576/article/details/7825930 LayerDrawable,系统将会按这些Drawable对象的数组顺序来绘制...

Android开发艺术探索<Drawable系列之三LayerDrawable>

LayerDrawable对应的XML标签是,表示一种把不同的Drawable摆放在不同层次显示一种叠加效果。一个layer-list可以包含多个标签,每一个代表显示一层Drawable。一个的结构比...

Drawable解析3——LayerDrawable、TransitionDrawable、NinePatchDrawable和LevelListDrawabl

1、前两节我们已经说了8个Drawable了,本节继续,第一个,LayerDrawable,表示层图形对象。LayerDrawable用于管理一组drawable,每个drawable处于不同的层,当...

使用Drawable资源之使用LayerDrawable资源

LayerDrawable,系统将会按这些Drawable对象的数组顺序来绘制它们,索引最大的Drawable对象将会被绘制在最上面 定义LayerDrawable对象的XML文件的根元素为,该...

Drawable资源——LayerDrawable 图层列表

Drawable资源——LayerDrawable 图层列表 1,认识   它表示一种层次化的Drawable集合,通过将不同的Drawable放置在不同的层上面从而达到一种叠加后的效果。系统将会...

LayerDrawable 层叠Drawable使用详解

LayerDrawable使用 layer-list xml文件编写方式 layer-list输入框LayerDrawable使用LayerDrawable对应的xml标签为,它表示一种层次化的D...

Drawable资源---LayerDrawable资源

实例,改变拖动条背景,和完成进度,进度条颜色 复制两张图片到drawable-mdpi文件夹下,一张是背景图,一张是拖动之后图片 然后在该文件夹下定义 my_bar.xml  ...

Android之android.graphics.drawable.Drawable.Callback回调接口

[java] view plaincopy /*如果你想实现一个扩展子Drawable的动画drawable,那么你可以通过setCallBack(android.graphics.drawable...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)