Note that there are some explanatory texts on larger screens.

plurals
  1. POAndroid - How do you scale views to fit a full screen application's height and width perfectly?
    text
    copied!<p>If you have a calculator app and you want to write a layout that looks like <a href="https://i.stack.imgur.com/RBXwX.png" rel="nofollow noreferrer">this</a>, how do you scale the buttons and display to fit on all screen sizes? </p> <p>Ideas I have looked into:</p> <ol> <li><p>Programatically calculating the height and width of each component. Programatically creating the views gives you the most power, but is not exactly ideal. I'd prefer to write my UI in XML.</p></li> <li><p>Nesting LinearLayouts with Layout weights. This works, but lint gives performance warnings because I am nesting weights. On top of that, it does not take into account text size. So on small screens, text is chopped off. Conversely, on large screens, text is too small.</p></li> </ol> <p>EDIT: 3. Using a TableLayout with nested weights. Considering these extend from LinearLayout, I assume the lack of lint warnings is irrelevant, this is still going to cause a loss in performance right?</p> <p>Is there a better way? I feel like I am missing something obvious</p> <p><strong>EDIT 2: In case anyone is interested in the solution for this, I have created a custom layout (as raphw suggested) and will post the source code here:</strong></p> <p>EvenSpaceGridLayout.java:</p> <pre><code>package com.example.evenspacegridlayout; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; public class EvenSpaceGridLayout extends ViewGroup { private int mNumColumns; public EvenSpaceGridLayout(Context context) { super(context); } public EvenSpaceGridLayout(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EvenSpaceGridLayout); try { mNumColumns = a.getInteger( R.styleable.EvenSpaceGridLayout_num_columns, 1); } finally { a.recycle(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); // Calculate how many cells we need int cellCount = countCellsNeeded(); // Calculate number of rows needed given the number of cells int numRows = cellCount / mNumColumns; // Calculate width/height of each individual cell int cellWidth = widthSize / mNumColumns; int cellHeight = heightSize / numRows; // Measure children measureChildrenViews(cellWidth, cellHeight); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); for (int i = 0; i &lt; count; i++) { View child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight()); } } private int countCellsNeeded() { int cellCount = 0; final int childCount = getChildCount(); for (int i = 0; i &lt; childCount; i++) { View child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); int spanColumns = lp.spanColumns; // If it's trying to span too far, make it span the maximum possible if (spanColumns &gt; mNumColumns) { spanColumns = mNumColumns; } int remainingCellsInRow = mNumColumns - (cellCount % mNumColumns); if (remainingCellsInRow - spanColumns &lt; 0) { cellCount += remainingCellsInRow + spanColumns; } else { cellCount += spanColumns; } } // Round off the last row if ((cellCount % mNumColumns) != 0) { cellCount += mNumColumns - (cellCount % mNumColumns); } return cellCount; } private void measureChildrenViews(int cellWidth, int cellHeight) { int cellCount = 0; final int childCount = getChildCount(); for (int i = 0; i &lt; childCount; i++) { View child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); int spanColumns = lp.spanColumns; // If it's trying to span too far, make it span the maximum possible if (spanColumns &gt; mNumColumns) { spanColumns = mNumColumns; } // If it can't fit on the current row, skip those cells int remainingCellsInRow = mNumColumns - (cellCount % mNumColumns); if (remainingCellsInRow - spanColumns &lt; 0) { cellCount += remainingCellsInRow; } // Calculate x and y coordinates of the view int x = (cellCount % mNumColumns) * cellWidth; int y = (cellCount / mNumColumns) * cellHeight; lp.x = x; lp.y = y; child.measure(MeasureSpec.makeMeasureSpec(cellWidth * spanColumns, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(cellHeight, MeasureSpec.EXACTLY)); cellCount += spanColumns; } } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams; } @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } @Override protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(p.width, p.height); } public static class LayoutParams extends ViewGroup.LayoutParams { int x, y; public int spanColumns; public LayoutParams(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EvenSpaceGridLayout_LayoutParams); try { spanColumns = a .getInteger( R.styleable.EvenSpaceGridLayout_LayoutParams_span_columns, 1); // Can't span less than one column if (spanColumns &lt; 1) { spanColumns = 1; } } finally { a.recycle(); } } public LayoutParams(int w, int h) { super(w, h); } } } </code></pre> <p>attrs.xml:</p> <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;resources&gt; &lt;declare-styleable name="EvenSpaceGridLayout"&gt; &lt;attr name="num_columns" format="integer" /&gt; &lt;/declare-styleable&gt; &lt;declare-styleable name="EvenSpaceGridLayout_LayoutParams"&gt; &lt;attr name="span_columns" format="integer" /&gt; &lt;/declare-styleable&gt; &lt;/resources&gt; </code></pre> <p>Usage as follows:</p> <pre><code>&lt;com.example.evenspacegridlayout.EvenSpaceGridLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:grid="http://schemas.android.com/apk/res/com.example.evenspacegridlayout" android:layout_width="match_parent" android:layout_height="match_parent" grid:num_columns="4" &gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="CL" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="Del" /&gt; &lt;!-- empty cell --&gt; &lt;View android:layout_width="0dp" android:layout_height="0dp" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="/" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="7" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="8" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="9" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="*" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="4" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="5" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="6" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="-" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="1" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="2" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="3" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="+" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="." /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="0" /&gt; &lt;Button android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="=" grid:span_columns="2" /&gt; &lt;/com.example.evenspacegridlayout.EvenSpaceGridLayout&gt; </code></pre> <p>And the end result:</p> <p><img src="https://i.stack.imgur.com/XNdGb.png" alt="portrait"> <img src="https://i.stack.imgur.com/k8ycT.png" alt="landscape"></p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload