从custom Drawable看invalidateSelf()

一、invalidateSelf()

参考(https://www.zybuluo.com/linux1s1s/note/93075)
我自己也尝试着看源码。不过不同的是在Drawable里面自己调用invalidateSelf(),而不是在view里面开始。
1.一个继承ImageView的自定义View,重写了setImageDrawble(Drawable drawable)

@Override
public void setImageDrawable(Drawable drawable) {
    Tool.LI("WeatherAnimView setImageDrawable");
    Drawable d = getDrawable();
    if (d != null && d.equals(drawable)) { 
       return;
    }
    if (d != null && d instanceof WeatherDrawable) {
        ((WeatherDrawable) d).stopAnimation();
    } 
   super.setImageDrawable(drawable); // start from here
   if (drawable != null && drawable instanceof WeatherDrawable && isShown()) {
        ((WeatherDrawable) drawable).startAnimation();
    }
}

2.从ImageView的方法setImageDrawable(drawable)开始找ViewDrawable的关系

/**
 * Sets a drawable as the content of this ImageView.
 *
 * @param drawable the Drawable to set, or {@code null}
 * to clear the content
 */
public void setImageDrawable(@Nullable Drawable drawable) {
    if (mDrawable != drawable) {
        mResource = 0;
        mUri = null;
        final int oldWidth = mDrawableWidth;
        final int oldHeight = mDrawableHeight;
        updateDrawable(drawable);
        if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
            requestLayout();
        }
        invalidate();
    }
}

3.先从updateDrawable(drawable)看起。起初被mDrawable骗了,从命名看这是ImageView的成员变量,updateDrawble(Drawable d)首先对其做了一些检查不管。接着代码就很明了,把传进来的Drawable对象赋给成员变量mDrawable。如果参数d不为空的话,那么设置d的Callback

private void updateDrawable(Drawable d) {
    if (d != mRecycleableBitmapDrawable && mRecycleableBitmapDrawable != null) {
        mRecycleableBitmapDrawable.setBitmap(null);
    }
    if (mDrawable != null) {
        mDrawable.setCallback(null);// 
        unscheduleDrawable(mDrawable);
    }
    mDrawable = d;
    if (d != null) {
        d.setCallback(this);
        d.setLayoutDirection(getLayoutDirection());
        if (d.isStateful()) {
            d.setState(getDrawableState());
        }
        d.setVisible(getVisibility() == VISIBLE, true);
        d.setLevel(mLevel);
        mDrawableWidth = d.getIntrinsicWidth();
        mDrawableHeight = d.getIntrinsicHeight();
        applyImageTint();
        applyColorMod();
        configureBounds();
    } else {
        mDrawableWidth = mDrawableHeight = -1;
    }
}

4.继续从d.setCallback(this);看下去,以View对象新建一个弱引用new WeakReference<Callback>(cb)赋给Drawable对象的d的成员变量mCallback

/**
 * Bind a {@link Callback} object to this Drawable.  Required for clients
 * that want to support animated drawables.
 *
 * @param cb The client's Callback implementation.
 *
 * @see #getCallback()
 */public final void setCallback(Callback cb) {
    mCallback = new WeakReference<Callback>(cb);
}

正如(https://www.zybuluo.com/linux1s1s/note/93075) 所说的,类ImageView的父类View实现了Drawable.Callback的接口。

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
    private static final boolean DBG = false;
    /**
     * The logging tag used by this class with android.util.Log.
     */    protected static final String VIEW_LOG_TAG = "View";
    // ...此处省略了余下代码

查看源码也可以看到Drawable类定义了该接口

public abstract class Drawable {

    // 此处省略了无关代码

    private WeakReference<Callback> mCallback = null;// <---- HERE!

    // 此间省略了无关代码

    /**
     * Implement this interface if you want to create an animated drawable that
     * extends {@link android.graphics.drawable.Drawable Drawable}.
     * Upon retrieving a drawable, use
     * {@link Drawable#setCallback(android.graphics.drawable.Drawable.Callback)}
     * to supply your implementation of the interface to the drawable; it uses
     * this interface to schedule and execute animation changes.
     */public static interface Callback {
        // 此处省略了注释
        public void invalidateDrawable(Drawable who);

        // 此处省略了注释
        public void scheduleDrawable(Drawable who, Runnable what, long when);

        // 此处省略了注释
        public void unscheduleDrawable(Drawable who, Runnable what);
}

Drawable没有实现任何与用户的互动,而完全是交给View,诚如Google文档(https://developer.android.com/reference/android/graphics/drawable/Drawable.html) 描述的,ViewDrawable各司其职。

A Drawable is a general abstraction for "something that can be drawn." Most often you will deal with Drawable as the type of resource retrieved for drawing things to the screen; the Drawable class provides a generic API for dealing with an underlying visual resource that may take a variety of forms. Unlike a View
, a Drawable does not have any facility to receive events or otherwise interact with the user.

二、Drawable Call

Google官方文档描述DrawableinvalidateSelf()如下:

invalidateSelf()
Use the current Drawable.Callback
implementation to have this Drawable redrawn.

1.查看DrawableinvalidateSelf()源码如下。
所以当Drawable内部或者其对象调用invalidateSelf()的时候,便以Drawable对象自身为参数,让类ImageView来调用实现了Drawable.CallbackinvalidateDrawable(Drawable who)方法。

/**
 * Use the current {@link Callback} implementation to have this Drawable
 * redrawn.  Does nothing if there is no Callback attached to the
 * Drawable.
 *
 * @see Callback#invalidateDrawable
 * @see #getCallback()
 * @see #setCallback(android.graphics.drawable.Drawable.Callback)
 */
public void invalidateSelf() {
    final Callback callback = getCallback();
    if (callback != null) {
        callback.invalidateDrawable(this);
    }
}

2.查看View是如何实现的invalidateDrawable(Drawable who)
如果一切“正常”,即参数dr等于成员变量mDrawable且不为空,那么最终会调用invalidate()方法。

@Override
public void invalidateDrawable(Drawable dr) {
    if (dr == mDrawable) {
        if (dr != null) {
            // update cached drawable dimensions if they've changed
            final int w = dr.getIntrinsicWidth();
            final int h = dr.getIntrinsicHeight();
            if (w != mDrawableWidth || h != mDrawableHeight) {
                mDrawableWidth = w;
                mDrawableHeight = h;
            }
        }
        /* we invalidate the whole view in this case because it's very
         * hard to know where the drawable actually is. This is made
         * complicated because of the offsets and transformations that
         * can be applied. In theory we could get the drawable's bounds
         * and run them through the transformation and offsets, but this
         * is probably not worth the effort.
         */
        invalidate();
    } else {
        super.invalidateDrawable(dr);
    }
}

3.最后invalidate()可以参考(https://www.zybuluo.com/linux1s1s/note/93075)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容