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.
<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.
<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?
<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.
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)); } }