Pavlo Zin
Pavlo Zin

Reputation: 790

ImageView's scale type unexpected behavior

I have an ImageView with scaleType="fitCenter" and layout_width="match_parent", so I expect the image inside it to scale and inflate all view.

Layout file:

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

The problem begins when images are too small (for example, 50x50 px) and it doesn't work as expected on all devices.

The image below shows the difference between API 16 and 22.

screenshot

Any ideas how to resolve this?

Upvotes: 1

Views: 1066

Answers (3)

blizzard
blizzard

Reputation: 5375

The problem is not with your code but with the way android:adjustViewBounds work. This is documented in setAdjustViewBounds method of ImageView.

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.

Based on the 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.

Code:

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

Upvotes: 4

inmyth
inmyth

Reputation: 9050

If you use fitXy the image fills the view in both dimensions, disregarding its aspect ratio. This will undoubtedly cause distortion.

Another option is centerCrop. This scale type will rescale the image until both dimensions x or y are greater or equal to the container while preserving aspect ratio. The result will be centered inside the view.

fitCenter would be the opposite of centerCrop. It scales the image until at least one dimension x or y is smaller or equal to the view.

That said, the behavior that you show in the screenshots has more to do with screen sizes, not api level.

Upvotes: 0

petey
petey

Reputation: 17140

Use android:scaleType="fitXY" to Scale the image using FILL that you are looking for.

Upvotes: 0

Related Questions