從custom Drawable看invalidateSelf()

一、invalidateSelf()

參考(https://www.zybuluo.com/linux1s1s/note/93075)
我自己也嘗試著看源碼。不過不同的是在Drawable里面自己調(diào)用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。如果參數(shù)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.繼續(xù)從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實現(xiàn)了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沒有實現(xiàn)任何與用戶的互動,而完全是交給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內(nèi)部或者其對象調(diào)用invalidateSelf()的時候,便以Drawable對象自身為參數(shù),讓類ImageView來調(diào)用實現(xiàn)了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是如何實現(xiàn)的invalidateDrawable(Drawable who)
如果一切“正常”,即參數(shù)dr等于成員變量mDrawable且不為空,那么最終會調(diào)用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)。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容