Estoy usando GridView
para mostrar un montón de vistas que son esencialmente LinearLayouts
. Quiero LinearLayouts
que todos sean cuadrados, pero también quiero que tengan un tamaño dinámico, es decir, hay dos columnas y quiero LinearLayouts
que se estiren dependiendo del tamaño de la pantalla pero que permanezcan cuadrados. ¿Hay alguna forma de hacer esto a través del xml
diseño o tengo que establecer las alturas y los anchos mediante programación?
84
Puede que sea tarde para responder, pero aún así, será útil para los nuevos visitantes.
Con el nuevo ConstraintLayout introducido en Android Studio 2.3, ahora es bastante fácil crear diseños receptivos.
En un ConstraintLayout padre, para hacer que cualquiera de sus hijos vista / diseño sea dinámicamente cuadrado, agregue este atributo
app:layout_constraintDimensionRatio="w,1:1"
w es para especificar restricciones de ancho y la relación 1: 1 asegura un diseño cuadrado.
fuente
No hay nada en el xml que le permita vincular las propiedades de ancho y alto. Probablemente lo más fácil de hacer es subclasificar
LinearLayout
y anularonMeasure
@Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int size = width > height ? height : width; setMeasuredDimension(size, size); }
Lo he usado para crear vistas que siempre son cuadradas antes. Todavía debería funcionar para a
LinearLayout
.Más información que ayudará a hacer esto: http://developer.android.com/guide/topics/ui/custom-components.html http://developer.android.com/reference/android/view/View.MeasureSpec.html
fuente
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
:!super.onMeasure(widthMeasureSpec, widthMeasureSpec);
te da un cuadrado, pero siempre es ancho x ancho. Si las medidas dadas son más anchas que altas, tendrá un problema. Mi solución usa la menor de las dos medidas, de modo que siempre obtienes el cuadrado más grande que quepa dentro del rectángulo dado.Lo he hecho de esta manera:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int size; if(widthMode == MeasureSpec.EXACTLY && widthSize > 0){ size = widthSize; } else if(heightMode == MeasureSpec.EXACTLY && heightSize > 0){ size = heightSize; } else{ size = widthSize < heightSize ? widthSize : heightSize; } int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); super.onMeasure(finalMeasureSpec, finalMeasureSpec); }
Con esta implementación, su diseño será cuadrado, asumiendo el tamaño más bajo entre ancho y alto. E incluso se puede configurar con valores dinámicos, como usar el peso dentro de un LinearLayout.
fuente
Podemos hacerlo de una manera muy sencilla: solo llame
super.onMeasure()
dos veces.protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int height = getMeasuredHeight(); int squareLen = Math.min(width, height); super.onMeasure( MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY)); }
Al llamar
super.onMeasure()
dos veces, esto es menos eficiente en términos del proceso de dibujo, pero es una forma sencilla de solucionar los problemas de diseño que pueden causar las otras respuestas.fuente
super.onMeasure()
dos veces garantiza que el diseño se realice correctamente dado el nuevo tamaño de la vista. Sin esto, necesitamos activar un diseño de una manera diferente o lidiar con diseños incorrectos.Es tan simple como:
public class SquareRelativeLayout extends RelativeLayout { public SquareRelativeLayout(Context context) { super(context); } public SquareRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (widthMeasureSpec < heightMeasureSpec) super.onMeasure(widthMeasureSpec, widthMeasureSpec); else super.onMeasure(heightMeasureSpec, heightMeasureSpec); } }
fuente
Aquí hay una solución que funciona para todos los parámetros de diseño que se pueden configurar para ver o ver el grupo:
int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int widthDesc = MeasureSpec.getMode(widthMeasureSpec); int heightDesc = MeasureSpec.getMode(heightMeasureSpec); int size = 0; if (widthDesc == MeasureSpec.UNSPECIFIED && heightDesc == MeasureSpec.UNSPECIFIED) { size = DP(defaultSize); // Use your own default size, in our case // it's 125dp } else if ((widthDesc == MeasureSpec.UNSPECIFIED || heightDesc == MeasureSpec.UNSPECIFIED) && !(widthDesc == MeasureSpec.UNSPECIFIED && heightDesc == MeasureSpec.UNSPECIFIED)) { //Only one of the dimensions has been specified so we choose the dimension that has a value (in the case of unspecified, the value assigned is 0) size = width > height ? width : height; } else { //In all other cases both dimensions have been specified so we choose the smaller of the two size = width > height ? height : width; } setMeasuredDimension(size, size);
Salud
fuente
Mi sugerencia es crear una clase de diseño personalizado que herede de FrameLayout. Anule el método OnMeasure () y coloque el control que desee que sea cuadrado dentro de ese SquareFrameLayout.
Así es como se hace en Xamarin.Android:
public class SquareFrameLayout : FrameLayout { private const string _tag = "SquareFrameLayout"; public SquareFrameLayout(Android.Content.Context context):base(context) {} public SquareFrameLayout(IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer):base(javaReference, transfer) {} public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs):base(context, attrs) {} public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr):base(context, attrs, defStyleAttr) {} public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes):base(context, attrs, defStyleAttr, defStyleRes) {} protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) { var widthMode = MeasureSpec.GetMode(widthMeasureSpec); int widthSize = MeasureSpec.GetSize(widthMeasureSpec); var heightMode = MeasureSpec.GetMode(heightMeasureSpec); int heightSize = MeasureSpec.GetSize(heightMeasureSpec); int width, height; switch (widthMode) { case MeasureSpecMode.Exactly: width = widthSize; break; case MeasureSpecMode.AtMost: width = Math.Min(widthSize, heightSize); break; default: width = 100; break; } switch (heightMode) { case MeasureSpecMode.Exactly: height = heightSize; break; case MeasureSpecMode.AtMost: height = Math.Min(widthSize, heightSize); break; default: height = 100; break; } Log.Debug(_tag, $"OnMeasure({widthMeasureSpec}, {heightMeasureSpec}) => Width mode: {widthMode}, Width: {widthSize}/{width}, Height mode: {heightMode}, Height: {heightSize}/{height}"); var size = Math.Min(width, height); var newMeasureSpec = MeasureSpec.MakeMeasureSpec(size, MeasureSpecMode.Exactly); base.OnMeasure(newMeasureSpec, newMeasureSpec); } }
Si desea que una Vista (o cualquier otro control) sea cuadrada (y centrada), simplemente agréguela a su diseño de la siguiente manera:
<your.namespace.SquareFrameLayout android:id="@+id/squareContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center"> <View android:id="@+id/squareContent" android:layout_width="match_parent" android:layout_height="match_parent" /> </your.namespace.SquareFrameLayout>
fuente
Agregue la siguiente línea en XML:
app:layout_constraintDimensionRatio="1:1"
fuente
Echa un vistazo a SquareLayout , una biblioteca de Android que proporciona una clase contenedora para diferentes diseños, dándolos dimensiones cuadradas sin perder ninguna funcionalidad principal.
Las dimensiones se calculan justo antes de que se renderice el diseño , por lo tanto, no hay que volver a renderizar ni nada por el estilo para ajustar una vez que se obtiene la vista.
Para usar la biblioteca, agregue esto a su build.gradle:
repositories { maven { url "https://maven.google.com" } } dependencies { compile 'com.github.kaushikthedeveloper:squarelayout:0.0.3' }
El que necesita es SquareLinearLayout .
fuente
Para cualquiera que quiera una solución con Kotlin, esto es lo que hice
FrameLayout
.package your.package.name import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout class SquareLayout: FrameLayout { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { if (widthMeasureSpec < heightMeasureSpec) super.onMeasure(widthMeasureSpec, widthMeasureSpec) else super.onMeasure(heightMeasureSpec, heightMeasureSpec) } }
fuente
Prueba este código:
public class SquareRelativeLayout extends RelativeLayout { public SquareRelativeLayout(Context context) { super(context); } public SquareRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int size; if (widthMode == MeasureSpec.EXACTLY && widthSize > 0) { size = widthSize; } else if (heightMode == MeasureSpec.EXACTLY && heightSize > 0) { size = heightSize; } else { size = widthSize < heightSize ? widthSize : heightSize; } int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); super.onMeasure(finalMeasureSpec, finalMeasureSpec); } }
fuente