劉下記錄:ImageView.scaleType 屬性全解析(含視頻)

0. 譯序


原文:Working with the ImageViewcss

ImageView 有一個常用場景:ImageView 的寬度固定,高度等比例縮放。且 ImageView 在 padding 全部爲 0 的狀況下不留白邊,即圖片全然填充 ImageView。(注意:該方法對「高度固定,寬度等比例縮放」不適用,詳細見 例1-2 代碼)html

這篇文給出了這個場景的答案,翻譯過來以背不時之需。java

上述需求可以用例如如下屬性實現:
例1-1android

<ImageView  android:id="@+id/image" android:layout_width="30dp" android:layout_height="wrap_content" android:scaleType="fitXY" android:adjustViewBounds="true" android:src="@drawable/image" />

但是,例如如下代碼是不行的,顯示的圖片可能會出現變形,變形的圖片寬度爲原始圖片的寬度,而高度被強制縮放成「30dp」:
例1-2git

<ImageView  android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="30dp" android:scaleType="fitXY" android:adjustViewBounds="true" android:src="@drawable/image" />

或者用 BitmapScaler 這個工具類。相關方法例如如下:github

public class BitmapScaler
{
    // Scale and maintain aspect ratio given a desired width
    // BitmapScaler.scaleToFitWidth(bitmap, 100);
    public static Bitmap scaleToFitWidth(Bitmap b, int width)
    {
        float factor = width / (float) b.getWidth();
        return Bitmap.createScaledBitmap(b, width, (int) (b.getHeight() * factor), true);
    }


    // Scale and maintain aspect ratio given a desired height
    // BitmapScaler.scaleToFitHeight(bitmap, 100);
    public static Bitmap scaleToFitHeight(Bitmap b, int height)
    {
        float factor = height / (float) b.getHeight();
        return Bitmap.createScaledBitmap(b, (int) (b.getWidth() * factor), height, true);
    }

    // ...
}

1. 概述


通常來講,咱們使用 SDK 內置的圖片控件展現圖片。這個圖片控件會本身考慮載入和優化問題。使得開發人員不少其它的考慮應用相關的佈局和內容等細節。markdown

這篇教程中,咱們將逐一解說怎樣使用 ImageView,怎樣操做 bitmap,各類分辨率的目錄以及其它內容。app

2. 用法


從最簡單的開始,ImageView 是佈局文件裏的一個控件。用途是在屏幕中展現圖片(或 drawable 資源文件)。ide

用法大概例如如下:svg

<ImageView  android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="center" android:src="@drawable/my_image" />

ImageView 會爲你處理全部的載入和縮放問題。注意,scaleType 屬性決定圖片圖片怎樣縮放以適應你的佈局。在上例中,scaleType = 「center」 使得圖片以原始分辨率和居中的方式展現,而不考慮控件佔用多大的空間。

3. 尺寸控制


一般條件下,ImageView 要控制的對象是圖片的寬高,即 layout_width 和 layout_height:

<ImageView  android:layout_width="50dp" android:layout_height="50dp" android:scaleType="fitXY" />

樣例中的 scaleType=」fitXY」 表示將寬高均縮放到指定的長度「50dp」。

Fixing the width and height however means that the proportions of the width and height of the original image, known as the aspect ratio, will be altered. We can take advantage of the adjustViewBounds parameter to preserve this aspect ratio. However, we must either allow the height and/or width to be adjustable (i.e. by using maxWidth and using wrap_content for the dimension). Otherwise, the dimensions cannot be readjusted to meet the required aspect ratio.

然而,固定的寬高會改變原始圖片的寬高比,而使用 adjustViewBounds 參數可以保持寬高比。必須將寬度或高度設置爲可調節的(好比使用 maxWidth 和 wrap_content)。不然沒法保持原始圖片的寬高比。

<ImageView  android:layout_width="50dp" android:layout_height="wrap_content" android:scaleType="fitXY" android:adjustViewBounds="true" />

經過這樣的將各類屬性組合使用的方式咱們可以控制圖片的大體尺寸和寬高比例。

一樣咱們可以用 Java 代碼經過 getLayoutParams() 動態的更改 ImageView 的寬高:

imageView.getLayoutParams().height = 100; // OR
imageView.getLayoutParams().width = 100;

某些場景下,圖片需要縮放以適應父view的寬度。同一時候要求圖片高度等比例縮放。

咱們可以使用連接中的 ResizableImageView 來達到目的。

4. 縮放類型


ImageView 依據 scaleType 的類型來展現圖片。

上文中咱們探討了 fitXY 和 adjustViewBounds。如下是常用的 scaleType 的一覽表:

(注意:

  • 如下規則僅符合 layout_width、layout_height、scaleType 三個屬性的共同做用,而未考慮 adjustViewBounds 等屬性;
  • 紙上來的終覺淺,牆裂建議親手試試,經過 SDK 自帶的 layout 實時預覽功能來查看不一樣的 scaleType 屬性值與各類 layout_width、layout_height 搭配使用時的效果。便於理解和記憶;
  • 儘管 fitXY 可以讓圖片全然依照 layout 的尺寸來顯示,但是假設原始圖片和 ImageView 的寬高比不一致則會致使終於顯示的圖片變形。假設你在開發圖片瀏覽類型的應用,推薦使用 center 或 fitCenter 類型。)
scaleType 描寫敘述
center 將圖片中心與 ImageView 的中心重合,保持圖片的原始尺寸,僅僅顯示落在 ImageView 內部的圖片部分
centerCrop 將圖片中心與 ImageView 的中心重合,而後進行等比例縮放。直到圖片寬高均不小於 ImageView 的寬高,位於 ImageView 以外的圖片部分不顯示,縮放過程當中保持圖片寬高比
centerInside 將圖片中心與 ImageView 的中心重合,假設圖片全部位於 ImageView 內部,則完畢,不然對圖片等比例縮小直到圖片寬高均不大於 ImageView 的寬高
fitCenter 將圖片中心與 ImageView 的中心重合。而後等比例進行縮放,直到圖片寬高均不大於 ImageView 的寬高;fitCenter 與 centerInside 的差異在於:當圖片原始尺寸小於 ImageView 時,後者不正確圖片作不論什麼處理而前者對圖片進行等比例放大直至其寬高至少有一項與 ImageView 相應的寬高相等
fitStart 將圖片左上角與 ImageView 的左上角重合。其他和 fitCenter 一樣
fitEnd 將圖片右下角與 ImageView 的右下角重合,其他和 fitCenter 一樣
fitXY 將圖片的寬高縮放到與 ImageView 的寬高全然一樣;縮放過程全然無視原始圖片的寬高比
matrix 依據 setImageMatrix() 方法設置的 Matrix 類來顯示圖片,Matrix 類可以用來實現圖片翻轉等效果


這裏寫圖片描寫敘述

上圖相應的 android:scaleType 值:
第一行(從左至右)依次是 center, centerCrop, centerInside;
第二行(從左至右)依次是 fitCenter, fitStart, fitEnd, fitXY。

由上述圖表可以看出,scaleType 可分爲 3 大類:center,fit。matrix:

  • 以 center 開頭的值表示將圖片中心和 ImageView 中心重合。而後保持原始尺寸(center)或將圖片縮放到圈在 ImageView 內部(centerInside)或將圖片放大到覆蓋 ImageView(centerCrop);
  • 以 fit 開頭的值表示將圖片的寬高縮放到至少有一項與 ImageView 的寬高一樣,依據兩方對準的起點分爲中心(fitCenter)、左上角(fitStart)、右下角(fitEnd)或無起點(fitXY);
scaleType 圖片和ImageView中心點重合 保持圖片原始尺寸 保持圖片寬高比 顯示圖片與 ImageView 大比較
center 同圖片原始尺寸和 ImageView 大小關係
centerCrop x >=
centerInside x <=
fitCenter x <=
fitStart x x <=
fitEnd x x <=
fitXY x x =


還不明確?看下視頻解說吧:安卓 ImageView scaleType 屬性值解析

5. 支持多種屏幕分辨率


因爲安卓設備的屏幕尺寸、分辨率類型繁雜,需要有一個能本身主動爲設備匹配合適的資源的強大系統。安卓project目錄和安裝包中有專門爲不一樣類型的設備準備的資源目錄,包含:ldpi(低分辨率)。mdpi(medium dots per inch。中分辨率),hdpi(高分辨率)。xhdpi(超高分辨率)。這些資源目錄以 drawable-mdip 的形式命名。
爲了適配多種分辨率,咱們要依照 3:4:6:8 的比例來爲上述資源目錄設計圖片文件。詳細例如如下:

分辨率 DPI 演示樣例設備 換算比例 像素 備註
ldpi 120 Galaxy Y 0.75x 1dp = 0.75px
mdpi 160 Galaxy Tab 1.0x 1dp = 1px
hdpi 240 Galaxy S II 1.5x 1dp = 1.5px
xhdpi 320 Nexus 4 2.0x 1dp = 2px 720p
xxhdpi 480 Nexus 5 3.0x 1dp = 3px 1080p
xxxhdpi 640 Nexus 6 4.0x 1dp = 4px

