国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android源碼系列之深入理解ImageView的ScaleType屬性

2019-12-12 06:15:00
字體:
來源:轉載
供稿:網友

做Android開發的童靴們肯定對系統自帶的控件使用的都非常熟悉,比如Button、TextView、ImageView等。如果你問我具體使用,我會給說:拿ImageView來說吧,首先創建一個新的項目,在項目布局文件中應用ImageView控件,代碼如下:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:background="#bbaacc" >   <ImageView  android:src="@drawable/ic_launcher"  android:layout_width="wrap_content"  android:layout_height="wrap_content"  android:background="#aabbcc" />  </LinearLayout> 

        上邊布局文件為了便于查看各種屬性效果,故意加了兩個背景顏色,這對我們今天的源碼分析影響不大。接著運行一下代碼,效果圖如下:

        恩,不錯,運行結果正如所愿,屏幕上顯示的正是我們設置的圖片,這時候心中不由欣喜ImageView的使用就是這樣簡單,so easy嘛!呵呵,如果真是這么想那就大錯特錯了,上邊的示例僅僅是在布局文件中使用了ImageView的src,layout_width和layout_height這三個屬性罷了,它其他的重要屬性我們還沒有用到,今天這篇文章就是主要結合源碼講解ImageView的另一個重要的屬性------ScaleType,其他的一些屬性等將來需要的話再做詳細解說。好了,現在正式進入主題。
        ScaleType屬性主要是用來定義圖片(Bitmap)如何在ImageView中展示的,姑且就認為是展示吧,系統給我們提供了8種可選屬性:matrix、fitXY、fitStart、fitCenter、fitEnd、center、centerCrop和centerInside。每一種屬性對應的展示效果是不一樣的,下面我們先來做一個實驗來說明每一種屬性的顯示效果,我從之前的項目中挑選了兩張圖片,一張圖片的實際尺寸是720*1152,另一張是我把第一張圖翻轉放縮得到的,它的實際尺寸是96*60,之所以采用兩張圖片是為了便于對比和觀察結果,原圖片如下:

        OK,測試圖片準備好了,接下來我們在布局文件中分別使用ScaleType的每一個屬性值,我們開始寫布局文件,內容如下:

<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:background="#bbccaa" >   <TableLayout  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:padding="10dp" >   <TextView  android:layout_width="wrap_content"  android:layout_height="wrap_content"  android:layout_marginBottom="10dp"  android:text="圖片尺寸的寬和高都遠遠大于ImageView的尺寸" />   <TableRow  android:layout_width="match_parent"  android:layout_height="wrap_content" >   <ImageView  android:layout_width="300px"  android:layout_height="300px"  android:background="#aabbcc"  android:scaleType="matrix"  android:src="@drawable/test" />   <ImageView  android:layout_width="300px"  android:layout_height="300px"  android:layout_marginLeft="10dp"  android:background="#aabbcc"  android:scaleType="fitXY"  android:src="@drawable/test" />   <ImageView  android:layout_width="300px"  android:layout_height="300px"  android:layout_marginLeft="10dp"  android:background="#aabbcc"  android:scaleType="fitStart"  android:src="@drawable/test" />   <ImageView  android:layout_width="300px"  android:layout_height="300px"  android:layout_marginLeft="10dp"  android:background="#aabbcc"  android:scaleType="fitCenter"  android:src="@drawable/test" />  </TableRow>   <TableRow  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_marginTop="5dp" >   <TextView  android:layout_width="300px"  android:layout_height="wrap_content"  android:gravity="center"  android:text="matrix" />   <TextView  android:layout_width="300px"  android:layout_height="wrap_content"  android:gravity="center"  android:text="fitXY" />   <TextView  android:layout_width="300px"  android:layout_height="wrap_content"  android:gravity="center"  android:text="fitStart" />   <TextView  android:layout_width="300px"  android:layout_height="wrap_content"  android:gravity="center"  android:text="fitCenter" />  </TableRow>   <TableRow  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_marginTop="10dp" >   <ImageView  android:layout_width="300px"  android:layout_height="300px"  android:background="#aabbcc"  android:scaleType="fitEnd"  android:src="@drawable/test" />   <ImageView  android:layout_width="300px"  android:layout_height="300px"  android:layout_marginLeft="10dp"  android:background="#aabbcc"  android:scaleType="center"  android:src="@drawable/test" />   <ImageView  android:layout_width="300px"  android:layout_height="300px"  android:layout_marginLeft="10dp"  android:background="#aabbcc"  android:scaleType="centerCrop"  android:src="@drawable/test" />   <ImageView  android:layout_width="300px"  android:layout_height="300px"  android:layout_marginLeft="10dp"  android:background="#aabbcc"  android:scaleType="centerInside"  android:src="@drawable/test" />  </TableRow>   <TableRow  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_marginTop="5dp" >   <TextView  android:layout_width="300px"  android:layout_height="wrap_content"  android:gravity="center"  android:text="fitEnd" />   <TextView  android:layout_width="300px"  android:layout_height="wrap_content"  android:gravity="center"  android:text="center" />   <TextView  android:layout_width="300px"  android:layout_height="wrap_content"  android:gravity="center"  android:text="centerCrop" />   <TextView  android:layout_width="300px"  android:layout_height="wrap_content"  android:gravity="center"  android:text="centerInside" />  </TableRow>  </TableLayout>  </ScrollView> 

        為了快速進入今天文章主題,布局文件并沒有按照平時開發中所遵循的Android開發規范來寫。布局中使用的是TableLayout標簽嵌套TableRow的方式,分成兩行,每一行是4列,恰好8種屬性可以合理對比查看。當然了這里有更高效的寫法來實現同樣的效果,比如使用RelativeLayout布局等。
        在布局文件中我們定義了每個ImageView的寬高都是固定的300像素,這個尺寸遠小于或者是遠大于測試的圖片尺寸,另外為了便于觀察效果我給每個ImageView的背景都設置了顏色,接著運行大圖和小圖的測試結果,效果如下:

        通過上述實驗結果對比,就可以得出部分屬性的展示效果。比如fitXY屬性,當ImageView的屬性設置成了fitXY時,圖片的寬和高就會相應的拉伸或者是壓縮來填充滿整個ImageView,注意這種拉放縮不成比例。當ImageView的屬性設置成了matrix時,如果圖片寬高大于ImageView的寬高時,圖片的顯示就是從ImageView的左上角開始平鋪,超出部分不再顯示;如果圖片寬高小于ImageView的寬高時,圖片的顯示也是從ImageView的左上角開始平鋪,缺少部分空白顯示出來或者是顯示ImageView的背景。以上僅僅是根據運行結果來得出的結果,權威結論還要通過查看源碼來得出,本文分析的源碼是Android2.2版本。
        分析ImageView的源碼首先從它的構造方法開始,看一下構造方法里邊都做了什么工作。構造方法如下:

public ImageView(Context context) {  super(context);  initImageView(); }  public ImageView(Context context, AttributeSet attrs) {  this(context, attrs, 0); }  public ImageView(Context context, AttributeSet attrs, int defStyle) {  super(context, attrs, defStyle);  initImageView();   TypedArray a = context.obtainStyledAttributes(attrs,  com.android.internal.R.styleable.ImageView, defStyle, 0);   Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);  if (d != null) {  setImageDrawable(d);  }   //////////////////////////////////////////////////  //  // 以下源碼部分屬性初始化不涉及主核心,不再貼出  //  //////////////////////////////////////////////////    a.recycle();   //need inflate syntax/reader for matrix } 

        通過查看構造方法發現,在三個構造方法中都調用了initImageView()這個方法,這個方法是干嘛使的,我們稍后在看。其次是根據在布局文件中定義的屬性來初始化相關屬性,在測試布局中我們僅僅是用了ImageView的src,layout_width,layout_height和scaleType屬性(background屬性暫時忽略)。那也就是說在構造函數的初始化中就是對相關屬性進行了賦值操作,通過解析src屬性我們獲取到了一個Drawable的實例對象d,如果d是非空的話就把d作為參數又調用了setImageDrawable(d)函數,我們看看一下這個函數主要做了什么工作,源碼如下:

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

        此方法類型是public的,目的是干嘛使的不再解釋了,注釋上說的是把給定的drawable作為當前ImageView的展示內容,接著是個條件判斷,在剛剛完成初始化的時候mDrawable屬性還沒有被賦值此時為空,因此判斷成立程序進入條件語句繼續執行,在這里把屬性mResource和mUri歸零操作并計入mDrawableWidth和mDrawableHeight的初始值,緊接著把傳遞進來的drawable參數傳遞給了updateDrawable()方法,那我們繼續跟進updateDrawable()看看這里邊又做了什么操作,源碼如下:

