ImageView 的比例类型意外行为

ImageView's scale type unexpected behavior

我有一个带有 scaleType="fitCenter"layout_width="match_parent" 的 ImageView,所以我希望其中的图像能够缩放和膨胀所有视图。

布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="20dp"
    android:layout_marginTop="20dp"
    android:gravity="center"
    android:orientation="vertical">

<FrameLayout
    android:layout_width="250dp"
    android:layout_height="wrap_content">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:src="@drawable/border" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:padding="6dp">

        <ImageView
            android:id="@+id/myImageView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:scaleType="fitCenter"
            android:src="@drawable/aaa"
            android:transitionName="image" />

        <!-- Some other views... -->
    </RelativeLayout>
</FrameLayout>

当图像太小(例如 50x50 像素)并且无法在所有设备上按预期工作时,问题就开始了。

下图是API16和22的区别

有什么解决办法吗?

使用 android:scaleType="fitXY" 使用您正在寻找的 FILL 缩放图像。

如果您使用 fitXy,图像会在两个维度上填充视图,而不管其宽高比。这无疑会造成失真。

另一种选择是centerCrop。此缩放类型将重新缩放图像,直到 x 或 y 的两个维度都大于或等于容器,同时保持纵横比。结果将在视图内居中。

fitCenter 与 centerCrop 相反。它缩放图像直到至少一个维度 x 或 y 小于或等于视图。

也就是说,您在屏幕截图中显示的行为更多地与屏幕尺寸有关,而不是 api 级别。

问题不在于您的代码,而在于 android:adjustViewBounds 的工作方式。这记录在 ImageViewsetAdjustViewBounds 方法中。

Note:If the application targets API level 17 or lower, adjustViewBounds will allow the drawable to shrink the view bounds, but not grow to fill available measured space in all cases.

基于 post Correct the ImageView's adjustViewBounds behaviour on API Level 17 and below with AdjustableImageView by nuuneoi, the possible solution for this issue is to use custom AdjustableImageView.

代码:

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;
    }
}