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 980x300 put on the 480px width screen, the height should scale to 146.9388px, but "adjustViewBounds" floored it to 146px. As a result, for the 980x300 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 *