Image aspect ratio kept & width fitting layout

By | September 7, 2012

For layout an Android app with well designed UI elements, I want to auto-fit all kinds of screen with different aspect ratios. I need to keep some parts of our app a fixed aspect ratio so that the art design would not be distorted, and other parts will fill the left space.

Let’s begin with a Title bar which has a distort-sensitive background and some buttons on it. After trying 3 different methods, I realized that without Java code, I just can’t do such a simple job!

First, I tried to use a LinearLayout and set its background to the title bar image. But I was surprised on the effect it showed! Who would need such a distorted bar on different screens?! The document didn’t mention how “wrap_content” wraps background.

Bad!

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/test_title" >
    </LinearLayout>

Second, I tried ImageView but I got a much more height bound box as the height of the ImageView, so that it can’t align to the top, sadly.

Bad!

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/test_title" />

Third, after I searched over the Google and found an option called “adjustViewBounds” of the ImageView on stackoverflow, I almost reached the goal. But when I tried on some other screens, I found that the Android system just left 1 pixel on both left and right sides on some of them! I thought out the reason – just because the “adjustViewBounds” option floored the height to the integer(e.g. If an image sized 980×300 put on the 480px width screen, the height should scale to 146.9388px, but “adjustViewBounds” floored it to 146px. As a result, for the 980×300 background image, the final width will be 146*980/300=476.93px. That is 3.1px “shrink” to the screen width!). The width was scaled a little bit down! Oh, my god! Can’t Google guys just fit the width?

Bad!

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:src="@drawable/test_title" />

Now, I decide to extern the LinearLayout and control the measure process precisely.

Finally, I wrote a new subclass of the LinearLayout named “AspectKeptLinearLayout” which keeps its background drawable to a fixed original aspect ratio, and set its height to exactly the final scaled height that can be precisely divided by its children.

Good!

in test_layout.xml:

    <org.lcsky.libview.AspectKeptLinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/test_title" >
    </com.ywqc.libview.AspectKeptLinearLayout>

AspectKeptLinearLayout.java(in package org.lcsky):

public class AspectKeptLinearLayout extends LinearLayout {

	private final int mBgWidth;
	private final int mBgHeight;
	private ViewGroup.LayoutParams mLayoutParams = null;

	public AspectKeptLinearLayout(Context context, AttributeSet attrs) {
		super(context, attrs);

		Drawable bg = getBackground();
		mBgWidth = bg.getIntrinsicWidth();
		mBgHeight = bg.getIntrinsicHeight();
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		if (mLayoutParams == null) {
			mLayoutParams = getLayoutParams();
		}

		int width = 0;
		int height = 0;

		if ((mLayoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT ||
			 mLayoutParams.width == 0
			) &&
			mLayoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT)
		{
			width = MeasureSpec.getSize(widthMeasureSpec);
			height = width * mBgHeight / mBgWidth;
		} else {
			// You can extend the MATCH_PARENT of the height case
			throw new UnsupportedOperationException(
				"width="+mLayoutParams.width+" height="+mLayoutParams.height
			);
		}

		int mode = MeasureSpec.EXACTLY;
		super.onMeasure(MeasureSpec.makeMeasureSpec(width, mode),
				MeasureSpec.makeMeasureSpec(height, mode));
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *