Cuadrícula heterogénea

ACTUALIZACIÓN: mi solución de trabajo: https://stackoverflow.com/a/21233824/902172


Estoy tratando de implementar el diseño a continuación:

Diseño de destino

Supongo que GridLayout es adecuado para mis necesidades, pero después de 2 horas de lucha no pude crear ni siquiera un diseño similar. El diseño se redimensiona incorrectamente , excede la pantalla del teléfono y tampoco abarca las filas y columnas especificadas. .

Aquí seleccioné un botón para que pueda ver cómo supera los límites:

Fallar

y aquí está el código xml asociado: https://gist.github.com/2834492

He alcanzado un diseño similar con linearlayouts nesteds, pero no es posible cambiar el tamaño correctamente para diferentes tamaños de pantalla.


ACTUALIZACIÓN: implementación aproximada de LinearLayout:

El código XML: https://gist.github.com/cdoger/2835887 Sin embargo, el problema es que no cambia el tamaño correctamente aquí algunas capturas de pantalla con diferentes configuraciones de pantalla:

enter image description here

enter image description here

enter image description here


TLDR: ¿alguien me puede mostrar una implementación de diseño heterogénea con GridLayout como en la primera imagen?

Discos compactos,

El problema al que se enfrenta es debido al uso inapropiado de GridLayout. El GridLayout está hecho para mostrar a sus hijos en una cuadrícula e intentas anularlo sin extender el GridLayout. Si bien lo que desea puede lograrse en el código (utilizando numcolumns y columnsize), no será útil para múltiples tamaños de pantalla sin una gran cantidad de código.

La única solución adecuada que no requerirá una tonelada de piratería es el uso juicioso de LinearLayout y RelativeLayout. LinearLayout no debe usarse exclusivamente ya que está hecho para colocar elementos en una línea (horizontal o verticalmente solamente). Esto se vuelve especialmente evidente cuando intenta hacer los cuatro botones inferiores. Si bien los botones anteriores se pueden hacer con LinearLayout con muy poco esfuerzo, RelativeLayout es lo que necesita para los cuatro botones inferiores.

Nota: RelativeLayout puede ser un poco truculento para aquellos con poca experiencia en usarlos. Algunos inconvenientes incluyen: superposición de niños, niños que se mueven fuera de la pantalla, interpretación de altura y anchura aplicada incorrectamente. Si desea un ejemplo, hágamelo saber y editaré mi respuesta.

Nota final: estoy a favor de utilizar los objetos del marco actual de maneras únicas, y realmente prefiero proporcionar la solución solicitada. La solución, sin embargo, no es viable dadas las limitaciones de la pregunta.

(Revisión) Solución 1

Después de pensarlo detenidamente anoche, esto se puede lograr con LinearLayout puro. Si bien no me gusta esta solución, debería ser compatible con múltiples pantallas y no requiere herramientas de mi parte. Se debe tener precaución con demasiados LinearLayouts, ya que según los desarrolladores de Google, puede dar lugar a IU de carga lenta debido a la propiedad layout_weight. Una segunda solución que utiliza RelativeLayout se proporcionará cuando regrese a casa. Ahora probado Esto proporciona los parámetros de diseño deseados en todos los tamaños de pantalla y orientaciones.

 < ?xml version="1.0" encoding="utf-8"?>                        

Solución 1 Explicación

La clave de LinearLayouts es definir sus imperativos como Diseños separados y anidar los otros en ellos. A medida que aplica restricciones a más dimensiones, se deben agregar más LinearLayouts para encapsular las demás. Para los suyos, era crucial tener dos padres más para mantener la proporción. Un buen indicador de cuándo deberías agregar otro nivel es cuando tienes que utilizar layout_weight usando algo que no sea un valor entero. Simplemente se vuelve difícil de calcular correctamente. A partir de allí, fue relativamente simple dividirlo en columnas.

Solución 2 (Fallido)

Si bien pude lograr resultados deseables utilizando RelativeLayout y “struts”, solo pude hacerlo con diseños que eran múltiplos de 2 botones de altura. Tal truco sería increíble ya que los niveles de diseño se reducen en gran medida, por lo que trabajaré en una solución XML pura y publicaré la respuesta aquí cuando lo logre. Mientras tanto, el LinearLayout anterior debe adaptarse perfectamente a sus necesidades.

Espero que esto ayude,

FuzzicalLogic

Leí este hilo y me di cuenta de que quería una solución más plana que aquellos con diseño lineal. Después de algunas investigaciones terminé haciendo mi propio diseño. Está inspirado en un GridLayout, pero difiere un poco.

Tenga en cuenta que si va a copiar y pegar el código, tendrá que cambiar los nombres de los paquetes en algunos lugares.

Este diseño tiene 4 parámetros de diseño que los niños usan para posicionarse. Estos son layout_left, layout_top, layout_right, layout_bottom . ICGridLayout en sí tiene dos atributos: diseño_espaciado y columnas .

Columns le dice al diseño cuántas columnas desea que contenga. A continuación, calculará el tamaño de una celda con la misma altura que el ancho. Cuál será el ancho / columnas de los diseños.

El espaciado es la cantidad de espacio que desea entre cada niño.

Los atributos layout_left | top | right | bottom son las coordenadas de cada lado. El diseño no realiza cálculos para evitar colisiones ni nada. Simplemente pone a los niños donde quieren estar.

Si desea tener cuadrados más pequeños, solo tiene que boost el atributo de columnas.

Tenga en cuenta que este es un prototipo rápido, continuaré trabajando en él y cuando sienta que está listo, lo subiré a Github y pondré un comentario aquí.

Todo mi código a continuación debería producir el siguiente resultado: Diseño cuando uso mi código provisto

***** EDITAR ***** Se agregó la llamada a medida para los niños, se olvidó de eso la primera vez. FIN EDITAR ICGridLayout.java:

 package com.risch.evertsson.iclib.layout; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import com.risch.evertsson.iclib.R; /** * Created by johanrisch on 6/13/13. */ public class ICGridLayout extends ViewGroup { private int mColumns = 4; private float mSpacing; public ICGridLayout(Context context) { super(context); } public ICGridLayout(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } public ICGridLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs); } private void init(AttributeSet attrs) { TypedArray a = getContext().obtainStyledAttributes( attrs, R.styleable.ICGridLayout_Layout); this.mColumns = a.getInt(R.styleable.ICGridLayout_Layout_columns, 3); this.mSpacing = a.getDimension(R.styleable.ICGridLayout_Layout_layout_spacing, 0); a.recycle(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (changed) { int width = (int) (r - l); int side = width / mColumns; int children = getChildCount(); View child = null; for (int i = 0; i < children; i++) { child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); int left = (int) (lp.left * side + mSpacing / 2); int right = (int) (lp.right * side - mSpacing / 2); int top = (int) (lp.top * side + mSpacing / 2); int bottom = (int) (lp.bottom * side - mSpacing / 2); child.layout(left, top, right, bottom); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureVertical(widthMeasureSpec, heightMeasureSpec); } private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int width = 0; int height = 0; if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.EXACTLY) { width = MeasureSpec.getSize(widthMeasureSpec); } else { throw new RuntimeException("widthMeasureSpec must be AT_MOST or " + "EXACTLY not UNSPECIFIED when orientation == VERTICAL"); } View child = null; int row = 0; int side = width / mColumns; int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.bottom > row) { row = lp.bottom; } int childHeight = (lp.bottom - lp.top)*side; int childWidth = (lp.right-lp.left)*side; int heightSpec = MeasureSpec.makeMeasureSpec(childHeight, LayoutParams.MATCH_PARENT); int widthSpec = MeasureSpec.makeMeasureSpec(childWidth, LayoutParams.MATCH_PARENT); child.measure(widthSpec, heightSpec); } height = row * side; // TODO: Figure out a good way to use the heightMeasureSpec... setMeasuredDimension(width, height); } @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new ICGridLayout.LayoutParams(getContext(), attrs); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof ICGridLayout.LayoutParams; } @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new ICGridLayout.LayoutParams(p); } @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(); } public static class LayoutParams extends ViewGroup.MarginLayoutParams { int right = 1; int bottom = 1; int top = 0; int left = 0; int width = -1; int height = -1; public LayoutParams() { super(MATCH_PARENT, MATCH_PARENT); top = 0; left = 1; } public LayoutParams(int width, int height) { super(width, height); top = 0; left = 1; } public LayoutParams(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.ICGridLayout_Layout); left = a.getInt(R.styleable.ICGridLayout_Layout_layout_left, 0); top = a.getInt(R.styleable.ICGridLayout_Layout_layout_top, 0); right = a.getInt(R.styleable.ICGridLayout_Layout_layout_right, left + 1); bottom = a.getInt(R.styleable.ICGridLayout_Layout_layout_bottom, top + 1); height = a.getInt(R.styleable.ICGridLayout_Layout_layout_row_span, -1); width = a.getInt(R.styleable.ICGridLayout_Layout_layout_col_span, -1); if (height != -1) { bottom = top + height; } if (width != -1) { right = left + width; } a.recycle(); } public LayoutParams(ViewGroup.LayoutParams params) { super(params); } } } 

ICGridLayout.java es bastante directo. Toma los valores proporcionados por los niños y los establece. attrs.xml:

 < ?xml version="1.0" encoding="utf-8"?>             

example_layout.xml:

 < ?xml version="1.0" encoding="utf-8"?>           

– Johan Risch

PD. Esta es mi primera respuesta larga. Intenté hacerlo de forma correcta. Si he fallado por favor díganme sin flaming 🙂 DS

Me gusta esto ?

enter image description here

 < ?xml version="1.0" encoding="utf-8"?>                      

Como muchos han dicho, los diseños lineales nesteds parecen ser la única forma de ganar aquí. Algunas de las soluciones no han utilizado los parámetros de diseño de la manera más flexible. El siguiente código intenta hacer eso, y de una manera que sea robusta con los cambios en la relación de aspecto. Los detalles están en los comentarios.

enter image description herePaisaje

 < ?xml version="1.0" encoding="utf-8"?>                                  

Así que aquí está la solución que prometí después de un año =) Básicamente utiliza ViewTreeObserver para obtener las dimensiones del diseño principal y crear vistas personalizadas en consecuencia. Dado que este código tiene un año de antigüedad, ViewTreeObserver podría no ser la mejor forma de obtener las dimensiones de forma dinámica.

Puede encontrar el código fuente completo aquí: https://github.com/cdoger/Android_layout

Dividí la pantalla en 8 anchos iguales y 6 alturas iguales. Aquí hay una instantánea de cómo expuse las vistas:

 final RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main_layout); ViewTreeObserver vto = mainLayout.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { final int oneUnitWidth = mainLayout.getMeasuredWidth() / 8; final int oneUnitHeight = mainLayout.getMeasuredHeight() / 6; /** * 1 ***************************************************************/ final RelativeLayout.LayoutParams otelParams = new RelativeLayout.LayoutParams( oneUnitWidth * 4, oneUnitHeight); otelParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); otelParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); // otelParams.setMargins(0, 0, 2, 0); View1.setLayoutParams(otelParams); /***************************************************************/ /** * 2 ***************************************************************/ final RelativeLayout.LayoutParams otherParams = new RelativeLayout.LayoutParams( oneUnitWidth * 4, oneUnitHeight); otherParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); otherParams.addRule(RelativeLayout.RIGHT_OF, View1.getId()); otherParams.setMargins(2, 0, 0, 0); View2.setLayoutParams(otherParams); /***************************************************************/ //... goes on like this 

Aquí está la captura de pantalla final:

enter image description here

He intentado este tuto

Estoy bloqueado aquí:

enter image description here

 < ?xml version="1.0" encoding="utf-8"?>            

Imposible para mí expandir el 8vo botón. : s

Lo he intentado

  

Pero es ineficaz. : s

Incruste su GridLayout en LinearLayout como se muestra a continuación e intente que funcionó para mí.

enter image description here

 < ?xml version="1.0" encoding="utf-8"?>