Сложность строк GridView: как сделать высоту строки подходящей для самого высокого элемента?

Как и предыдущий человек , у меня есть нежелательное совпадение между элементами GridView:

Элементы GridView перекрываются

Обратите внимание на текст в каждом столбце, кроме самого правого.

Там, где я отличаюсь от предыдущего вопроса, я не хочу постоянной высоты строки . Я хочу, чтобы высота строки изменялась для размещения самого высокого содержания в каждой строке, для эффективного использования пространства экрана.

Глядя на источник GridView (а не на авторитетную копию, но kernel.org все еще не работает), мы можем видеть в fillDown () и makeRow (), что последний вид показан как «ссылочный вид»: высота строки задана из Высота этого вида, а не от самого высокого. Это объясняет, почему самый правый столбец в порядке. К сожалению, GridView не очень хорошо настроен для меня, чтобы исправить это путем наследования. Все соответствующие поля и методы являются частными.

Итак, прежде чем я возьму изрядно раздутый путь «клона и собственного», есть ли трюк, который мне здесь не хватает? Я мог бы использовать TableLayout, но это потребовало бы, чтобы я сам реализовал numColumns="auto_fit" (так как я хочу, например, только один длинный столбец на экране телефона), и это также не было бы AdapterView, который, похоже, быть.

Изменить: на самом деле, клон и собственный не практичны. GridView зависит от недоступных частей его родительских и родственных классов и приведет к импорту не менее 6000 строк кода (AbsListView, AdapterView и т. Д.),

Solutions Collecting From Web of "Сложность строк GridView: как сделать высоту строки подходящей для самого высокого элемента?"

Я использовал статический массив для максимальных высот для строки. Это не идеально, поскольку ранние столбцы не будут изменены до тех пор, пока ячейка не будет повторно отображена. Вот код для раздутого просмотра многоразового содержимого.

Изменить : я правильно выполнил эту работу, но перед рендерингом я предварительно измерил все ячейки. Я сделал это путем подклассификации 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); } } 

Причина, по которой у меня есть количество столбцов в качестве ресурса, заключается в том, что у меня может быть другое число, основанное на ориентации и т. Д.

Основываясь на информации от Криса, я использовал это обходное решение, используя ссылочный вид, используемый родным 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 вашего адаптера либо оберните преобразование в качестве дочернего элемента в новом 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. Eg: 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» – это список View в текущей строке, где находится «позиция».

Предоставление веса вашему GridView также работает с GridViews внутри LinearLayouts в детстве. Таким образом, GridView заполняет окно просмотра своими дочерними элементами, поэтому вы можете просматривать его элементы, пока они соответствуют экрану (затем вы прокручиваете).

Но всегда избегайте использования GridViews внутри ScrollViews. В противном случае вам нужно будет рассчитать высоту каждого ребенка и переназначить их, как сказал 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"/>