[翻譯]在API level 17以下時正確顯示適應邊界的ImageView

[翻譯]在APIlevel 17以下時正確顯示adjustViewBounds的ImageView

Correct the ImageView’s adjustViewBounds behaviour on API Level 17 and below with AdjustableImageView
原文地址:
http://inthecheesefactory.com/blog/correct-imageview-adjustviewbounds-with-adjustable-imageview/en

幾乎每一個應用都會有這麼一個需求:我想要很好的縮放一個ImageView讓它適應它的父容器,我該怎麼辦?就像下面這樣
adjust

實際上ImageView已經提供了這種能力,你可以簡單的設定android:adjustViewBounds 爲 true即可。

<ImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:src="@mipmap/ic_launcher" />

下面是顯示效果:
Image2

一切看起來很完美? 實際上不是,如果你切換你的預覽版本爲API level17 或者更低,你會看到ImageView根本沒縮放。

image3

這不是bug。官方文檔裏有如下說明

注意:如果應用Target API level 小於等於17,adjustViewBounds會讓drawable收縮到圖片的邊界,而不會拉伸到填充可用的測量空間。這是爲了和過去的MeasureSpec 和 RelativeLayout行爲保持兼容

這就意味着,小於等於API Level 17, 最大寬度和最大高度會和android:src定義的圖片源一致,於是,圖片就會像上面展示的一樣。

看一下Android平臺版本統計就會發現,Android手機幾乎有一半是小於等於API level 17的。
versions

把minSdkVersion設定爲18顯然不是一個解決問題的好主意。

修改ImageView的源碼來讓它表現和API level 18+ 一致的行爲顯然更好,使用一個自定義的ImageView來替代普通的ImageView。代碼如下:

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ImageView;

/**
 * Created by nuuneoi on 2/17/15 AD.
 */
public class AdjustableImageView extends ImageView {

    boolean mAdjustViewBounds;

    public AdjustableImageView(Context context) {
        super(context);
    }

    public AdjustableImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public AdjustableImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setAdjustViewBounds(boolean adjustViewBounds) {
        mAdjustViewBounds = adjustViewBounds;
        super.setAdjustViewBounds(adjustViewBounds);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Drawable mDrawable = getDrawable();
        if (mDrawable == null) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        if (mAdjustViewBounds) {
            int mDrawableWidth = mDrawable.getIntrinsicWidth();
            int mDrawableHeight = mDrawable.getIntrinsicHeight();
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);

            if (heightMode == MeasureSpec.EXACTLY && widthMode != MeasureSpec.EXACTLY) {
                // Fixed Height & Adjustable Width
                int height = heightSize;
                int width = height * mDrawableWidth / mDrawableHeight;
                if (isInScrollingContainer())
                    setMeasuredDimension(width, height);
                else
                    setMeasuredDimension(Math.min(width, widthSize), Math.min(height, heightSize));
            } else if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) {
                // Fixed Width & Adjustable Height
                int width = widthSize;
                int height = width * mDrawableHeight / mDrawableWidth;
                if (isInScrollingContainer())
                    setMeasuredDimension(width, height);
                else
                    setMeasuredDimension(Math.min(width, widthSize), Math.min(height, heightSize));
            } else {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    private boolean isInScrollingContainer() {
        ViewParent p = getParent();
        while (p != null && p instanceof ViewGroup) {
            if (((ViewGroup) p).shouldDelayChildPressedState()) {
                return true;
            }
            p = p.getParent();
        }
        return false;
    }
}

這些代碼的工作原理很直接,在onMeasure方法中,如果寬度固定了(拉伸到父控件允許的最大),則計算等比例下的高度是多少,反之亦然。如果這個AdjustableImageView對象被放置在了一個非滾動的容器中,寬高將會被限定到父控件剩下的空間,否則,它將會無限制的放大。

在layout的xml文件中使用com.inthecheesefactory.thecheeselibrary.widget.AdjustableImageView 代替ImageView即可使用。

<com.inthecheesefactory.thecheeselibrary.widget.AdjustableImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:src="@mipmap/ic_launcher" />

使用AdjustableImageView庫來簡化操作

(譯者竊以爲如果你使用Eclipse或者網絡條件一般,還是直接拷貝代碼到工程中吧)
我們知道創建一個文件,拷貝代碼,粘貼,reformat。檢查是不是正確… 這些很煩人的。

使用我給你準備的依賴庫讓生活更簡單,依賴庫目前在Jcentor可以找到,一旦你添加依賴到工程,AdjustableImageView和AdjustableImageButton可以直接使用,源碼上傳到了GitHub.

下面是gradle依賴,只需要在build.gradle文件中添加如下:

dependencies {
    compile 'com.inthecheesefactory.thecheeselibrary:adjustable-imageview:1.0.0'
}

AdjustableImageView 和AdjustableImageButton 可以在AdjustableImageView and AdjustableImageButton包中找到。

只需要使用AdjustableImageView 和AdjustableImageButton替代項目中的ImageView 和 ImageButton。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">

        <LinearLayout android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <com.inthecheesefactory.thecheeselibrary.widget.AdjustableImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:adjustViewBounds="true"
                android:src="@mipmap/ic_launcher"/>

            <com.inthecheesefactory.thecheeselibrary.widget.AdjustableImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:adjustViewBounds="true"
                android:src="@mipmap/ic_launcher"/>
        </LinearLayout>

    </ScrollView>

</LinearLayout>

這樣,ImageView現在可以完美的放大,無論你使用什麼Android版本。
image5

這也是爲什麼我們應該在電腦裏安裝多版本的SDK,而不是隻安裝最近的一個。一旦你想要讓你的Android Studio在不同版本下預覽你的layout。你就得安裝那個版本的SDK Platform,否則它不會作爲一個選擇出現在你的預覽面板上。我建議你應該安裝從API level 14開始的每一個SDK Platform。 雖然損失一點硬盤空間,但是回報是值得的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章