3
votes

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.

1
What's strange about output? imageview's width matches parent's width (android:layout_width=match_parent && no padding) and the default value of scaleType is fitCenter, I don't get what you intend to do and what you exactly expect.Farshad Tahmasbi
Here is where I'm confused: The width of the image (as drawn) is approximately half of the screen width. My log outputs are intended to reveal the exact dimensions as drawn, which is clearly different than the screen width. While the default value of scaleType is fitCenter, the intended purpose of my code is to change the rendering of this image such that it is no longer fitCenter. As I said in my post, my objective is closer to centerCrop except aligned to the top rather than the center.AndroidDev
So I'd rather not get bogged down on log outputs but instead focus on why my code isn't accomplishing the redraw of the image in the way I am hoping.AndroidDev
You could try setting android:layout_height="wrap_content" for imageviewebyt
@Alex unlike your assumption, width of imageview is not same as the drawn part, log drawable dimensions and you'll find it out yourself.Farshad Tahmasbi

1 Answers

3
votes

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