Android GridView行重叠:如何使行高度适合最高的项目?
例如,我在GridView项目之间有不需要的重叠: 请注意,除了最右边的一列之外,每列中都有文本 我与前一个问题的不同之处在于,我不想要恒定的行高。我希望行高有所不同,以容纳每行中最高的内容,从而有效利用屏幕空间 查看(不是权威副本,但kernel.org仍处于关闭状态),我们可以在fillDown()和makeRow()中看到最后一个视图是“参考视图”:行的高度是从该视图的高度设置的,而不是从最高的视图设置的这解释了为什么最右边的列是可以的。不幸的是,GridView没有很好的设置,我无法通过继承来解决这个问题。所有相关字段和方法都是私有的 所以,在我走上“克隆并拥有”这条老掉牙的老路之前,我有没有遗漏一个技巧?我可以使用一个表格布局,但这需要我自己实现Android GridView行重叠:如何使行高度适合最高的项目?,android,android-layout,android-gridview,Android,Android Layout,Android Gridview,例如,我在GridView项目之间有不需要的重叠: 请注意,除了最右边的一列之外,每列中都有文本 我与前一个问题的不同之处在于,我不想要恒定的行高。我希望行高有所不同,以容纳每行中最高的内容,从而有效利用屏幕空间 查看(不是权威副本,但kernel.org仍处于关闭状态),我们可以在fillDown()和makeRow()中看到最后一个视图是“参考视图”:行的高度是从该视图的高度设置的,而不是从最高的视图设置的这解释了为什么最右边的列是可以的。不幸的是,GridView没有很好的设置,我无法通
numColumns=“auto_-fit”
(因为我只想在手机屏幕上显示一个长列),而且它也不是AdapterView,这感觉应该是这样的
编辑:事实上,克隆和拥有在这里并不实用。GridView依赖于其父类和同级类的不可访问部分,将导致导入至少6000行代码(AblistView、AdapterView等)。我使用静态数组来驱动行的最大高度。这并不完美,因为在重新显示单元格之前,不会调整先前列的大小。下面是可重用内容视图的代码 编辑:我正确地完成了这项工作,但在渲染之前我已经预先测量了所有单元格。为此,我将GridView子类化,并在onLayout方法中添加一个测量挂钩
/**
* Custom view group that shares a common max height
* @author Chase Colburn
*/
public class GridViewItemLayout extends LinearLayout {
// Array of max cell heights for each row
private static int[] mMaxRowHeight;
// The number of columns in the grid view
private static int mNumColumns;
// The position of the view cell
private int mPosition;
// Public constructor
public GridViewItemLayout(Context context) {
super(context);
}
// Public constructor
public GridViewItemLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Set the position of the view cell
* @param position
*/
public void setPosition(int position) {
mPosition = position;
}
/**
* Set the number of columns and item count in order to accurately store the
* max height for each row. This must be called whenever there is a change to the layout
* or content data.
*
* @param numColumns
* @param itemCount
*/
public static void initItemLayout(int numColumns, int itemCount) {
mNumColumns = numColumns;
mMaxRowHeight = new int[itemCount];
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Do not calculate max height if column count is only one
if(mNumColumns <= 1 || mMaxRowHeight == null) {
return;
}
// Get the current view cell index for the grid row
int rowIndex = mPosition / mNumColumns;
// Get the measured height for this layout
int measuredHeight = getMeasuredHeight();
// If the current height is larger than previous measurements, update the array
if(measuredHeight > mMaxRowHeight[rowIndex]) {
mMaxRowHeight[rowIndex] = measuredHeight;
}
// Update the dimensions of the layout to reflect the max height
setMeasuredDimension(getMeasuredWidth(), mMaxRowHeight[rowIndex]);
}
}
最后,这里是GridView子类,用于在布局期间测量视图单元:
/**
* Custom subclass of grid view to measure all view cells
* in order to determine the max height of the row
*
* @author Chase Colburn
*/
public class AutoMeasureGridView extends GridView {
public AutoMeasureGridView(Context context) {
super(context);
}
public AutoMeasureGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AutoMeasureGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if(changed) {
CustomAdapter adapter = (CustomAdapter)getAdapter();
int numColumns = getContext().getResources().getInteger(R.integer.list_num_columns);
GridViewItemLayout.initItemLayout(numColumns, adapter.getCount());
if(numColumns > 1) {
int columnWidth = getMeasuredWidth() / numColumns;
adapter.measureItems(columnWidth);
}
}
super.onLayout(changed, l, t, r, b);
}
}
我将列数作为资源的原因是,我可以根据方向等使用不同的列数。根据Chris提供的信息,我在确定其他GridView项的高度时,使用了本机GridView使用的参考视图,使用了此解决方法 我创建了这个GridViewItemContainer自定义类:
/**
* This class makes sure that all items in a GridView row are of the same height.
* (Could extend FrameLayout, LinearLayout etc as well, RelativeLayout was just my choice here)
* @author Anton Spaans
*
*/
public class GridViewItemContainer extends RelativeLayout {
private View[] viewsInRow;
public GridViewItemContainer(Context context) {
super(context);
}
public GridViewItemContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public GridViewItemContainer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setViewsInRow(View[] viewsInRow) {
if (viewsInRow != null) {
if (this.viewsInRow == null) {
this.viewsInRow = Arrays.copyOf(viewsInRow, viewsInRow.length);
}
else {
System.arraycopy(viewsInRow, 0, this.viewsInRow, 0, viewsInRow.length);
}
}
else if (this.viewsInRow != null){
Arrays.fill(this.viewsInRow, null);
}
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (viewsInRow == null) {
return;
}
int measuredHeight = getMeasuredHeight();
int maxHeight = measuredHeight;
for (View siblingInRow : viewsInRow) {
if (siblingInRow != null) {
maxHeight = Math.max(maxHeight, siblingInRow.getMeasuredHeight());
}
}
if (maxHeight == measuredHeight) {
return;
}
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
switch(heightMode) {
case MeasureSpec.AT_MOST:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(maxHeight, heightSize), MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
break;
case MeasureSpec.EXACTLY:
// No debate here. Final measuring already took place. That's it.
break;
case MeasureSpec.UNSPECIFIED:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
break;
}
}
在适配器的getView方法中,将convertView作为子级包装到新的GridViewItemContainer中,或者将其作为项布局的顶部XML元素:
// convertView has been just been inflated or came from getView parameter.
if (!(convertView instanceof GridViewItemContainer)) {
ViewGroup container = new GridViewItemContainer(inflater.getContext());
// If you have tags, move them to the new top element. E.g.:
container.setTag(convertView.getTag());
convertView.setTag(null);
container.addView(convertView);
convertView = container;
}
...
...
viewsInRow[position % numColumns] = convertView;
GridViewItemContainer referenceView = (GridViewItemContainer)convertView;
if ((position % numColumns == (numColumns-1)) || (position == getCount()-1)) {
referenceView.setViewsInRow(viewsInRow);
}
else {
referenceView.setViewsInRow(null);
}
其中numColumns是GridView中的列数,“viewsInRow”是“position”所在的当前行上的视图列表。赋予GridView权重也可以作为子视图应用于LinearLayouts中的GridView。通过这种方式,GridView会用其子对象填充视口,以便您可以查看其项目,只要它们适合屏幕(然后滚动) 但始终避免在ScrollView中使用GridView。否则,您将需要计算每个孩子的身高,并按照上面的回答重新指定他们
<GridView
android:id="@+id/gvFriends"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:verticalSpacing="5dp"
android:horizontalSpacing="5dp"
android:clipChildren="false"
android:listSelector="@android:color/transparent"
android:scrollbarAlwaysDrawHorizontalTrack="false"
android:scrollbarAlwaysDrawVerticalTrack="false"
android:stretchMode="columnWidth"
android:scrollbars="none"
android:numColumns="4"/>
我做了很多研究,但发现了不完整的答案,或者很难理解解决方案的内容,但最终找到了一个完全符合正确解释的答案 我的问题是将gridview项适当地调整到高度。当所有视图的高度相同时,此
网格视图
非常有效。但是,当视图具有不同高度时,栅格的行为与预期不符。视图将相互重叠,形成美观的网格
在这里,我在XML布局中使用了这个类
我使用了这个解决方案,效果非常好,非常感谢。-Abhishek Mittal如果将GridView或ListView转换为a,则不会发生此问题。而且您不需要定制GridView类。这不是我在下面提到的正确解决方案,但可以根据您的要求进行变通 只需从gridview的子布局中设置视图固定点的高度(在某些dp中,即-50dp),即可对其进行包装
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:ellipsize="end"
android:textColor="@color/text_color"
android:textSize="13dp"
android:textStyle="normal" />
您可以在此处包含GridView子类代码,还是其中的相关部分?给您!我希望能有帮助。谢谢!我会在某个时候切换到这个,它看起来应该比TableLayout好一些。:-)我喜欢你的想法,但在测试或使用之前,我有很多疑问。首先,将所有行高度存储在静态变量中。我认为当在同一个视图中使用多个GridView时,它会搞砸。第二,如果“numColumns”是“auto_fit”呢。第三,在“AutoMeasureGridView”的“onLayout”内测量“columnWidth”时。您正在使用“getMeasuredWidth()/numColumns”。但是水平间距呢?它不应该是“getMeasuredWidth()/(numColumns+((numColumns-1)*水平间距))”吗?谢谢:)请上传你的itemView.updateItemDisplay(item,mLanguage);方法,这让我很困惑,这个答案对我很有帮助,但我对updateItemDisplay()方法感到困惑。对于固定高度,这个链接可以是一个解决方案:最好使用循环视图。它将正确地处理这类事情。工作正常,直到两个后续行暴露出相同的问题:然后它们在最初太高的第一行上再次重叠。另一方面,固定高度会发生变化的物体的高度总是有效的。请参见:)我有一个gridView,其中包含不同高度的项目。到目前为止,我找到的唯一解决办法是设置项目的固定高度。我很确定你的解决方案是错误的。首先,布局权重不是由子元素继承的(如果是的话会很奇怪),即使是,它仍然不能解决问题。如问题中所述,问题在于GridView将行的最后一个元素作为引用视图,而不是最高的元素。如果你的解决方案确实有效,那就太好了,但是请在本例中提供一个更全面的例子来说明它是有效的。问题
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:ellipsize="end"
android:textColor="@color/text_color"
android:textSize="13dp"
android:textStyle="normal" />