我阅读了这个帖子并意识到我想要一个比线性布局更扁平的解决方案。经过一番研究,我最终制作了自己的布局。它受到 GridLayout 的启发,但略有不同。
请注意,如果您要复制粘贴代码,则需要在某些地方更改包名称。
这个布局有 4 个布局参数,孩子们用来定位自己。这些是layout_left、layout_top、layout_right、layout_bottom。ICGridLayout 本身有两个属性:layout_spacing 和 columns。
Columns告诉布局您希望它包含多少列。然后它将计算高度与宽度相同的单元格的大小。这将是布局宽度/列。
间距是您希望每个孩子之间的空间量。
layout_left|top|right|bottom 属性是每一边的坐标。布局不进行计算以避免碰撞或任何事情。它只是把孩子放在他们想去的地方。
如果你想要更小的方块,你只需要增加 columns 属性。
请记住,这是一个快速原型,我会继续努力,当我觉得它准备好时,我会将它上传到 Github 并在这里发表评论。
我下面的所有代码都应该产生以下结果:
***** 编辑***** 添加了为孩子们测量的调用,第一次忘记了。
结束编辑
ICGridLayout.java:
package com.risch.evertsson.iclib.layout;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.risch.evertsson.iclib.R;
/**
* Created by johanrisch on 6/13/13.
*/
public class ICGridLayout extends ViewGroup {
private int mColumns = 4;
private float mSpacing;
public ICGridLayout(Context context) {
super(context);
}
public ICGridLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ICGridLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
TypedArray a = getContext().obtainStyledAttributes(
attrs,
R.styleable.ICGridLayout_Layout);
this.mColumns = a.getInt(R.styleable.ICGridLayout_Layout_columns, 3);
this.mSpacing = a.getDimension(R.styleable.ICGridLayout_Layout_layout_spacing, 0);
a.recycle();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int width = (int) (r - l);
int side = width / mColumns;
int children = getChildCount();
View child = null;
for (int i = 0; i < children; i++) {
child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int left = (int) (lp.left * side + mSpacing / 2);
int right = (int) (lp.right * side - mSpacing / 2);
int top = (int) (lp.top * side + mSpacing / 2);
int bottom = (int) (lp.bottom * side - mSpacing / 2);
child.layout(left, top, right, bottom);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
}
private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = 0;
int height = 0;
if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.EXACTLY) {
width = MeasureSpec.getSize(widthMeasureSpec);
} else {
throw new RuntimeException("widthMeasureSpec must be AT_MOST or " +
"EXACTLY not UNSPECIFIED when orientation == VERTICAL");
}
View child = null;
int row = 0;
int side = width / mColumns;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.bottom > row) {
row = lp.bottom;
}
int childHeight = (lp.bottom - lp.top)*side;
int childWidth = (lp.right-lp.left)*side;
int heightSpec = MeasureSpec.makeMeasureSpec(childHeight, LayoutParams.MATCH_PARENT);
int widthSpec = MeasureSpec.makeMeasureSpec(childWidth, LayoutParams.MATCH_PARENT);
child.measure(widthSpec, heightSpec);
}
height = row * side;
// TODO: Figure out a good way to use the heightMeasureSpec...
setMeasuredDimension(width, height);
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new ICGridLayout.LayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof ICGridLayout.LayoutParams;
}
@Override
protected ViewGroup.LayoutParams
generateLayoutParams(ViewGroup.LayoutParams p) {
return new ICGridLayout.LayoutParams(p);
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams();
}
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
int right = 1;
int bottom = 1;
int top = 0;
int left = 0;
int width = -1;
int height = -1;
public LayoutParams() {
super(MATCH_PARENT, MATCH_PARENT);
top = 0;
left = 1;
}
public LayoutParams(int width, int height) {
super(width, height);
top = 0;
left = 1;
}
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(
attrs,
R.styleable.ICGridLayout_Layout);
left = a.getInt(R.styleable.ICGridLayout_Layout_layout_left, 0);
top = a.getInt(R.styleable.ICGridLayout_Layout_layout_top, 0);
right = a.getInt(R.styleable.ICGridLayout_Layout_layout_right, left + 1);
bottom = a.getInt(R.styleable.ICGridLayout_Layout_layout_bottom, top + 1);
height = a.getInt(R.styleable.ICGridLayout_Layout_layout_row_span, -1);
width = a.getInt(R.styleable.ICGridLayout_Layout_layout_col_span, -1);
if (height != -1) {
bottom = top + height;
}
if (width != -1) {
right = left + width;
}
a.recycle();
}
public LayoutParams(ViewGroup.LayoutParams params) {
super(params);
}
}
}
ICGridLayout.java 非常简单。它采用孩子们提供的价值观并将其展示出来。attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ICGridLayout_Layout">
<attr name="columns" format="integer"/>
<attr name="layout_left" format="integer"/>
<attr name="layout_top" format="integer"/>
<attr name="layout_right" format="integer"/>
<attr name="layout_bottom" format="integer"/>
<attr name="layout_col_span" format="integer"/>
<attr name="layout_row_span" format="integer"/>
<attr name="layout_spacing" format="dimension"/>
</declare-styleable>
</resources>
example_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.rischit.projectlogger"
android:id="@+id/scroller"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.risch.evertsson.iclib.layout.ICGridLayout
android:id="@+id/ICGridLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_spacing="4dp"
app:columns="4" >
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_bottom="1"
app:layout_left="0"
app:layout_right="4"
app:layout_top="0"
android:background="#ff0000"
android:text="TextView" />
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_bottom="3"
app:layout_left="3"
app:layout_right="4"
app:layout_top="1"
android:background="#00ff00"
android:text="TextView" />
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_bottom="4"
app:layout_left="0"
app:layout_right="3"
app:layout_top="1"
android:background="#0000ff"
android:text="TextView" />
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_bottom="4"
app:layout_left="3"
app:layout_right="4"
app:layout_top="3"
android:background="#ffff00"
android:text="TextView" />
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_bottom="6"
app:layout_left="0"
app:layout_right="1"
app:layout_top="4"
android:background="#ff00ff"
android:text="TextView" />
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_bottom="6"
app:layout_left="1"
app:layout_right="4"
app:layout_top="4"
android:background="#ffffff"
android:text="TextView" />
</com.risch.evertsson.iclib.layout.ICGridLayout>
</ScrollView>
——约翰·里施
PS这是我的第一个长答案,我试图以正确的方式做到这一点。如果我失败了,请告诉我不要发火:) DS