包含图像的网格布局的对齐问题
Alignment issues with gridlayout containing images
Android Studio 1.2
你好,
我正在尝试使此对齐方式与以下线框正确对齐。
我只为此使用网格布局,不想使用线性或相对布局或嵌套布局。
我使用以下 xml 关闭了它。只需要一些帮助来做最后的润色,让它看起来更完美。
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightLarge"
android:columnCount="6"
android:orientation="horizontal"
android:padding="6dp">
<TextView
android:id="@+id/tvDate"
android:layout_gravity="start"
android:text="16 Mar 2015"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvCorrectTxt"
android:layout_gravity="end"
android:layout_columnSpan="2"
android:text="Correct 20"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivCorrect"
android:layout_columnSpan="2"
android:layout_gravity="top"
android:src="@drawable/ic_action_good"/>
<ImageView
android:id="@+id/ivArrow"
android:layout_rowSpan="2"
android:layout_gravity="center"
android:src="@drawable/ic_action_next_item"/>
<TextView
android:id="@+id/tvTime"
android:layout_gravity="start|bottom"
android:text="22:15"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvIncorrect"
android:layout_gravity="end|bottom"
android:layout_columnSpan="2"
android:text="Incorrect 18"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivIncorrect"
android:layout_gravity="bottom"
android:src="@drawable/ic_action_bad"/>
</GridLayout>
非常感谢您的任何建议,
我建议您尝试使用属性 columnWeight 来调整单元格的大小。我还建议为您的图片分配一个特定的尺寸。
看看我编辑的版本,它几乎给出了想要的结果! (是的,我使用了不同的图像资源;)):
这是XML:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightLarge"
android:columnCount="6"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:id="@+id/tvDate"
android:layout_gravity="start"
android:text="16 Mar 2015"
android:layout_columnWeight="1"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvCorrectTxt"
android:layout_gravity="end"
android:layout_columnSpan="2"
android:text="Correct 20"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivCorrect"
android:layout_columnSpan="2"
android:layout_gravity="center_horizontal|top"
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/ic_action_good"/>
<ImageView
android:id="@+id/ivArrow"
android:layout_rowSpan="2"
android:layout_columnWeight="0"
android:layout_gravity="center"
android:src="@drawable/ic_action_next_item"/>
<TextView
android:id="@+id/tvTime"
android:layout_columnWeight="1"
android:layout_gravity="start|bottom"
android:text="22:15"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvIncorrect"
android:layout_gravity="end|bottom"
android:layout_columnSpan="2"
android:layout_columnWeight="0"
android:text="Incorrect 18"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivIncorrect"
android:layout_columnWeight="2"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="center_horizontal|bottom"
android:src="@drawable/ic_action_bad"/>
</GridLayout>
您的布局(从图片中看)看起来相当静态 - 没有太多活动部件。为什么不将其实现为自定义 Viewgroup
?由于您追求性能,因此这就是要走的路。
child 视图的定位将相对于 ViewGroup
,而不是 children 本身。涉及数学:
列(child左 & child右)
- 将可用宽度 (actualWidth - paddingLeft - paddingRight) 分成 6 个等距的列
- TextViews tvDate 和 tvTime 向左刷新
- TextViews tvCorrectTxt 和 tvIncorrect 在第 4 列内刷新
- ivCorrect 和 ivIncorrect 在第 5 列内水平居中
- ivArrow 在第 6 列右侧刷新
行(child顶部和child底部)
- 我们有两行要显示。为了使行上方、行间和行下方的间距相等,我们将第一行的垂直中心与
container's height/3
==> y = 0.333*availableHeight
对齐;第二行的垂直中心将位于 2*container's height/3
==> y = 0.666*availableHeight
- ivArrow 将在可用高度内垂直居中。
CusViewGroup:
public class CusViewGroup extends ViewGroup {
// In case we have to explicitly set the viewgroup's height
private int mDesiredHeight;
public CusViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
// Attribute array
int[] attrss = new int[] { android.R.attr.listPreferredItemHeightLarge };
TypedArray a = context.getTheme().obtainStyledAttributes(attrss);
// 200 is being used as the default value.
// This should actually be defined in res/values/dimens.xml
// and retrieved using
// getResources.getDimensionPixelSize(R.dimen.defaultListItemHeight)
mDesiredHeight = a.getDimensionPixelSize(0, 200);
a.recycle();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
final int parentLeft = getPaddingStart();
final int parentRight = right - left - getPaddingEnd();
final int parentTop = getPaddingTop();
final int parentBottom = bottom - top - getPaddingBottom();
final int availableParentWidth = parentRight - parentLeft;
final int availableParentHeight = parentBottom - parentTop;
final int columnWidth = availableParentWidth/6;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft, childTop;
// We can use the assigned ids and calculate
// view positions.
switch(child.getId()) {
case R.id.tvDate:
childLeft = parentLeft;
childTop = parentTop + availableParentHeight/3 - height/2;
break;
case R.id.tvCorrectTxt:
childLeft = parentLeft + columnWidth*4 - width;
childTop = parentTop + availableParentHeight/3 - height/2;
break;
case R.id.ivCorrect:
childLeft = parentLeft + columnWidth*4 + (columnWidth - width) / 2;
childTop = parentTop + availableParentHeight/3 - height/2;
break;
case R.id.ivArrow:
childLeft = parentRight - width;
childTop = parentTop + (availableParentHeight - height) / 2;
break;
case R.id.tvTime:
childLeft = parentLeft;
childTop = parentTop + 2*availableParentHeight/3 - height/2;
break;
case R.id.tvIncorrect:
childLeft = parentLeft + columnWidth*4 - width;
childTop = parentTop + 2*availableParentHeight/3 - height/2;
break;
case R.id.ivIncorrect:
childLeft = parentLeft + columnWidth*4 + (columnWidth - width) / 2;
childTop = parentTop + 2*availableParentHeight/3 - height/2;
break;
default:
// We shouldn't be here
child.setVisibility(View.GONE);
continue;
}
// layout child
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// We're using an exact value for height & MATCH_PARENT for width
int height = getMeasuredHeight();
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
// Use mDesiredHeight if heightMeasureSpec is not
// measured exactly.
height = mDesiredHeight;
// Update heightMeasureSpec
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
setMeasuredDimension(getMeasuredWidth(), height);
final int count = getChildCount();
// measure children
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
int childWidthMeasureSpec;
int childHeightMeasureSpec;
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
0, LayoutParams.WRAP_CONTENT);
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, LayoutParams.WRAP_CONTENT);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
现在,对于 xml,我们不需要定义太多。只需投入所有 children 并让 onLayout(..)
处理展示位置。
<?xml version="1.0" encoding="utf-8"?>
<!-- paddingStart=18dp(10dp + 8dp) because ivArrow's size(32dp) - optical square(24dp) = 8dp -->
<your.package.name.CusViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightLarge"
android:paddingStart="18dp"
android:paddingEnd="10dp"
android:background="#ffffff">
<TextView
android:id="@+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="16 Mar 2015"
android:textColor="#858585"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvCorrectTxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Correct 20"
android:textColor="#858585"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivCorrect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_good"/>
<ImageView
android:id="@+id/ivArrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_next_item"/>
<TextView
android:id="@+id/tvTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="22:15"
android:textSize="18sp"
android:textColor="#858585"/>
<TextView
android:id="@+id/tvIncorrect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Incorrect 18"
android:textColor="#858585"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivIncorrect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_bad"/>
</com.appeaser.playground.CusViewGroup>
如果您希望 child 视图支持 padding/margin,您可以通过更改 onLayout(...)
和 onMeasure(...)
来实现。要支持 RTL,请修改 onLayout(...)
.
纵向:
横向:
Android Studio 1.2
你好,
我正在尝试使此对齐方式与以下线框正确对齐。
我只为此使用网格布局,不想使用线性或相对布局或嵌套布局。
我使用以下 xml 关闭了它。只需要一些帮助来做最后的润色,让它看起来更完美。
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightLarge"
android:columnCount="6"
android:orientation="horizontal"
android:padding="6dp">
<TextView
android:id="@+id/tvDate"
android:layout_gravity="start"
android:text="16 Mar 2015"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvCorrectTxt"
android:layout_gravity="end"
android:layout_columnSpan="2"
android:text="Correct 20"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivCorrect"
android:layout_columnSpan="2"
android:layout_gravity="top"
android:src="@drawable/ic_action_good"/>
<ImageView
android:id="@+id/ivArrow"
android:layout_rowSpan="2"
android:layout_gravity="center"
android:src="@drawable/ic_action_next_item"/>
<TextView
android:id="@+id/tvTime"
android:layout_gravity="start|bottom"
android:text="22:15"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvIncorrect"
android:layout_gravity="end|bottom"
android:layout_columnSpan="2"
android:text="Incorrect 18"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivIncorrect"
android:layout_gravity="bottom"
android:src="@drawable/ic_action_bad"/>
</GridLayout>
非常感谢您的任何建议,
我建议您尝试使用属性 columnWeight 来调整单元格的大小。我还建议为您的图片分配一个特定的尺寸。
看看我编辑的版本,它几乎给出了想要的结果! (是的,我使用了不同的图像资源;)):
这是XML:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightLarge"
android:columnCount="6"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:id="@+id/tvDate"
android:layout_gravity="start"
android:text="16 Mar 2015"
android:layout_columnWeight="1"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvCorrectTxt"
android:layout_gravity="end"
android:layout_columnSpan="2"
android:text="Correct 20"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivCorrect"
android:layout_columnSpan="2"
android:layout_gravity="center_horizontal|top"
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/ic_action_good"/>
<ImageView
android:id="@+id/ivArrow"
android:layout_rowSpan="2"
android:layout_columnWeight="0"
android:layout_gravity="center"
android:src="@drawable/ic_action_next_item"/>
<TextView
android:id="@+id/tvTime"
android:layout_columnWeight="1"
android:layout_gravity="start|bottom"
android:text="22:15"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvIncorrect"
android:layout_gravity="end|bottom"
android:layout_columnSpan="2"
android:layout_columnWeight="0"
android:text="Incorrect 18"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivIncorrect"
android:layout_columnWeight="2"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="center_horizontal|bottom"
android:src="@drawable/ic_action_bad"/>
</GridLayout>
您的布局(从图片中看)看起来相当静态 - 没有太多活动部件。为什么不将其实现为自定义 Viewgroup
?由于您追求性能,因此这就是要走的路。
child 视图的定位将相对于 ViewGroup
,而不是 children 本身。涉及数学:
列(child左 & child右)
- 将可用宽度 (actualWidth - paddingLeft - paddingRight) 分成 6 个等距的列
- TextViews tvDate 和 tvTime 向左刷新
- TextViews tvCorrectTxt 和 tvIncorrect 在第 4 列内刷新
- ivCorrect 和 ivIncorrect 在第 5 列内水平居中
- ivArrow 在第 6 列右侧刷新
行(child顶部和child底部)
- 我们有两行要显示。为了使行上方、行间和行下方的间距相等,我们将第一行的垂直中心与
container's height/3
==>y = 0.333*availableHeight
对齐;第二行的垂直中心将位于2*container's height/3
==>y = 0.666*availableHeight
- ivArrow 将在可用高度内垂直居中。
CusViewGroup:
public class CusViewGroup extends ViewGroup {
// In case we have to explicitly set the viewgroup's height
private int mDesiredHeight;
public CusViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
// Attribute array
int[] attrss = new int[] { android.R.attr.listPreferredItemHeightLarge };
TypedArray a = context.getTheme().obtainStyledAttributes(attrss);
// 200 is being used as the default value.
// This should actually be defined in res/values/dimens.xml
// and retrieved using
// getResources.getDimensionPixelSize(R.dimen.defaultListItemHeight)
mDesiredHeight = a.getDimensionPixelSize(0, 200);
a.recycle();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
final int parentLeft = getPaddingStart();
final int parentRight = right - left - getPaddingEnd();
final int parentTop = getPaddingTop();
final int parentBottom = bottom - top - getPaddingBottom();
final int availableParentWidth = parentRight - parentLeft;
final int availableParentHeight = parentBottom - parentTop;
final int columnWidth = availableParentWidth/6;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft, childTop;
// We can use the assigned ids and calculate
// view positions.
switch(child.getId()) {
case R.id.tvDate:
childLeft = parentLeft;
childTop = parentTop + availableParentHeight/3 - height/2;
break;
case R.id.tvCorrectTxt:
childLeft = parentLeft + columnWidth*4 - width;
childTop = parentTop + availableParentHeight/3 - height/2;
break;
case R.id.ivCorrect:
childLeft = parentLeft + columnWidth*4 + (columnWidth - width) / 2;
childTop = parentTop + availableParentHeight/3 - height/2;
break;
case R.id.ivArrow:
childLeft = parentRight - width;
childTop = parentTop + (availableParentHeight - height) / 2;
break;
case R.id.tvTime:
childLeft = parentLeft;
childTop = parentTop + 2*availableParentHeight/3 - height/2;
break;
case R.id.tvIncorrect:
childLeft = parentLeft + columnWidth*4 - width;
childTop = parentTop + 2*availableParentHeight/3 - height/2;
break;
case R.id.ivIncorrect:
childLeft = parentLeft + columnWidth*4 + (columnWidth - width) / 2;
childTop = parentTop + 2*availableParentHeight/3 - height/2;
break;
default:
// We shouldn't be here
child.setVisibility(View.GONE);
continue;
}
// layout child
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// We're using an exact value for height & MATCH_PARENT for width
int height = getMeasuredHeight();
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
// Use mDesiredHeight if heightMeasureSpec is not
// measured exactly.
height = mDesiredHeight;
// Update heightMeasureSpec
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
setMeasuredDimension(getMeasuredWidth(), height);
final int count = getChildCount();
// measure children
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
int childWidthMeasureSpec;
int childHeightMeasureSpec;
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
0, LayoutParams.WRAP_CONTENT);
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, LayoutParams.WRAP_CONTENT);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
现在,对于 xml,我们不需要定义太多。只需投入所有 children 并让 onLayout(..)
处理展示位置。
<?xml version="1.0" encoding="utf-8"?>
<!-- paddingStart=18dp(10dp + 8dp) because ivArrow's size(32dp) - optical square(24dp) = 8dp -->
<your.package.name.CusViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightLarge"
android:paddingStart="18dp"
android:paddingEnd="10dp"
android:background="#ffffff">
<TextView
android:id="@+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="16 Mar 2015"
android:textColor="#858585"
android:textSize="18sp"/>
<TextView
android:id="@+id/tvCorrectTxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Correct 20"
android:textColor="#858585"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivCorrect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_good"/>
<ImageView
android:id="@+id/ivArrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_next_item"/>
<TextView
android:id="@+id/tvTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="22:15"
android:textSize="18sp"
android:textColor="#858585"/>
<TextView
android:id="@+id/tvIncorrect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Incorrect 18"
android:textColor="#858585"
android:textSize="18sp"/>
<ImageView
android:id="@+id/ivIncorrect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_bad"/>
</com.appeaser.playground.CusViewGroup>
如果您希望 child 视图支持 padding/margin,您可以通过更改 onLayout(...)
和 onMeasure(...)
来实现。要支持 RTL,请修改 onLayout(...)
.
纵向:
横向: