Superposición de filas GridView: cómo hacer que la altura de la fila se ajuste al elemento más alto?

Al igual que esta persona anterior , tengo una superposición no deseada entre los elementos de GridView:

GridView elementos superpuestos

Observe el texto, en cada columna excepto la más a la derecha.

Donde difiero de la pregunta anterior es que no quiero una altura de fila constante . Quiero que la altura de la fila varíe para acomodar el contenido más alto en cada fila, para un uso eficiente del espacio en la pantalla.

Mirando la fuente de GridView (no la copia autorizada, pero kernel.org aún está inactivo), podemos ver en fillDown () y makeRow () que la última Vista vista es la “vista de referencia”: la altura de la fila se establece desde la altura de esa vista, no del más alto. Esto explica por qué la columna de la derecha está bien. Desafortunadamente, GridView no está bien configurado para que lo solucione por herencia. Todos los campos y métodos relevantes son privados.

Entonces, antes de tomar el desgastado camino lleno de baches de “clonar y poseer”, ¿hay algún truco que me falta aquí? Podría usar un TableLayout, pero eso requeriría implementar numColumns="auto_fit" (ya que quiero, por ejemplo, una sola columna larga en la pantalla de un teléfono), y tampoco sería un AdapterView, que parece que debería ser.

Editar: de hecho, clonar y poseer no es práctico aquí. GridView depende de partes inaccesibles de sus clases principales y hermanas, y daría como resultado la importación de al menos 6000 líneas de código (AbsListView, AdapterView, etc.)

    Usé una matriz estática para manejar alturas máximas para la fila. Esto no es perfecto ya que las columnas anteriores no se redimensionarán hasta que la celda se vuelva a mostrar. Aquí está el código para la vista de contenido reutilizable inflado.

    Editar : Obtuve este trabajo correctamente, pero he medido previamente todas las celdas antes de renderizar. Hice esto subclasificando GridView y agregando un gancho de medición en el método 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]); } } 

    Aquí está la función de medición en mi subclase BaseAdapter. Tenga en cuenta que tengo un método updateItemDisplay que establece todos los textos e imágenes apropiados en la celda de vista.

      /** * 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); } } 

    Y finalmente, aquí está la subclase GridView configurada para medir celdas de vista durante el diseño:

     /** * 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); } } 

    La razón por la que tengo el número de columnas como recurso es para que pueda tener un número diferente en función de la orientación, etc.

    Basado en la información de Chris, utilicé esta solución utilizando la vista de referencia utilizada por el GridView nativo para determinar la altura de otros elementos de GridView.

    Creé esta clase personalizada 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; } } 

    En el método getView de su adaptador, ajuste su convertView como elemento secundario en un nuevo GridViewItemContainer o conviértalo en el elemento XML superior del diseño de su elemento:

      // 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); } 

    Donde numColumns es el número de columnas en el GridView y ‘viewsInRow’ es una lista de Ver en la fila actual de donde se encuentra ‘posición’.

    Hice muchas investigaciones pero encontré una respuesta incompleta o tuve dificultades para entender qué sucedía con la solución, pero finalmente encontré una respuesta que encajaba perfectamente con la explicación adecuada.

    Mi problema era encajar el artículo gridview en altura correctamente. Esta Grid-view funcionó muy bien cuando todas tus vistas tienen la misma altura. Pero cuando sus puntos de vista tienen diferentes alturas, la cuadrícula no se comporta como se esperaba. Las vistas se superpondrán entre sí, lo que generará an-aesthetically grilla an-aesthetically agradable.

    Aquí la Solución I utilizó esta clase en el diseño XML.

    Usé esta solución, y esto está funcionando muy bien, muchas gracias .– Abhishek Mittal

    Dando peso a su GridView también funciona en GridViews dentro de LinearLayouts como un niño. De esta forma, GridView llena la ventana gráfica con sus elementos secundarios para que pueda ver sus elementos siempre que se ajusten a la pantalla (luego se desplaza).

    Pero siempre evite usar GridViews dentro de ScrollViews. De lo contrario, tendrá que calcular la altura de cada niño y reasignarlos como Chase contestó anteriormente.