与this previous person一样,我在GridView项目之间有不必要的重叠:
注意除最右边一列之外的每一列中的文字。
我与上一个问题的不同之处在于我不想要一个恒定的行高。为了有效利用屏幕空间,我希望行高可以变为容纳每行中最高的内容。
查看source for GridView(不是权威副本,但kernel.org仍然关闭),我们可以在fillDown()和makeRow()中看到最后一个View看到的是“参考视图”:行的高度是从该视图的高度设置的,而不是从最高的高度设置的。 这解释了为什么最右边的列没问题。不幸的是,GridView没有很好地设置我通过继承来解决这个问题。所有相关领域和方法都是私密的。
所以,在我采取“克隆和拥有”这条破旧的臃肿路径之前,是否有一个我在这里缺少的技巧?我可以使用TableLayout,但这需要我我自己实现numColumns="auto_fit"
(因为我想在手机屏幕上只需要一个长列),它也不会是一个AdapterView,这应该是它应该是。
编辑:实际上,克隆和自己在这里不实用。 GridView依赖于其父类和兄弟类的不可访问部分,并导致至少导入6000行代码(AbsListView,AdapterView等)
答案 0 :(得分:27)
我使用静态数组来驱动行的最大高度。这并不完美,因为在重新显示单元格之前,之前的列不会调整大小。以下是膨胀的可重用内容视图的代码。
编辑:我正确地完成了这项工作,但我在渲染之前预先测量了所有单元格。我通过继承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]);
}
}
这是我的BaseAdapter子类中的测量功能。请注意,我有一个方法updateItemDisplay
,可以在视图单元格上设置所有适当的文本和图像。
/**
* Run a pass through each item and force a measure to determine the max height for each row
*/
public void measureItems(int columnWidth) {
// Obtain system inflater
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// Inflate temp layout object for measuring
GridViewItemLayout itemView = (GridViewItemLayout)inflater.inflate(R.layout.list_confirm_item, null);
// Create measuring specs
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(columnWidth, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
// Loop through each data object
for(int index = 0; index < mItems.size(); index++) {
String[] item = mItems.get(index);
// Set position and data
itemView.setPosition(index);
itemView.updateItemDisplay(item, mLanguage);
// Force measuring
itemView.requestLayout();
itemView.measure(widthMeasureSpec, heightMeasureSpec);
}
}
最后,这里是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);
}
}
我将列数作为资源的原因是我可以根据方向等使用不同的数字。
答案 1 :(得分:1)
根据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'所在位置的当前行的View列表。
答案 2 :(得分:1)
我做了很多研究,但发现了不完整的答案,或者很难理解解决方案的进展情况,但最终找到了一个完全符合正确解释的答案。
我的问题是将gridview项正确放入高度。当您的所有视图都具有相同的高度时,此Grid-view
效果很好。但是当您的视图具有不同的高度时,网格的行为不会像预期的那样。视图将相互重叠,从而导致an-aesthetically
令人愉悦的网格。
这里Solution 我在XML布局中使用了这个类。
我使用了这个解决方案,这非常有效,非常感谢.-- Abhishek Mittal
答案 3 :(得分:0)
如果将GridView或ListView转换为RecyclerView,则不会发生此问题。而且您无需创建自定义GridView类。
答案 4 :(得分:0)
这不是我在下面提到的正确解决方案,但可以根据您的要求解决。
只需从 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" />
答案 5 :(得分:-1)
为GridView赋予权重也适用于作为孩子的LinearLayouts内的GridViews。通过这种方式,GridView可以使用子项填充视口,这样只要它们适合屏幕(然后滚动),就可以查看它的项目。
但总是避免在ScrollViews中使用GridViews。否则,您将需要计算每个孩子的身高,并将其重新分配为Chase在上面回答。
<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"/>