Android的ImageView的繪制原理

首先,以我們在xml文件中使用<ImageView>標(biāo)簽使用ImageView為例,然后在Activity中設(shè)置圖片的過程,分析ImageView的顯示圖片發(fā)生了什么梭姓。

ImageView的構(gòu)造函數(shù)

 public ImageView(Context context) {
        super(context);
        初始化一些變量和配置
        initImageView();
    }

    public ImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        這里也調(diào)用到了這個函數(shù)
        initImageView();

        // ImageView is not important by default, unless app developer overrode attribute.
        if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
            setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO);
        }

        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);
        xml中設(shè)置的圖片源
        final Drawable d = a.getDrawable(R.styleable.ImageView_src);
        if (d != null) {
            setImageDrawable(d);
        }
        基線對齊
        mBaselineAlignBottom = a.getBoolean(R.styleable.ImageView_baselineAlignBottom, false);
        mBaseline = a.getDimensionPixelSize(R.styleable.ImageView_baseline, -1);
        是否調(diào)整view邊界
        setAdjustViewBounds(a.getBoolean(R.styleable.ImageView_adjustViewBounds, false));
        設(shè)置View的最大寬
        setMaxWidth(a.getDimensionPixelSize(R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
        設(shè)置View的最大高
        setMaxHeight(a.getDimensionPixelSize(R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
        xml中的縮放類型
        final int index = a.getInt(R.styleable.ImageView_scaleType, -1);
        if (index >= 0) {
            setScaleType(sScaleTypeArray[index]);
        }

        if (a.hasValue(R.styleable.ImageView_tint)) {
            mDrawableTintList = a.getColorStateList(R.styleable.ImageView_tint);
            mHasDrawableTint = true;

            // Prior to L, this attribute would always set a color filter with
            // blending mode SRC_ATOP. Preserve that default behavior.
            mDrawableTintMode = PorterDuff.Mode.SRC_ATOP;
            mHasDrawableTintMode = true;
        }

        if (a.hasValue(R.styleable.ImageView_tintMode)) {
            mDrawableTintMode = Drawable.parseTintMode(a.getInt(
                    R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
            mHasDrawableTintMode = true;
        }

        applyImageTint();
        圖片透明度
        final int alpha = a.getInt(R.styleable.ImageView_drawableAlpha, 255);
        if (alpha != 255) {
            setImageAlpha(alpha);
        }

        mCropToPadding = a.getBoolean(
                R.styleable.ImageView_cropToPadding, false);

        a.recycle();

        //need inflate syntax/reader for matrix
    }

在初始化時都用到了 initImageView();方法,看看著了方法做了什么嫩码;

 private void initImageView() {
        初始化了默認(rèn)矩陣
        mMatrix = new Matrix();
        初始化了默認(rèn)縮放類型是fit_center
        mScaleType = ScaleType.FIT_CENTER;

        if (!sCompatDone) {
            final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
            sCompatAdjustViewBounds = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
            sCompatUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M;
            sCompatDrawableVisibilityDispatch = targetSdkVersion < Build.VERSION_CODES.N;
            sCompatDone = true;
        }
    }

ImageView 的幾個種設(shè)置圖片的方式

我們知道誉尖,對于Imageview顯示圖片,常用的有一下幾種方式

  • imaegView.setImageBitmap();
  • imaegView.setImageResource();
  • imaegView.setImageDrawable();
  • imaegView.setImageURI();

setImageBitmap()

 /**
     * Sets a Bitmap as the content of this ImageView.
     *
     * @param bm The bitmap to set
     */
    @android.view.RemotableViewMethod
    public void setImageBitmap(Bitmap bm) {
        // Hacky fix to force setImageDrawable to do a full setImageDrawable
        // instead of doing an object reference comparison
        mDrawable = null;
        if (mRecycleableBitmapDrawable == null) {
            mRecycleableBitmapDrawable = new BitmapDrawable(mContext.getResources(), bm);
        } else {
            mRecycleableBitmapDrawable.setBitmap(bm);
        }
        setImageDrawable(mRecycleableBitmapDrawable);
    }

通過源碼可知铸题,setImageBitmap()最終是把傳遞過來的bitmap轉(zhuǎn)換成一個Drawable對象铡恕,然后調(diào)用的setImageDrawable()方法,那setImageDrawable()方法又是干嘛的啊
setImageDrawable

public void setImageDrawable(Drawable drawable) {
        if (mDrawable != drawable) {
            資源置0
            mResource = 0;
            uri置null
            mUri = null;
            原來drawable的寬高
            final int oldWidth = mDrawableWidth;
            final int oldHeight = mDrawableHeight;
            更新此ImageView顯示的drawable,此方法重要丢间,幾個設(shè)置圖像的方法都調(diào)用
            updateDrawable(drawable);
            if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
               重新布局
                requestLayout();
            }
            重新繪制
            invalidate();
        }
    }

由源碼可知探熔,

  • 全局變量mResource設(shè)置為0,
  • 全局變量mUri置為null烘挫,
  • 把傳遞過來的drawable對象再傳遞到updateDrawable()法中

setImageResource()

public void setImageResource(int resId) {
        final int oldWidth = mDrawableWidth;
        final int oldHeight = mDrawableHeight;
        updateDrawable(null);
        mResource = resId;
        mUri = null;
        resolveUri();
        if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
            requestLayout();
        }
        invalidate();
    }

由源碼可知:

  • 沒有Drawable對象诀艰,所以 updateDrawable()傳遞null
  • 把傳遞過來的res對象賦值給 全局變量mResource,
  • 全局變量mUri置為null饮六,
  • 調(diào)用 resolveUri()

setImageURI()

 public void setImageURI(Uri uri) {
        if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) {
            updateDrawable(null);
            mResource = 0;
            mUri = uri;
            final int oldWidth = mDrawableWidth;
            final int oldHeight = mDrawableHeight;
            resolveUri();
            if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
                requestLayout();
            }
            invalidate();
        }
    }

由源碼可知:

  • 全局變量mResource設(shè)置為0
  • 把傳遞過來的uri對象賦值給全局變量mUri其垄,
  • 由于沒有Drawable對象,所以 updateDrawable()遞null
  • 調(diào)用 resolveUri()

從上可知卤橄,有兩個方法挺關(guān)鍵的绿满,一個是updateDrawable(),還有就是resolveUri(),接下來就查查這兩個方法是干嘛的
updateDrawable()更新ImageView需要顯示Drawable。


    private void updateDrawable(Drawable d) {
        if (d != mRecycleableBitmapDrawable && mRecycleableBitmapDrawable != null) {
            mRecycleableBitmapDrawable.setBitmap(null);
        }

        boolean sameDrawable = false;

        if (mDrawable != null) {
            sameDrawable = mDrawable == d;
            mDrawable.setCallback(null);
            unscheduleDrawable(mDrawable);
            if (!sCompatDrawableVisibilityDispatch && !sameDrawable && isAttachedToWindow()) {
                mDrawable.setVisible(false, false);
            }
        }
        賦值虽风,更新此ImageView顯示的drawable
        mDrawable = d;

        if (d != null) {
            d.setCallback(this);
            d.setLayoutDirection(getLayoutDirection());
            if (d.isStateful()) {
                d.setState(getDrawableState());
            }
            if (!sameDrawable || sCompatDrawableVisibilityDispatch) {
                final boolean visible = sCompatDrawableVisibilityDispatch
                        ? getVisibility() == VISIBLE
                        : isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown();
                d.setVisible(visible, true);
            }
            d.setLevel(mLevel);
            mDrawableWidth = d.getIntrinsicWidth();
            mDrawableHeight = d.getIntrinsicHeight();
            
            設(shè)置圖片的著色列表棒口,這里會用到顏色狀態(tài)列表ColorStateList
            applyImageTint();
            設(shè)置圖片的顏色過濾器寄月,即設(shè)置ColorFilter
            applyColorMod();
            
            這個重要辜膝,是指根據(jù)設(shè)置的ScaleType,使用對應(yīng)的矩陣對Drawable進行變換漾肮,以顯示合適的區(qū)域
            configureBounds();
        } else {
            mDrawableWidth = mDrawableHeight = -1;
        }
    }

resolveUri()

private void resolveUri() {
        if (mDrawable != null) {
            return;
        }
        Resources rsrc = getResources();
        if (rsrc == null) {
            return;
        }
        Drawable d = null;
        if (mResource != 0) {
            try {
                d = rsrc.getDrawable(mResource);
            } catch (Exception e) {
                mUri = null;
            }
        } else if (mUri != null) {
            String scheme = mUri.getScheme();
            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
                try {
                    ContentResolver.OpenResourceIdResult r = mContext.getContentResolver().getResourceId(mUri);
                    d = r.r.getDrawable(r.id);
                } catch (Exception e) {
                    Log.w("ImageView", "Unable to open content: " + mUri, e);
                }
            } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
                    || ContentResolver.SCHEME_FILE.equals(scheme)) {
                InputStream stream = null;
                try {
                    stream = mContext.getContentResolver().openInputStream(mUri);
                    d = Drawable.createFromStream(stream, null);
                } catch (Exception e) {
                    Log.w("ImageView", "Unable to open content: " + mUri, e);
                } finally {
                    if (stream != null) {
                        try {
                            stream.close();
                        } catch (IOException e) {
                            Log.w("ImageView", "Unable to close content: " + mUri, e);
                        }
                    }
                }
        } else {
                d = Drawable.createFromPath(mUri.toString());
            }
            if (d == null) {
                System.out.println("resolveUri failed on bad bitmap uri: " + mUri);
                // Don't try again.
                mUri = null;
            }
        } else {
            return;
        }
        updateDrawable(d);
    }

通過上面的代碼可知

  • 如果mResource不為null 厂抖,把mResource 轉(zhuǎn)換成一個Drawable對象,然后執(zhí)行updateDrawable()
  • 如果mUri不為null克懊,就把uri轉(zhuǎn)換成一個Drawable對象忱辅,然后執(zhí)行updataDrawable()方法
所以七蜘,不管是setImageUri還是setImageDrawable或者setImageResource()或者setImageBitmap
  • 首先都是把傳遞過來的對象轉(zhuǎn)換成一個Drawable對象,
  • 然后把執(zhí)行updataDrawable()方法墙懂,
  • 因為之前在updataDrawable 中重新設(shè)置的寬高橡卤,所以執(zhí)行requestLayout() 重新布局view
  • 最后執(zhí)行invalidate()重新繪制

對于上面的updateDrawale中的三個方法:

1. applyImageTint()
    ImageView在的Drawable 的著色變量

     private Drawable mDrawable = null;
    private BitmapDrawable mRecycleableBitmapDrawable = null;
    顏色狀態(tài)列表
    private ColorStateList mDrawableTintList = null;
    著色模式,有16種
    private PorterDuff.Mode mDrawableTintMode = null;
    private boolean mHasDrawableTint = false;
    private boolean mHasDrawableTintMode = false;

 private void applyImageTint() {
        if (mDrawable != null && (mHasDrawableTint || mHasDrawableTintMode)) {
            mDrawable = mDrawable.mutate();

            if (mHasDrawableTint) {
               設(shè)置著色集合
                mDrawable.setTintList(mDrawableTintList);
            }

            if (mHasDrawableTintMode) {
               設(shè)置著色模式
                mDrawable.setTintMode(mDrawableTintMode);
            }

            // The drawable (or one of its children) may not have been
            // stateful before applying the tint, so let's try again.
            if (mDrawable.isStateful()) {
                mDrawable.setState(getDrawableState());
            }
        }
    }
1.1 ColorStateList類

ColorStateList是顏色狀態(tài)列表類损搬,ColorStateList是從XML中定義的XML資源文件中創(chuàng)建的應(yīng)用程序資源目錄的“color”子目錄碧库。 XML文件包含一個單一的“選擇器”元素內(nèi)有多個“item”元素。

<selector xmlns:android="http://schemas.android.com/apk/res/android"/>
   < item android:state_focused="true"
           android:color="@color/sample_focused" />;
     <item android:state_pressed="true"
            android:state_enabled="false"
            android:color="@color/sample_disabled_pressed" />;
      <item android:state_enabled="false"
           android:color="@color/sample_disabled_not_pressed" />;
     <item android:color="@color/sample_default" />;
   </selector/>

這定義了一組狀態(tài)規(guī)格/顏色對巧勤,其中每個狀態(tài)規(guī)范指定了一組表示視圖必須處于或不在嵌灰,顏色指定相關(guān)的顏色與該規(guī)范。
安卓控制Texview的文字顏色切換:

<?xml version="1.0" encoding="utf-8"?>  
<selector xmlns:android="http://schemas.android.com/apk/res/android">  
    <item android:state_focused="true" android:color="#ff2525"></item>  
    <item android:color="#ff5b5b5b"></item>  
</selector>  
ColorStateList csl=(ColorStateList)getResources().getColorStateList(R.color.button_text);  
       for (int i =0;i<3;i++){  
           Button btn =  new Button(mContext);  
           btn.setText("按鈕"+i);  
           btn.setTextColor(csl);  
           this.addView(btn,params);  
       }  
2. applyColorMod()
// these are applied to the drawable
    
    顏色過濾器   
    private ColorFilter mColorFilter = null;
    private boolean mHasColorFilter = false;
    private Xfermode mXfermode;
    private int mAlpha = 255;
    private final int mViewAlphaScale = 256;
    private boolean mColorMod = false;

 private void applyColorMod() {
        // Only mutate and apply when modifications have occurred. This should
        // not reset the mColorMod flag, since these filters need to be
        // re-applied if the Drawable is changed.
        if (mDrawable != null && mColorMod) {
            mDrawable = mDrawable.mutate();
            if (mHasColorFilter) {
                 設(shè)置顏色過濾器
                mDrawable.setColorFilter(mColorFilter);
            }
            mDrawable.setXfermode(mXfermode);
            mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
        }
    }
2.1 ColorFilter類的使用

ColorFilter主要用來處理顏色颅悉,這里將講解它的三個子類沽瞭,ColorMatrixColorFilter,
LightingColorFilter以及PorterDuffColorFilter。
** ColorMatrixColorFilter**
這個類主要是使用matrix也就是矩陣對顏色做運算剩瓶,矩陣的形態(tài)如下:


ColorMatrix.png

顏色值和該矩陣的換算關(guān)系如下:

RGB和Alpha的終值計算方法如下:
Red通道終值= a[0] * srcR + a[1] * srcG + a[2] * srcB + a[3] * srcA + a[4]
Green通道終值= a[5] * srcR + a[6] * srcG + a[7] * srcB + a[8] * srcA + a[9]
Blue通道終值= a[10] * srcR + a[11] * srcG + a[12] * srcB + a[13] * srcA + a[14]
Alpha通道終值= a[15]*srcR+a[16]*srcG + a[17] * srcB + a[18] * srcA + a[19]

備注:
srcR為原圖Red通道值驹溃,srcG為原圖Green通道值,srcB為原圖Blue通道值延曙,srcA為原圖Alpha通道值吠架。
每個通道的源值和終值都在0到255的范圍內(nèi)。即使計算結(jié)果大于255或小于0搂鲫,值都將被限制在0到255的范圍內(nèi)傍药。

看了上面的說明可能不太清晰,這里再看看例子


image.png

上面有3張圖片魂仍,其實是同一個圖片繪制的拐辽,只是使用了不同的ColorMatrixColorFilter,最后面一張是完全黑白的。

private final static float[] MATRIX = new float[] {
            0.5f, 0, 0, 0, 0,
            0, 0.5f, 0, 0, 0,
            0, 0, 0.5f, 0, 0,
            0, 0, 0, 1, 0 };

Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.home);

canvas.drawBitmap(bitmap, 100, 0, paint);

ColorMatrixColorFilter filter = new ColorMatrixColorFilter(MATRIX);
paint.setColorFilter(filter);
canvas.drawBitmap(bitmap, 100, 500, paint);


ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
canvas.drawBitmap(bitmap, 100, 1000, paint);

MATRIX中的值是0.5使得R,G,B通道的值都減半了擦酌,所以第二張圖看起來暗了很多
第三張圖使用了ColorMatrix俱诸,并且使用colorMatrix.setSaturation(0)將飽和度設(shè)置為了0,這樣一來就變?yōu)榱撕诎讏D片赊舶。

明白了上面顏色運算的規(guī)則睁搭,就可以自己更改矩陣的值從而達到想要的效果。這里舉一個示例

LightingColorFilter
LightingColorFilter是上面ColorMatrixColorFilter的一個簡化版本笼平,構(gòu)造函數(shù)也比較簡單:

public LightingColorFilter(int mul, int add)  

mul代表multiply,也就是乘法
add代表加法园骆,也就是顏色偏移量

那么這里將怎么計算顏色值呢,其實跟上面矩陣的方式差不多寓调。這里兩個參數(shù)都是int類型锌唾,所以每8位被分別分解為RGB對應(yīng)的值來做乘法和加法

image.png
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.home);

canvas.drawBitmap(bitmap, 100, 0, paint);

LightingColorFilter filter = new LightingColorFilter(0x888888, 0x000000);
paint.setColorFilter(filter);
canvas.drawBitmap(bitmap, 100, 500, paint);

LightingColorFilter filter2 = new LightingColorFilter(0x888888, 0x555555);
paint.setColorFilter(filter2);
canvas.drawBitmap(bitmap, 100, 1000, paint);

看第二張圖片的ColorFilter構(gòu)造new LightingColorFilter(0x888888, 0x000000);
其中mul為0x888888,那么RGB通道對應(yīng)的mul值都為88,add為0x000000則對應(yīng)的偏移量都為0晌涕。第二張圖變暗了滋捶,基本可以看出計算方法。
color = colormul/255+add (計算結(jié)果大于等于255都指定為255)
其中color可以為RGB三種通道中的一種余黎,mul和add分別為通道對應(yīng)的值重窟。假設(shè)R通道的值就為
R=R
0x88/0xff+0
0x88/0xff肯定是小于1的,所以顏色變暗了惧财。

第三張圖的mul值和第二張相同亲族,但是由于第三張圖的add值比較大,所以反而比第一張圖還亮可缚。

PorterDuffColorFilter

public PorterDuffColorFilter(int srcColor, PorterDuff.Mode mode)  

srcColor源顏色霎迫,
mode是色彩的混合模式,這里的混合模式我們在后面再詳細講解帘靡,這里簡單看一個示例知给。


image.png
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.home);

canvas.drawBitmap(bitmap, 100, 0, paint);

PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.MULTIPLY);
paint.setColorFilter(filter);
canvas.drawBitmap(bitmap, 100, 500, paint);

這里PorterDuffColorFilter的兩個參數(shù)分別是Color.BLUE以及PorterDuff.Mode.MULTIPLY

先不要糾結(jié)PorterDuff.Mode.MULTIPLY的含義,這里主要是將Color.BLUE提供的藍色和原圖進行一定的合并運算描姚。就得到了上面的效果涩赢,那么傳入不同PorterDuff.Mode值就能得到不同的效果。各個值不同的效果我們在后面的篇幅中詳細講解轩勘。

關(guān)于PorterDuff.Mode筒扒,Android系統(tǒng)一共提供了18種混排模式,在模擬器的ApiDemos/Graphics/XferModes绊寻,有張效果圖:

image.png

這張圖可以很形象的說明圖片各種混排模式下的效果花墩。其中Src代表原圖,Dst代表目標(biāo)圖澄步,兩張圖片使用不同的混排方式后冰蘑,得到的圖像是如上圖所示的。PorterDuff.Mode也提供了18種混排模式已經(jīng)算法村缸,其中比上圖多了ADD和OVERLAY兩種模式
image.png

其中Sa全稱為Source alpha表示源圖的Alpha通道祠肥;Sc全稱為Source color表示源圖的顏色;Da全稱為Destination alpha表示目標(biāo)圖的Alpha通道梯皿;Dc全稱為Destination color表示目標(biāo)圖的顏色仇箱,[...,..]前半部分計算的是結(jié)果圖像的Alpha通道值,“,”后半部分計算的是結(jié)果圖像的顏色值东羹。圖像混排后是依靠這兩個值來重新計算ARGB值的剂桥,具體計算算法,抱歉百姓,我也不知道渊额,不過不要緊,不了解計算算法也不影響我們程序員寫程序的垒拢。我們只要對照上面的apiDemo中提供的圖片就能推測出混排后的結(jié)果的旬迹,下面是在網(wǎng)上找到的漢字語言描述,感謝這位作者的總結(jié)求类。

注意:先繪制dst奔垦,再繪制src。

1.PorterDuff.Mode.CLEAR

所繪制源圖像不會提交到畫布上尸疆。

2.PorterDuff.Mode.SRC

只顯示源圖像椿猎。

3.PorterDuff.Mode.DST

只顯示目標(biāo)圖像。

4.PorterDuff.Mode.SRC_OVER

正常繪制顯示寿弱,源圖像居上顯示犯眠。

5.PorterDuff.Mode.DST_OVER

上下層都顯示。目標(biāo)圖像居上顯示症革。

6.PorterDuff.Mode.SRC_IN

取兩層繪制交集中的源圖像筐咧。

7.PorterDuff.Mode.DST_IN

取兩層繪制交集中的目標(biāo)圖像。

8.PorterDuff.Mode.SRC_OUT

只在源圖像和目標(biāo)圖像不相交的地方繪制源圖像噪矛。

9.PorterDuff.Mode.DST_OUT

只在源圖像和目標(biāo)圖像不相交的地方繪制目標(biāo)圖像量蕊。

10.PorterDuff.Mode.SRC_ATOP

在源圖像和目標(biāo)圖像相交的地方繪制源圖像,在不相交的地方繪制目標(biāo)圖像艇挨。

11.PorterDuff.Mode.DST_ATOP

在源圖像和目標(biāo)圖像相交的地方繪制目標(biāo)圖像而在不相交的地方繪制源圖像残炮。

12.PorterDuff.Mode.XOR

異或:去除兩圖層交集部分

13.PorterDuff.Mode.DARKEN

取兩圖層全部區(qū)域,交集部分顏色加深

14.PorterDuff.Mode.LIGHTEN

取兩圖層全部缩滨,點亮交集部分顏色

15.PorterDuff.Mode.MULTIPLY

取兩圖層交集部分疊加后顏色

16.PorterDuff.Mode.SCREEN

濾色势就。

PorterDuff的源碼

package android.graphics;

  這個類包含了alpha合成和混合模式的列表
  可以傳遞給{@link PorterDuffXfermode},一個專門的實現(xiàn)
  {@link Paint}的{@link Paint#setXfermode(Xfermode)傳輸模式}脉漏。
  所有可用的模式都可以在{@link Mode}枚舉中找到

public class PorterDuff {
   
    public enum Mode {
        // these value must match their native equivalents. See SkXfermode.h
     
        CLEAR       (0),
   
        SRC         (1),
       
        DST         (2),
     
        SRC_OVER    (3),
      
        DST_OVER    (4),
        
        SRC_IN      (5),
      
        DST_IN      (6),
      
        SRC_OUT     (7),
      
        DST_OUT     (8),
       
        SRC_ATOP    (9),
       
        DST_ATOP    (10),
       
        XOR         (11),
        
        DARKEN      (16),
       
        LIGHTEN     (17),
       
        MULTIPLY    (13),
       
        SCREEN      (14),
       
        ADD         (12),
        
        OVERLAY     (15);

        Mode(int nativeInt) {
            this.nativeInt = nativeInt;
        }

        /**
         * @hide
         */
        public final int nativeInt;
    }

    /**
     * @hide
     */
    public static int modeToInt(Mode mode) {
        return mode.nativeInt;
    }

    /**
     * @hide
     */
    public static Mode intToMode(int val) {
        switch (val) {
            default:
            case  0: return Mode.CLEAR;
            case  1: return Mode.SRC;
            case  2: return Mode.DST;
            case  3: return Mode.SRC_OVER;
            case  4: return Mode.DST_OVER;
            case  5: return Mode.SRC_IN;
            case  6: return Mode.DST_IN;
            case  7: return Mode.SRC_OUT;
            case  8: return Mode.DST_OUT;
            case  9: return Mode.SRC_ATOP;
            case 10: return Mode.DST_ATOP;
            case 11: return Mode.XOR;
            case 16: return Mode.DARKEN;
            case 17: return Mode.LIGHTEN;
            case 13: return Mode.MULTIPLY;
            case 14: return Mode.SCREEN;
            case 12: return Mode.ADD;
            case 15: return Mode.OVERLAY;
        }
    }
}
3. applyColorMod()
 private void configureBounds() {
        if (mDrawable == null || !mHaveFrame) {
            return;
        }
        圖片drawable的寬和高
        final int dwidth = mDrawableWidth;
        final int dheight = mDrawableHeight;
        imageview用于顯示區(qū)域的寬和高
        final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
        final int vheight = getHeight() - mPaddingTop - mPaddingBottom;

        final boolean fits = (dwidth < 0 || vwidth == dwidth)
                && (dheight < 0 || vheight == dheight);
         ScaleType.FIT_XY 的縮放類型
        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
            /* If the drawable has no intrinsic size, or we're told to
                scaletofit, then we just fill our entire view.
            */
            選擇mDrawable的區(qū)域顯示
            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        } else {
            // We need to do the scaling ourself, so have the drawable
            // use its native size.
            mDrawable.setBounds(0, 0, dwidth, dheight);
              ScaleType.MATRIX 的縮放類型
            if (ScaleType.MATRIX == mScaleType) {
                // Use the specified matrix as-is.
                if (mMatrix.isIdentity()) {
                    mDrawMatrix = null;
                } else {
                    mDrawMatrix = mMatrix;
                }
            } else if (fits) {
                // The bitmap fits exactly, no transform needed.
                mDrawMatrix = null;
            } else if (ScaleType.CENTER == mScaleType) {
                   ScaleType.CENTER 的縮放類型
                // Center bitmap in view, no scaling.
                mDrawMatrix = mMatrix;
                mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
                                         Math.round((vheight - dheight) * 0.5f));
            } else if (ScaleType.CENTER_CROP == mScaleType) {
                  ScaleType.CENTER_CROP 的縮放類型
                mDrawMatrix = mMatrix;

                float scale;
                float dx = 0, dy = 0;

                if (dwidth * vheight > vwidth * dheight) {
                    scale = (float) vheight / (float) dheight;
                    dx = (vwidth - dwidth * scale) * 0.5f;
                } else {
                    scale = (float) vwidth / (float) dwidth;
                    dy = (vheight - dheight * scale) * 0.5f;
                }
                先對圖片縮放
                mDrawMatrix.setScale(scale, scale);
                在平移
                mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
            } else if (ScaleType.CENTER_INSIDE == mScaleType) {
                ScaleType.CENTER_INSIDE 的縮放類型
                mDrawMatrix = mMatrix;
                float scale;
                float dx;
                float dy;

                if (dwidth <= vwidth && dheight <= vheight) {
                    圖片寬高小于view寬高時蛋勺,不縮放
                    scale = 1.0f;
                } else {
                   計算縮放
                    scale = Math.min((float) vwidth / (float) dwidth,
                            (float) vheight / (float) dheight);
                }

                dx = Math.round((vwidth - dwidth * scale) * 0.5f);
                dy = Math.round((vheight - dheight * scale) * 0.5f);
                先縮放 后平移
                mDrawMatrix.setScale(scale, scale);
                mDrawMatrix.postTranslate(dx, dy);
            } else {
                // Generate the required transform.
                mTempSrc.set(0, 0, dwidth, dheight);
                mTempDst.set(0, 0, vwidth, vheight);

                mDrawMatrix = mMatrix;
               剩下的Fit_XY等Fit系列,都在這里
                mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
            }
        }
    }


    private static final Matrix.ScaleToFit[] sS2FArray = {
        Matrix.ScaleToFit.FILL,
        Matrix.ScaleToFit.START,
        Matrix.ScaleToFit.CENTER,
        Matrix.ScaleToFit.END
    };

    private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st)  {
        // ScaleToFit enum to their corresponding Matrix.ScaleToFit values
        return sS2FArray[st.nativeInt - 1];
    }

 /**
      *將矩陣設(shè)置為比例并轉(zhuǎn)換將源矩形映射到的值
      *目標(biāo)矩形鸠删,如果結(jié)果可以表示抱完,則返回true。
     *
      * @param src要映射的源矩形刃泡。
      * @參數(shù)dst要映射到的目標(biāo)矩形巧娱。
      * @參數(shù)stf ScaleToFit選項
      * @如果矩陣可以用矩形映射表示,則返回true烘贴。
     */
    public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
        if (dst == null || src == null) {
            throw new NullPointerException();
        }
        return nSetRectToRect(native_instance, src, dst, stf.nativeInt);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末禁添,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子桨踪,更是在濱河造成了極大的恐慌老翘,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異铺峭,居然都是意外死亡墓怀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門卫键,熙熙樓的掌柜王于貴愁眉苦臉地迎上來傀履,“玉大人,你說我怎么就攤上這事莉炉〉稣耍” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵絮宁,是天一觀的道長梆暮。 經(jīng)常有香客問我,道長绍昂,這世上最難降的妖魔是什么啦粹? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮治专,結(jié)果婚禮上卖陵,老公的妹妹穿的比我還像新娘。我一直安慰自己张峰,他們只是感情好泪蔫,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著喘批,像睡著了一般撩荣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饶深,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天餐曹,我揣著相機與錄音,去河邊找鬼敌厘。 笑死台猴,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俱两。 我是一名探鬼主播饱狂,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宪彩!你這毒婦竟也來了休讳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤尿孔,失蹤者是張志新(化名)和其女友劉穎俊柔,沒想到半個月后筹麸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡雏婶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年物赶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尚骄。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡佑吝,死狀恐怖蜡饵,靈堂內(nèi)的尸體忽然破棺而出馏颂,到底是詐尸還是另有隱情系宫,我是刑警寧澤游桩,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布丧裁,位于F島的核電站讼撒,受9級特大地震影響再菊,放射性物質(zhì)發(fā)生泄漏轧坎。R本人自食惡果不足惜宏邮,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缸血。 院中可真熱鬧蜜氨,春花似錦、人聲如沸捎泻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笆豁。三九已至郎汪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闯狱,已是汗流浹背煞赢。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留哄孤,地道東北人照筑。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像瘦陈,于是被迫代替她去往敵國和親凝危。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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