private void updateDrawable(Drawable d) {  if (mDrawable != null) {  mDrawable.setCallback(null);  unscheduleDrawable(mDrawable);  }  mDrawable = d;  if (d != null) {  d.setCallback(this);  if (d.isStateful()) {  d.setState(getDrawableState());  }  d.setLevel(mLevel);  mDrawableWidth = d.getIntrinsicWidth();  mDrawableHeight = d.getIntrinsicHeight();  applyColorMod();  configureBounds();  } else {  mDrawableWidth = mDrawableHeight = -1;  } } 

        該方法首先進行了非空判斷,此時mDrawable的值依然是空,所以條件判斷不成立跳過此部分,緊接著把傳遞進來的非空參數d的字賦值給了屬性mDrawable,到這里mDrawable才算是完成了賦值操作。然后又進行了條件判斷,并設置d的callback為當前ImageView(因為ImageView的父類View實現了Drawable的Callback接口)接下來又把圖片的寬和高分別賦值給了mDrawableWidth和mDrawableHeight,緊接著又調用了applyColorMod()方法,當我們沒有給ImageView設置透明度或者是顏色過濾器時該方法不會執行。然后調用configureBounds()方法,此方法是我們今天要講的和ScaleType屬性息息相關的重點,不耽誤時間了趕緊瞅一下源碼吧,(*^__^*) 嘻嘻……

 private void configureBounds() {  if (mDrawable == null || !mHaveFrame) {  return;  }    int dwidth = mDrawableWidth;  int dheight = mDrawableHeight;    int vwidth = getWidth() - mPaddingLeft - mPaddingRight;  int vheight = getHeight() - mPaddingTop - mPaddingBottom;    boolean fits = (dwidth < 0 || vwidth == dwidth) &&  (dheight < 0 || vheight == dheight);   ////////////////////////////////////////代碼塊一////////////////////////////////////////   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.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);    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) {  // Center bitmap in view, no scaling.  mDrawMatrix = mMatrix;  mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f),   (int) ((vheight - dheight) * 0.5f + 0.5f));   ////////////////////////////////////////代碼塊五////////////////////////////////////////   } else if (ScaleType.CENTER_CROP == mScaleType) {  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((int) (dx + 0.5f), (int) (dy + 0.5f));   ////////////////////////////////////////代碼塊六////////////////////////////////////////   } else if (ScaleType.CENTER_INSIDE == mScaleType) {  mDrawMatrix = mMatrix;  float scale;  float dx;  float dy;   if (dwidth <= vwidth && dheight <= vheight) {  scale = 1.0f;  } else {  scale = Math.min((float) vwidth / (float) dwidth,  (float) vheight / (float) dheight);  }   dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);  dy = (int) ((vheight - dheight * scale) * 0.5f + 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;  mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));   ////////////////////////////////////////代碼塊八////////////////////////////////////////   }  }  } 

        configureBoundd()函數比較長,為了方便分析源碼,我把該函數代碼分成了8小塊,每一個小塊都是一個單獨的邏輯。每一塊的詳解如下:
代碼塊一:
        該模塊代碼首先做了一個條件判斷,如果當前mDrawable為空或者是mHaveFrame為false則函數直接返回不再往下執行,由于后邊的邏輯主要是根據ScaleType屬性的類型來判斷圖片的展示方式,所以再后來這個函數肯定是能往下走的通的,由于篇幅的原因不再深入講解該函數的調用時機,我會在之后的文章中專門根據源碼講解一下Android系統下View的繪制流程,在之后的繪制流程中會提到configureBounds()的調用時機。該代碼塊的邏輯是獲取圖片的寬高存儲在dwidth,dheight中,然后又獲取到了ImageView的顯示圖片區域的寬高存放在vwidth和vheight中。然后定義了一個boolean類型的變量,該變量若為true就表示不需要對圖片進行放縮處理。
代碼塊二:
        該代碼塊的邏輯是當獲取到的圖片尺寸的寬高未知或者是ImageView的ScaleType屬性為FIT_XY時,將mDrawable的顯示邊界設置成控件ImageView的顯示區域大小,并且把mDrawMatrix對象設置成null。需要說明的是setBounds方法用來設置Drawable的繪制區域的,最終在ImageView的onDraw方法中把mDrawable表示的圖片繪制到ImageView上。
代碼塊三:
        如果圖片寬高不為0并且ImageView的ScaleType屬性不是FIT_XY時,就會把mDrawable的繪制區域設置成圖片的原始大小。接著進行判斷ImageView的ScaleType屬性值是否是MATRIX,如果是MATRIX類型,就會把當前mMatrix賦值給mDrawMatrix。
代碼塊四:
        代碼塊四是一個if(fits)的判斷,如果fits的值為true,就表示圖片大小等于ImageView的大小,不需要對圖片進行放縮處理了。
代碼塊五:
       當mScaleType的類型為CENTER時,實際是將圖片進行移位操作,直接點說就是把圖片的中心點移動到ImageView的中心點,如果圖片的寬高大于ImageView的寬高此時只顯示ImageView所包含的部分,大于ImageView的部分不再顯示。
      【注意:CENTER屬性只對圖片進行移動操作而不會進行放縮操作】。
代碼塊六:
        代碼塊六是當mScaleType==CENTER_CROP時,進行了一個條件判斷:if(dwidth *vheight >vwidth *dheight),看到這句代碼的時候我并沒有理解其含義,然后我把這句代碼轉換了一下寫法:if(dwidth / vwidth > dheight / vheight),通過這種轉換寫法然后再看就比較明白了,主要是用來判斷寬高比的,就是說用來判斷是圖片的寬比較接近ImageView控件的寬還是圖片的高比較接近ImageView控件的高。如果是圖片的高比較接近ImageView的高,通過計算獲取需要放縮的scale的值,再計算出需要對圖片的寬進行移動的值,最后通過對mDrawMatrix屬性進行設置放縮和移動來達到控制圖片進行放縮和移動的效果,同樣的邏輯處理了當圖片的寬比較接近ImageView的寬的情況。從代碼可以總結CENTER_CROP屬性的特點是:對圖片的寬高進行放縮處理,使一邊達到ImageView控件的寬高,另一邊進行進行移動居中顯示若超出則不再顯示。
代碼塊七:
        代碼塊七是當mScaleType==CENTER_INSIDE時,首先判斷圖片寬高是否小于ImageView寬高,如果圖片寬高小于ImageView的寬高,則scale=1.0f,也就是說不對圖片進行放縮處理而是直接移動圖片進行居中顯示,否則通過Math.min((float)vwidth / (float)dwidth, (float) vheight / (float)dheight);計算出需要對圖片進行的放縮值,然后放縮圖片寬高并對圖片移動居中顯示。從代碼可以總結CENTER_INSIDE的特點是:控制圖片尺寸,對圖片寬高進行壓縮處理,根據圖片和控件的寬高比拿最大的一邊進行壓縮使之同控件一邊相同,另一邊小于控件。
代碼塊八:
        代碼塊八是對mScaleType為FIT_CENTER,FIT_START,FIT_END的情況下統一做了處理,先設置mTempSrc和mTempDst的邊界后,通過調用mDrawMatrix的setRectToRect()方法來對圖片進行放縮和移動操作,使圖片最大邊始終等于ImageView相應的邊。結合代碼和代碼測試結果可以得出如下結論:
        當圖片的高大于寬時:
1.當mScaleType == FIT_START時,對圖片進行等比放縮,使圖片的高與ImageView的高相等,移動圖片使之左對齊。
2.當mScaleType == FIT_CENTER時,對圖片進行等比放縮,使圖片的高與ImageView的高相等,移動圖片使之居中對齊。
3.當mScaleType == FIT_END時,對圖片進行等比放縮,使圖片的高與ImageView的高相等,移動圖片使之右對齊。
        當圖片的寬大于高時:
1.當mScaleType == FIT_START時,對圖片進行等比放縮,使圖片的寬與ImageView的寬相等,移動圖片使之上對齊。
2.當mScaleType == FIT_CENTER時,對圖片進行等比放縮,使圖片的寬與ImageView的寬相等,移動圖片使之居中對齊。
3.當mScaleType == FIT_END時,對圖片進行等比放縮,使圖片的寬與ImageView的寬相等,移動圖片使之下對齊。
        到這里mScaleType的8種用根據法算是分析完了,現在稍做總結:
FIT_XY:對原圖寬高進行放縮,該放縮不保持原比例來填充滿ImageView。
MATRIX:不改變原圖大小從ImageView的左上角開始繪制,超過ImageView部分不再顯示。
CENTER:對原圖居中顯示,超過ImageView部分不再顯示。
CENTER_CROP:對原圖居中顯示后進行等比放縮處理,使原圖最小邊等于ImageView的相應邊。
CENTER_INSIDE:若原圖寬高小于ImageView寬高,這原圖不做處理居中顯示,否則按比例放縮原圖寬(高)是之等于ImageView的寬(高)。
FIT_START:對原圖按比例放縮使之等于ImageView的寬高,若原圖高大于寬則左對齊否則上對其。
FIT_CENTER:對原圖按比例放縮使之等于ImageView的寬高使之居中顯示。
FIT_END:對原圖按比例放縮使之等于ImageView的寬高,若原圖高大于寬則右對齊否則下對其。
        還記得在博文開始的時候說到在ImageView的構造方法中都調用了initImageView()方法么?他的源碼如下:

private void initImageView() {  mMatrix = new Matrix();  mScaleType = ScaleType.FIT_CENTER; } 

        可以看到,當我們沒有在布局文件中使用scaleType屬性或者是沒有手動調用setScaleType方法時,那么mScaleType的默認值就是FIT_CENTER。
        好了,有關ImageView的ScaleType的講解就算結束了,如有錯誤歡迎指正。以后如有其它屬性需要詳解,再做記錄吧。

原文地址:http://blog.csdn.net/llew2011/article/details/50855655

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 方山县| 扎鲁特旗| 格尔木市| 汉中市| 清镇市| 五大连池市| 玉溪市| 徐汇区| 泾源县| 沈丘县| 社旗县| 黔西| 随州市| 三河市| 涞源县| 汉沽区| 祥云县| 大竹县| 凤阳县| 克拉玛依市| 奉新县| 商洛市| 柯坪县| 嘉善县| 阳春市| 玉田县| 大洼县| 石首市| 大邑县| 丹凤县| 宕昌县| 龙胜| 萨嘎县| 甘孜县| 丹寨县| 醴陵市| 禄劝| 依兰县| 杨浦区| 呼玛县| 监利县|