這意味着。假設你在 drawable-mdpi 目錄中有一張 100x100 的圖片。那麼你要在 drawable-ldpi 文件裏放置 75x75 的圖片,drawable-hdpi 文件裏放置 150x150 的圖片,在 drawable-xhdpi 中放置 200x200的。在 drawable-xxhdpi 中放置 300x300 的圖片。

分辨率

Final Android Resizer 可以讓你方便的對圖片進行縮放:
這裏寫圖片描寫敘述

這個便捷的工具可以讓咱們選擇 drawable-xxhdpi 目錄中的一張圖片,本身主動的在相應的分辨率的資源目錄中生成相應分辨率的圖片。

不少其它支持多分辨率的知識請移步安卓支持多屏幕圖標設計官方文檔。

6. 紋理和資源


從 4.3 系統開始,安卓提供一個 res/mipmap 的目錄,用來存儲紋理圖片。紋理最大的用處是應用圖標,如啓動圖標等。關於紋理的優勢請移步這篇博文:紋理資源文件

紋理圖片資源經過 @mipmap/ic_launcher 的方式引用。將圖標放在 res/mipmap 目錄(而不是 drawable 目錄)是最優的做法,因爲這些圖標會在與其它分辨率下常用。

For example, an xxxhdpi app icon might be used on the launcher for an xxhdpi device. Review this post about preparing for the Nexus 6 which explains in more detail。

7. 使用 Bitmap


咱們可以在 Java 代碼中設置 ImageView 展現的圖片:

ImageView image = (ImageView) findViewById(R.id.test_image);
image.setImageResource(R.drawable.test2);

或者:

ImageView image = (ImageView) findViewById(R.id.test_image);
Bitmap bMap = BitmapFactory.decodeFile("/sdcard/test2.png");
image.setImageBitmap(bMap);

8. 縮放 Bitmap


你可以調用 createScaleBitmap() 方法來調整 Bitmap 的尺寸:

// Load a bitmap from the drawable folder
Bitmap bMap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image);

// Resize the bitmap to 150x100 (width x height)
Bitmap bMapScaled = Bitmap.createScaledBitmap(bMap, 150, 100, true);

// Loads the resized Bitmap into an ImageView
ImageView image = (ImageView) findViewById(R.id.test_image);
image.setImageBitmap(bMapScaled);

假設要等比例縮放 Bitmap。可以用 BitmapScaler 這個工具類,代碼例如如下:

public class BitmapScaler
{
    // Scale and maintain aspect ratio given a desired width
    // BitmapScaler.scaleToFitWidth(bitmap, 100);
    public static Bitmap scaleToFitWidth(Bitmap b, int width)
    {
        float factor = width / (float) b.getWidth();
        return Bitmap.createScaledBitmap(b, width, (int) (b.getHeight() * factor), true);
    }


    // Scale and maintain aspect ratio given a desired height
    // BitmapScaler.scaleToFitHeight(bitmap, 100);
    public static Bitmap scaleToFitHeight(Bitmap b, int height)
    {
        float factor = height / (float) b.getHeight();
        return Bitmap.createScaledBitmap(b, (int) (b.getWidth() * factor), height, true);
    }

    // ...
}

其它場景下,你可能想要依據屏幕的高或寬來縮放圖片。將 DeviceDimensionsHelper.java 這個工具類複製到你的project中,而後在 Context 中使用例如如下代碼:

// Get height or width of screen at runtime
int screenWidth = DeviceDimensionsHelper.getDisplayWidth(this);
// Resize a Bitmap maintaining aspect ratio based on screen width
BitmapScaler.scaleToFitWidth(bitmap, screenWidth);

關於這個工具類的不少其它知識請戳我

注意:不論什麼形式的圖片縮放都會形成圖片 EXIF 元數據的丟失,包含相機。旋轉。拍攝時間等。儘管有解決方式能將這些數據轉移,但現在仍然有缺陷。假設你需要這些元數據或者想連同元數據一塊兒上傳到server,那麼你應該上傳原始文件而非處理後的文件。

9. SVG 格式圖片


經過 svg-android 這個第三方庫咱們可以使用屏幕分辨率無關的 SVG 圖片。

// Parse an SVG resource from the "raw" resource folder
SVG svg = SVGParser.getSVGFromResource(getResources(), R.raw.android);

// Fix issue with renderer on certain devices
myImageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

// Get a drawable from the parsed SVG and set it as the drawable for the ImageView
myImageView.setImageDrawable(svg.createPictureDrawable());

不少其它知識詳見官方文檔

10. 參考文獻


相關文章
相關標籤/搜索