AndroidDev
AndroidDev

Reputation: 21237

Android Stretch ImageView to fill width, maintain aspect ratio, align top

I'm trying to expand an ImageView that sits inside a RelativeLayout. I want the width to fill the parent while still maintaining the aspect ratio of the image. This sounds an awful lot like I could just use centerCrop in the XML layout file. The difference is that I do not want the image center-aligned. What I want is the image to be aligned to the top of the parent, with any excess on the bottom of the image to be cropped.

Here is the XML:

<RelativeLayout
        android:background="#f00"
        android:id="@+id/skyline_header"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="25">

        <ImageView
            android:id="@+id/bg_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/bg_img"/>
</RelativeLayout>

And here is my code:

final RelativeLayout headerContainer = (RelativeLayout) view.findViewById(R.id.skyline_header);
final ImageView iv = (ImageView) view.findViewById(R.id.bg_image);

ViewTreeObserver vto = headerContainer.getViewTreeObserver();

vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {

        headerContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);

        double imgAspectRatio = (double)iv.getMeasuredWidth() / (double)iv.getMeasuredHeight();

        int screenWidth = getActivity().getResources().getDisplayMetrics().widthPixels;

        Log.d("tag", Integer.toString(iv.getMeasuredWidth()));
        Log.d("tag", Integer.toString(iv.getMeasuredHeight()));
        Log.d("tag", Double.toString(imgAspectRatio));
        Log.d("tag", Integer.toString(screenWidth));

        int newHeight = (int) ((double)screenWidth * imgAspectRatio);

        img.setLayoutParams(new RelativeLayout.Layout(screenWidth, newHeight));
        img.requestLayout();
    }
});

This code results in exactly what you would see if you just set scaleType="fitCenter". It fills the height of the container, centers the image, and leaves margins on the left and right.

The output is strange. It shows that the image view has the same width as the screen:

1440
562
2.5622775800711746
1440

Hopefully this will communicate conceptually what I have right now:

enter image description here

I want the picture to stretch to fill the entire horizontal space (no visible red remaining) without making the swimmer look really freakishly wide (i.e. keep the aspect ratio intact). The water underneath the swimmer can be cropped as needed to maintain the first two requirements.

Upvotes: 3

Views: 1585

Answers (1)

Farshad
Farshad

Reputation: 3140

Well, fortunately android API provides another scaleType: matrix, So you are free to show parts of image you want, add these lines of code to onCreate method of the Activity :

    final ImageView iv = (ImageView) findViewById(R.id.iv);
    iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int viewWidth = iv.getMeasuredWidth();
            int viewHeight = iv.getMeasuredHeight();
            Drawable src = iv.getDrawable();
            if (src != null) {
                int srcWidth = src.getIntrinsicWidth();
                int srcHeight = src.getIntrinsicHeight();
                float wRatio = (float) viewWidth / srcWidth;
                float hRatio = (float) viewHeight / srcHeight;
                float scaleFactor = Math.max(wRatio, hRatio);
                float hTranslate = (viewWidth - scaleFactor * srcWidth) / 2;
                Matrix matrix = new Matrix();
                matrix.setScale(scaleFactor, scaleFactor);
                matrix.postTranslate(hTranslate, 0);
                iv.setScaleType(ImageView.ScaleType.MATRIX);
                iv.setImageMatrix(matrix);

                Log.i("test", "srcWidth: " + srcWidth);
                Log.i("test", "srcHeight: " + srcHeight);
                Log.i("test", "viewWidth: " + viewWidth);
                Log.i("test", "viewHeight: " + viewHeight);
                Log.i("test", "wRatio: " + wRatio);
                Log.i("test", "hRatio: " + hRatio);
                Log.i("test", "scaleFactor: " + scaleFactor);
                Log.i("test", "hTranslate: " + hTranslate);
            }
            iv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
    });

first you register an OnGlobalLayoutListener so you will access the dimension of the view in the right time (I believe you already know it), then you compute wRatio(width ratio) and hRatio(height ratio) using provided parameters and choose the bigger one as the scaleFactor (so you make sure that drawable covers whole of the view), Also you need to align drawable center horizontally (that's the role hTranslate plays), finally create the matrix and we're there!

Original image :

enter image description here

centerCrop result:

enter image description here

topCrop result (what we did by code)

enter image description here

Upvotes: 3

Related Questions