如果你有一个计算器应用程序,并且想要编写一个看起来像this的布局,那么如何缩放按钮和显示以适合所有屏幕尺寸?
我已经研究过的想法:
以编程方式计算每个组件的高度和宽度。以编程方式创建视图可以为您提供最大的功能,但并不完全理想。我更喜欢用XML编写我的UI。
使用布局权重嵌套LinearLayouts。这是有效的,但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>
最终结果:
答案 0 :(得分:3)
其他人建议的GridLayout
不够灵活,无法做到这一点。为此,请使用android:layout_weight
属性。这允许您根据指定的分数填充可用空间。
等权重的示例:
<LinearLayout android:layout_width="match_parent" android:layout_height="100dp">
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="A" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="B" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="C" />
</LinearLayout>
不同重量的示例:
<LinearLayout android:layout_width="match_parent" android:layout_height="100dp">
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="A" />
<Button android:layout_weight="2" android:layout_width="match_parent" android:layout_height="match_parent" android:text="B" />
<Button android:layout_weight="2" android:layout_width="match_parent" android:layout_height="match_parent" android:text="C" />
</LinearLayout>
更复杂的例子
以下是使用多个LinearLayout
s的计算器应用中使用的布局的更复杂示例:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<LinearLayout android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent">
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="7" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="8" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="9" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="x" />
</LinearLayout>
<LinearLayout android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent">
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="4" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="5" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="6" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="-" />
</LinearLayout>
<LinearLayout android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent">
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="1" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="2" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="3" />
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="+" />
</LinearLayout>
<LinearLayout android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent">
<Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="0" />
<Button android:layout_weight="1.5" android:layout_width="match_parent" android:layout_height="match_parent" android:text="." />
<Button android:layout_weight="1.5" android:layout_width="match_parent" android:layout_height="match_parent" android:text="=" />
</LinearLayout>
</LinearLayout>
答案 1 :(得分:2)
您可以自己编写一个ViewGroup
子类,它可以完成您想要的操作,并且仍然可以在 XML 布局定义中使用此布局,就像使用任何预定义布局一样。或者,查看GridLayout
类。也许这个ViewGroup
实现已经完成了你想要的。 (http://developer.android.com/reference/android/widget/GridLayout.html)最后,这些ViewGroup
布局以编程方式计算其包含的View
组件的大小,如果没有预定义的布局提供您所需的功能,除了实现您的功能之外别无他法。个人要求。
但是,按钮View
实例仍应负责将其内容保持在最近一次调用onMeasure
期间所收到的范围内。
使用示例图片将布局嵌套到扩展中,确实应该避免LinearLayout
类。应该指出的是,这仍然是一种常见的做法,因为这在使用TableLayout
时是隐含的。