Android Drawable徹底解析(一):Drawable源碼分析(中)

Android Drawable徹底解析(一):Drawable源碼分析(上) Android Drawable徹底解析(一):Drawable源碼分析(中) Android Drawable徹底解析(一):Drawable源碼分析(下)android

呃...我不是故意要湊篇幅寫個什麼上下篇,實在是由於Drawable源碼有點長,一篇寫不下啦O(∩_∩)O~canvas

鑑於源碼通常較長,之後全部源碼分析的部分,英文註釋非必要狀況都再也不保留! ####2:Drawable源碼分析/翻譯 繼續上Drawable源碼:緩存

package android.graphics.drawable;

public abstract class Drawable {
    ****
    略
    ****

    /**
    這個方法很重要,故保留英文註釋!
    調用mutate(),使當前Drawable實例mutable,這個操做不可逆。
    一個mutable的Drawable實例不會和其餘Drawable實例共享它的狀態。
    當你須要修改一個從資源文件加載的Drawable實例時,mutate()方法尤爲有用。
    默認狀況下,全部加載同一資源文件生成的Drawable實例都共享一個通用的狀態,
    若是你修改了其中一個Drawable實例,全部的相關Drawable實例都會發生一樣的變化。

    這個方法在[其實你不懂:Drawable着色(tint)的兼容方案 源碼解析]
    這篇文章裏有過介紹,就是爲了限定Drawable實例的編輯生效範圍僅限於自身。
     * Make this drawable mutable. This operation cannot be reversed. A mutable
     * drawable is guaranteed to not share its state with any other drawable.
     * This is especially useful when you need to modify properties of drawables
     * loaded from resources. By default, all drawables instances loaded from
     * the same resource share a common state; if you modify the state of one
     * instance, all the other instances will receive the same modification.
     *
     * Calling this method on a mutable Drawable will have no effect.
     *
     * @return This drawable.
     * @see ConstantState
     * @see #getConstantState()
     */
    public @NonNull Drawable mutate() {
        return this;
    }

    /**
    被隱匿
     * @hide
     */
    public void clearMutated() {
        // Default implementation is no-op.
    }

    //下面幾個方法介紹了經過不一樣的方式建立Drawable實例:
    //流、XML、文件地址
    public static Drawable createFromStream(InputStream is, String srcName) {
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
        try {
            return createFromResourceStream(null, null, is, srcName);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }
    public static Drawable createFromResourceStream(Resources res, TypedValue value,
            InputStream is, String srcName) {
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
        try {
            return createFromResourceStream(res, value, is, srcName, null);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }
    public static Drawable createFromResourceStream(Resources res, TypedValue value,
            InputStream is, String srcName, BitmapFactory.Options opts) {
        if (is == null) {
            return null;
        }
        Rect pad = new Rect();
        if (opts == null) opts = new BitmapFactory.Options();
        opts.inScreenDensity = Drawable.resolveDensity(res, 0);
        Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
        if (bm != null) {
            byte[] np = bm.getNinePatchChunk();
            if (np == null || !NinePatch.isNinePatchChunk(np)) {
                np = null;
                pad = null;
            }
            final Rect opticalInsets = new Rect();
            bm.getOpticalInsets(opticalInsets);
            return drawableFromBitmap(res, bm, np, pad, opticalInsets, srcName);
        }
        return null;
    }
    public static Drawable createFromXml(Resources r, XmlPullParser parser)
            throws XmlPullParserException, IOException {
        return createFromXml(r, parser, null);
    }
    public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)
            throws XmlPullParserException, IOException {
        AttributeSet attrs = Xml.asAttributeSet(parser);
        int type;
        //noinspection StatementWithEmptyBody
        while ((type=parser.next()) != XmlPullParser.START_TAG
                && type != XmlPullParser.END_DOCUMENT) {
            // Empty loop.
        }
        if (type != XmlPullParser.START_TAG) {
            throw new XmlPullParserException("No start tag found");
        }
        Drawable drawable = createFromXmlInner(r, parser, attrs, theme);
        if (drawable == null) {
            throw new RuntimeException("Unknown initial tag: " + parser.getName());
        }
        return drawable;
    }
    public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
            throws XmlPullParserException, IOException {
        return createFromXmlInner(r, parser, attrs, null);
    }
    public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
            Theme theme) throws XmlPullParserException, IOException {
        return r.getDrawableInflater().inflateFromXml(parser.getName(), parser, attrs, theme);
    }
    public static Drawable createFromPath(String pathName) {
        if (pathName == null) {
            return null;
        }
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
        try {
            Bitmap bm = BitmapFactory.decodeFile(pathName);
            if (bm != null) {
                return drawableFromBitmap(null, bm, null, null, null, pathName);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
        return null;
    }
    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
        inflate(r, parser, attrs, null);
    }

    /**
    從XML文件中加載Drawable實例,Drawable實例接受主題設置的風格
     */
    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.Drawable);
        mVisible = a.getBoolean(R.styleable.Drawable_visible, mVisible);
        a.recycle();
    }
    /**
    從XML文件中加載Drawable實例
     */
    void inflateWithAttributes(@NonNull @SuppressWarnings("unused") Resources r,
            @NonNull @SuppressWarnings("unused") XmlPullParser parser, @NonNull TypedArray attrs,
            @AttrRes int visibleAttr) throws XmlPullParserException, IOException {
        mVisible = attrs.getBoolean(visibleAttr, mVisible);
    }

    /**
    這段註釋很重要,故保留英文註釋!

    ConstantState這個抽象類被用於存儲 多個Drawable實例間 共享的 常量狀態值及數據。
    如從同一個圖片資源建立的多個BitmapDrawable實例,它們將共享
    同一個存儲在它們的ConstantState中的Bitmap。
     * This abstract class is used by {@link Drawable}s to store shared constant state and data
     * between Drawables. {@link BitmapDrawable}s created from the same resource will for instance
     * share a unique bitmap stored in their ConstantState.
     *
    newDrawable能夠運用ConstantState建立一個新的Drawable實例
     * <p>
     * {@link #newDrawable(Resources)} can be used as a factory to create new Drawable instances
     * from this ConstantState.
     * </p>
     *
    Drawable#getConstantState能夠獲取一個Drawable關聯的ConstantState。
    調用Drawable#mutate(),則將爲新建立的Drawable實例單獨關聯一個ConstantState。
     * Use {@link Drawable#getConstantState()} to retrieve the ConstantState of a Drawable. Calling
     * {@link Drawable#mutate()} on a Drawable should typically create a new ConstantState for that
     * Drawable.
     */
    public static abstract class ConstantState {
        /**
        運用ConstantState建立一個新的Drawable實例
         */
        public abstract @NonNull Drawable newDrawable();
        /**
         運用ConstantState建立一個新的Drawable實例
         */
        public @NonNull Drawable newDrawable(@Nullable Resources res) {
            return newDrawable();
        }
        /**
        運用ConstantState建立一個新的Drawable實例
         */
        public @NonNull Drawable newDrawable(@Nullable Resources res,
                @Nullable @SuppressWarnings("unused") Theme theme) {
            return newDrawable(res);
        }
        /**
        返回會影響Drawable實例的一個bit掩碼變化設置
         */
        public abstract @Config int getChangingConfigurations();
        /**
        返回全部的像素數
        public int addAtlasableBitmaps(@NonNull Collection<Bitmap> atlasList) {
            return 0;
        }
        /** @hide */
        protected final boolean isAtlasable(@Nullable Bitmap bitmap) {
            return bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888;
        }
        /**
        返回當前共享狀態是否能夠設置主題
         */
        public boolean canApplyTheme() {
            return false;
        }
    }

    /**
    返回當前Drawable的用於存儲共享狀態值的ConstantState實例
     */
    public @Nullable ConstantState getConstantState() {
        return null;
    }
    //經過Bitmap實例建立Drawable實例
    private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,
            Rect pad, Rect layoutBounds, String srcName) {
        if (np != null) {
            return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName);
        }
        return new BitmapDrawable(res, bm);
    }

    /**
    確保色彩過濾器和當前色彩與色彩模式一致
     */
    @Nullable PorterDuffColorFilter updateTintFilter(@Nullable PorterDuffColorFilter tintFilter,
            @Nullable ColorStateList tint, @Nullable PorterDuff.Mode tintMode) {
        if (tint == null || tintMode == null) {
            return null;
        }
        final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
        if (tintFilter == null) {
            return new PorterDuffColorFilter(color, tintMode);
        }
        tintFilter.setColor(color);
        tintFilter.setMode(tintMode);
        return tintFilter;
    }

    /**
    若是主題有效,則從中獲取樣式屬性,
    若是主題無效,則返回沒有樣式的資源。
     */
    static @NonNull TypedArray obtainAttributes(@NonNull Resources res, @Nullable Theme theme,
            @NonNull AttributeSet set, @NonNull int[] attrs) {
        if (theme == null) {
            return res.obtainAttributes(set, attrs);
        }
        return theme.obtainStyledAttributes(set, attrs, 0, 0);
    }

    /**
    根據 原始像素值,資源單位密度和目標設備單位密度 得到一個float像素值
     */
    static float scaleFromDensity(float pixels, int sourceDensity, int targetDensity) {
        return pixels * targetDensity / sourceDensity;
    }
    static int scaleFromDensity(
            int pixels, int sourceDensity, int targetDensity, boolean isSize) {
        if (pixels == 0 || sourceDensity == targetDensity) {
            return pixels;
        }
        final float result = pixels * targetDensity / (float) sourceDensity;
        if (!isSize) {
            return (int) result;
        }
        final int rounded = Math.round(result);
        if (rounded != 0) {
            return rounded;
        } else if (pixels > 0) {
            return 1;
        } else {
            return -1;
        }
    }

    //獲取單位密度
    static int resolveDensity(@Nullable Resources r, int parentDensity) {
        final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi;
        return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
    }
    static void rethrowAsRuntimeException(@NonNull Exception cause) throws RuntimeException {
        final RuntimeException e = new RuntimeException(cause);
        e.setStackTrace(new StackTraceElement[0]);
        throw e;
    }

    /**
   經過解析tintMode屬性枚舉值得到一個PorterDuff.Mode
   
   被隱匿
     * @hide
     */
    public static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
        switch (value) {
            case 3: return Mode.SRC_OVER;
            case 5: return Mode.SRC_IN;
            case 9: return Mode.SRC_ATOP;
            case 14: return Mode.MULTIPLY;
            case 15: return Mode.SCREEN;
            case 16: return Mode.ADD;
            default: return defaultMode;
        }
    }
}

Drawable類自己源碼先寫到這兒,接着往下看。app

####3:Drawable繪製流程 看過Drawable源碼,其實咱們仍是不清楚: Drawable實例究竟是如何被繪製到屏幕上面? Drawable源碼中的那些方法又是何時被誰調用的?ide

咱們回想一下,使用Drawable最一般的步驟: 經過Resource獲取Drawable實例 將獲取的Drawable實例當作背景設置給View或者做爲ImageView的src進行顯示:oop

下面就逐步分析理解Drawable的繪製流程。 ######3.1:經過Resource獲取Drawable實例 最經常使用寫法:getResources().getDrawable(int id),看下關鍵代碼:源碼分析

public class Resources {
    ****
    public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
        final Drawable d = getDrawable(id, null);
        *****
        return d;
    }
    public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
            throws NotFoundException {
        final TypedValue value = obtainTempTypedValue();
        try {
            final ResourcesImpl impl = mResourcesImpl;
            //
            impl.getValue(id, value, true);
            //將獲取到的Drawable實例返回
            return impl.loadDrawable(this, value, id, theme, true);
        } ****
    }
}
一路追蹤下去:
public class ResourcesImpl {
    //Resource實例,TypedValue,資源ID,Theme實例,true
    @Nullable
    Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,
            boolean useCache) throws NotFoundException {
        try {
            ********
            //是否屬於ColorDrawable
            final boolean isColorDrawable;
            //Drawable緩存
            final DrawableCache caches;
            final long key;
            //判斷資源是否屬於顏色資源
            if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
                    && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
                isColorDrawable = true;
                caches = mColorDrawableCache;
                key = value.data;
            } else {
                //若是是加載一張普通的圖片,不屬於顏色資源
                isColorDrawable = false;
                caches = mDrawableCache;
                key = (((long) value.assetCookie) << 32) | value.data;
            }
            if (!mPreloading && useCache) {
                final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
                if (cachedDrawable != null) {
                    return cachedDrawable;
                }
            }
            //若是在Drawable緩存裏面未找到資源ID對應的Drawable實例,繼續
            final Drawable.ConstantState cs;
            if (isColorDrawable) {
                cs = sPreloadedColorDrawables.get(key);
            } else {
                //若是不屬於顏色資源,則從sPreloadedDrawables中查詢
                //sPreloadedDrawables只有在執行cacheDrawable方法時
                //纔會進行數據添加:而第一次加載圖片時候還未執行cacheDrawable
                //因此此時cs = null.
                cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
            }
            Drawable dr;
            if (cs != null) {
                dr = cs.newDrawable(wrapper);
            } else if (isColorDrawable) {
                dr = new ColorDrawable(value.data);
            } else {
                //當第一次加載圖片資源時候,cs=null且不屬於顏色資源,
                //實際是經過loadDrawableForCookie來獲取Drawable實例
                dr = loadDrawableForCookie(wrapper, value, id, null);
            }
            *********
    }
    private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,
            Resources.Theme theme) {
        ****
        final String file = value.string.toString();
        ****
        final Drawable dr;
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
        try {
            if (file.endsWith(".xml")) {
                //若是是從xml文件加載Drawable
                final XmlResourceParser rp = loadXmlResourceParser(
                        file, id, value.assetCookie, "drawable");
                dr = Drawable.createFromXml(wrapper, rp, theme);
                rp.close();
            } else {
                //從圖片資源加載Drawable,執行Drawable.createFromResourceStream
                //獲取Drawable實例
                final InputStream is = mAssets.openNonAsset(
                        value.assetCookie, file, AssetManager.ACCESS_STREAMING);
                dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
                is.close();
            }
        } catch (Exception e) {
            ****
        }
        ****
        return dr;
    }
}
一路追蹤下去:
public abstract class Drawable {
    public static Drawable createFromResourceStream(Resources res, TypedValue value,
            InputStream is, String srcName, BitmapFactory.Options opts) {
        ****
            return drawableFromBitmap(res, bm, np, pad, opticalInsets, srcName);
        ****
    }
    private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,
            Rect pad, Rect layoutBounds, String srcName) {
        if (np != null) {
            //若是加載的圖片資源是.9 PNG,返回NinePatchDrawable實例
            return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName);
        }
        //對於普通圖片資源,返回BitmapDrawable
        return new BitmapDrawable(res, bm);
    }
}

因而可知,經過Resource實例加載一張資源圖片: .9圖返回1個NinePatchDrawable實例; 普通圖片返回1個BitmapDrawable實例。 ######3.2:將獲取的Drawable實例當作背景設置給View 最經常使用寫法:targetView.setBackgroundDrawable(Drawable bg), 一樣看一下關鍵代碼佈局

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    ****
    public void setBackgroundDrawable(Drawable background) {
        ****
        if (background == mBackground) {
            //若是當前背景和background相同,直接return
            return;
        }
        boolean requestLayout = false;
        mBackgroundResource = 0;
        if (mBackground != null) {
            if (isAttachedToWindow()) {
                //若是當前View實例已經被繪製到屏幕上,則首先取消
                //該View實例原始背景Drawable的動畫
                mBackground.setVisible(false, false);
            }
            //移除該View實例原始背景Drawable的動畫監聽接口
            mBackground.setCallback(null);
            //取消該View實例原始背景Drawable的全部事件
            unscheduleDrawable(mBackground);
        }
        if (background != null) {
            ****
            //設置background的佈局方向和View實例一致,
            //Drawable.setLayoutDirection見上一篇文章
            background.setLayoutDirection(getLayoutDirection());
            if (background.getPadding(padding)) {
                //若是Drawable實例background有padding
                resetResolvedPaddingInternal();
                switch (background.getLayoutDirection()) {
                    case LAYOUT_DIRECTION_RTL:
                        //佈局方向從右至左
                        mUserPaddingLeftInitial = padding.right;
                        mUserPaddingRightInitial = padding.left;
                        internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);
                        break;
                    case LAYOUT_DIRECTION_LTR:
                    default:
                        //佈局方向從左至右
                        mUserPaddingLeftInitial = padding.left;
                        mUserPaddingRightInitial = padding.right;
                        //internalSetPadding會將四個參數值和View實例的padding進行比對,若不一樣則會從新佈局+重建View的外部輪廓
                        internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);
                }
                mLeftPaddingDefined = false;
                mRightPaddingDefined = false;
            }
            if (mBackground == null
                    || mBackground.getMinimumHeight() != background.getMinimumHeight()
                    || mBackground.getMinimumWidth() != background.getMinimumWidth()) {
                requestLayout = true;
            }
            //設置當前View實例的背景爲傳入的Drawable實例 background
            mBackground = background;
            if (background.isStateful()) {
                //若是background會根據狀態值變動外觀,則設置其狀態爲
                //當前View實例的state
                background.setState(getDrawableState());
            }
            if (isAttachedToWindow()) {
                //若是當前View實例已經被繪製到屏幕上
                //且實例和實例的父控件及遞歸得到的根佈局都處於可見狀態,
                //則設置background開啓動畫效果
                background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
            }
            applyBackgroundTint();
            //設置background動畫接口監聽爲View實例自己(View實現了 Drawable.Callback):
            //public class View implements Drawable.Callback
            background.setCallback(this);
            if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
                mPrivateFlags &= ~PFLAG_SKIP_DRAW;
                //須要從新佈局
                requestLayout = true;
            }
        } else {
            mBackground = null;
            if ((mViewFlags & WILL_NOT_DRAW) != 0
                    && (mForegroundInfo == null || mForegroundInfo.mDrawable == null)) {
                mPrivateFlags |= PFLAG_SKIP_DRAW;
            }
            requestLayout = true;
        }
        computeOpaqueFlags();
        if (requestLayout) {
            //從新佈局
            requestLayout();
        }
        mBackgroundSizeChanged = true;
        //重繪View實例
        invalidate(true);
        //重建View實例的外部輪廓
        invalidateOutline();
    }
}

因而可知,setBackgroundDrawable方法,調用了Drawable實例的一系列方法,最終引起了View實例的從新佈局(requestLayout())重繪(invalidate(true))及重建View實例的外部輪廓(invalidateOutline())。 invalidate會觸發draw方法,咱們繼續看View.draw方法的關鍵代碼:動畫

public void draw(Canvas canvas) {
        ****
        /*
        翻譯可能不甚準確,歡迎英語好的同窗留言指正O(∩_∩)O~

        Draw方法會執行如下幾個步驟,且必須按順序執行:
        1:繪製View實例的背景
        2:若有必要,保存畫布圖層以備褪色
        3:繪製View實例的內容
        4:繪製View實例的中的子控件
        5:若有必要,繪製邊緣並恢復圖層
        6:繪製滾動條
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */
        //從步驟順序上看,和Drawable相關的就是第1步,只看第1步代碼
        // Step 1, draw the background, if needed
        int saveCount;
        if (!dirtyOpaque) {
            //繪製背景
            drawBackground(canvas);
        }
        ****
    }

    一路追蹤下去:

    private void drawBackground(Canvas canvas) {
        //mBackground就是以前setBackgroundDrawable傳入的Drawable實例
        final Drawable background = mBackground;
        if (background == null) {
            return;
        }
        //設置background繪製範圍爲View實例的所在範圍
        setBackgroundBounds();
        ****
        final int scrollX = mScrollX;
        final int scrollY = mScrollY;
        if ((scrollX | scrollY) == 0) {
            //最終調用Drawable.draw(Canvas canvas)將Drawable實例
            //繪製到屏幕上
            background.draw(canvas);
        } else {
            canvas.translate(scrollX, scrollY);
            //最終調用Drawable.draw(Canvas canvas)將Drawable實例
            //繪製到屏幕上
            background.draw(canvas);
            canvas.translate(-scrollX, -scrollY);
        }
    }

因而可知,View實例的背景Drawable實例最終仍是調用自身的Drawable.draw(@NonNull Canvas canvas)方法繪製到屏幕上。this

繼續查看Drawable.draw方法:

public abstract class Drawable {
    //Drawable中的draw是一個抽象方法,應該是爲了衆多的
    //子類Drawable擁有自定義的繪製邏輯進行重寫
    public abstract void draw(@NonNull Canvas canvas);
}

在分析Resource.getDrawable時候已經知道,
對於普通的圖片資源,獲取到的是一個BitmapDrawable實例,
咱們就來看看BitmapDrawable的draw具體的繪製邏輯:

public class BitmapDrawable extends Drawable {
    @Override
    public void draw(Canvas canvas) {
        ****
        if (shader == null) {
            ****
            //最終調用了Canvas.drawBitmap方法,將Drawable實例中的bitmap繪製到View實例關聯的畫布上
            canvas.drawBitmap(bitmap, null, mDstRect, paint);
            if (needMirroring) {
                canvas.restore();
            }
        } ****
    }
}

至此,將獲取的Drawable實例當作背景設置給View,和Drawable相關的一系列邏輯就分析完了,大體以下:

  • 1:setBackgroundDrawable方法,調用了Drawable的一系列方法,設置了Drawable實例一系列屬性值,最終引起了View實例的從新佈局(requestLayout()),重繪(invalidate(true))及重建View實例的外部輪廓(invalidateOutline())
  • 2:在View實例重繪過程的第一步,將獲得的Drawable實例(View實例的背景)繪製到屏幕上,實質是調用了Drawable.draw(@NonNull Canvas canvas)
  • 3:Drawable.draw自己是個抽象方法,繪製具體邏輯由其子類實現。 咱們以以前得到的BitmapDrawable爲例進行分析: 最終調用了Canvas.drawBitmap方法,將Drawable實例中的bitmap繪製到View實例關聯的畫布上

Drawable繪製流程今天先寫到這兒,如今是2017/03/07 20:41,加班碼字到如今有點累了,明後天繼續把ImageView和Drawable關聯的部分寫完吧!

未完待續...

以上就是我的分析的一點結果,如有錯誤,請各位同窗留言告知!

That's all !

相關文章
相關標籤/搜索