Beerbossa Beerbossa - 11 days ago 5
Android Question

Android XML - Have an ImageView fill height while maintaining ANY width ratio

I'm building an Android application and am now designing the user profile activity.

It is supposed to display the profile picture at the top in a rectangle container that looks like a banner.

<ImageView
android:id="@+id/user_pic"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:layout_centerHorizontal="true"
android:elevation="2dp"/>


This XML code works well if the image is a 1x1 ratio or if the height is bigger than the width. (I display a blurry and stretched version of the same image in background so it's not completely ugly).

The problem I hit is when the WIDTH is bigger than the HEIGHT. It tries to scale the height (in my case to 300dp) but then the width hits the borders of the layout and it stops resizing, leaving me with an empty space under the image.

I know that the scaleType="centerCrop" does pretty much what I'm looking for, but if the ratio of the image is 1x1 it's cropping the top part of the image (most of the time the hairs) of the person, which I don't want.

So basically, is there a way in XML, or in JAVA, to resize the height of an image to fill my container while keeping the width ratio and ignoring the max width of the parent layout ? (I imagine it's supposed to crop what would overflow just like "centerCrop" would).

Here is an image of what I'm currently acheiving and the problem occuring. The grey part is a blurry and resized version of the same Image, so it doesn't look too ugly when the image can't fill the width (for example, in a 1x1 ratio picture).
https://i.stack.imgur.com/FUDcP.png

Answer

The answer was found at this link.

Basically, the trick used is to make a custom ImageView class in Java, then use it. It will calculate the correct area to crop, if necessary, depending of the situation.

If anyone finds a pure XML solution, feel free to tell me !

Without any further wait, here is the code I used.

JAVA CLASS

public class FitYCropXImageView extends ImageView {
    boolean done = false;

    @SuppressWarnings("UnusedDeclaration")
    public FitYCropXImageView(Context context) {
        super(context);
        setScaleType(ScaleType.MATRIX);
    }

    @SuppressWarnings("UnusedDeclaration")
    public FitYCropXImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setScaleType(ScaleType.MATRIX);
    }

    @SuppressWarnings("UnusedDeclaration")
    public FitYCropXImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setScaleType(ScaleType.MATRIX);
    }

    private final RectF drawableRect = new RectF(0, 0, 0,0);
    private final RectF viewRect = new RectF(0, 0, 0,0);
    private final Matrix m = new Matrix();
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (done) {
            return;//Already fixed drawable scale
        }
        final Drawable d = getDrawable();
        if (d == null) {
            return;//No drawable to correct for
        }
        int viewHeight = getMeasuredHeight();
        int viewWidth = getMeasuredWidth();
        int drawableWidth = d.getIntrinsicWidth();
        int drawableHeight = d.getIntrinsicHeight();
        drawableRect.set(0, 0, drawableWidth, drawableHeight);//Represents the original image
        //Compute the left and right bounds for the scaled image
        float viewHalfWidth = viewWidth / 2;
        float scale = (float) viewHeight / (float) drawableHeight;
        float scaledWidth = drawableWidth * scale;
        float scaledHalfWidth = scaledWidth / 2;
        viewRect.set(viewHalfWidth - scaledHalfWidth, 0, viewHalfWidth + scaledHalfWidth, viewHeight);

        m.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.CENTER /* This constant doesn't matter? */);
        setImageMatrix(m);

        done = true;

        requestLayout();
    }
}

XML

<com.app.YourAppName.YourAppName.CustomViews.FitYCropXImageView
                        android:id="@+id/user_pic"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:background="@android:color/transparent"
                        android:elevation="2dp"
                        />

JAVA ACTIVITY IF NECESSARY

import com.app.YourAppName.YourAppName.CustomViews.FitYCropXImageView;

FitYCropXImageView profilePic = (FitYCropXImageView) findViewById(R.id.user_pic);

EDIT : For some reason, the FitYCropXImageView only seem to work correctly in certain activities if it is the child of a ScrollView, but I'm probably missing something.

Comments