如果你有一个计算器应用程序并且你想编写一个看起来像这样的布局,你如何缩放按钮和显示以适应所有屏幕尺寸?
我研究过的想法:
以编程方式计算每个组件的高度和宽度。以编程方式创建视图可为您提供最大的功能,但并不完全理想。我更喜欢用 XML 编写我的 UI。
使用布局权重嵌套线性布局。这可行,但 lint 会发出性能警告,因为我正在嵌套权重。最重要的是,它不考虑文本大小。所以在小屏幕上,文本被截断。相反,在大屏幕上,文本太小。
编辑: 3. 使用带有嵌套权重的 TableLayout。考虑到 LinearLayout 的这些扩展,我认为缺少 lint 警告是无关紧要的,这仍然会导致性能损失,对吗?
有没有更好的办法?我觉得我错过了一些明显的东西
编辑 2:如果有人对此解决方案感兴趣,我创建了一个自定义布局(如 raphw 建议的那样)并将在此处发布源代码:
EvenSpaceGridLayout.java:
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 < 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 < 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 > mNumColumns) {
spanColumns = mNumColumns;
}
int remainingCellsInRow = mNumColumns - (cellCount % mNumColumns);
if (remainingCellsInRow - spanColumns < 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 < 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 > mNumColumns) {
spanColumns = mNumColumns;
}
// If it can't fit on the current row, skip those cells
int remainingCellsInRow = mNumColumns - (cellCount % mNumColumns);
if (remainingCellsInRow - spanColumns < 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 < 1) {
spanColumns = 1;
}
} finally {
a.recycle();
}
}
public LayoutParams(int w, int h) {
super(w, h);
}
}
}
attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="EvenSpaceGridLayout">
<attr name="num_columns" format="integer" />
</declare-styleable>
<declare-styleable name="EvenSpaceGridLayout_LayoutParams">
<attr name="span_columns" format="integer" />
</declare-styleable>
</resources>
用法如下:
<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" >
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="CL" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Del" />
<!-- empty cell -->
<View
android:layout_width="0dp"
android:layout_height="0dp" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="/" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="7" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="8" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="9" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="*" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="4" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="5" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="6" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="-" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="1" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="2" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="3" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="+" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="." />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="0" />
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="="
grid:span_columns="2" />
</com.example.evenspacegridlayout.EvenSpaceGridLayout>
最终结果: