Hacer cuadrados los elementos de GridView

78

Me gustaría que los elementos de mi GridViewfueran cuadrados. Hay 2 columnas y el ancho de los elementos es fill_parent(por ejemplo, ocupan tanto espacio horizontal como sea posible. Los elementos son vistas personalizadas.

¿Cómo hago para que la altura de los elementos sea igual a su ancho variable?

anagaf
fuente

Respuestas:

106

Existe una solución más simple cuando las columnas GridView se estiran. Simplemente anule el onMeasure del diseño del elemento GridView con ...

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
jdamcd
fuente
Funciona genial. Solo algunas cosas que me perdí en la respuesta: 1. La altura se ignora y el ancho se pasa como la altura. 2. El método debe anularse en el elemento y en la propia Grid.
AlikElzin-kilaka
12
Aproximadamente 1: eso es lo que lo hace cuadrado - "
mDroidd
2
@KarthikPalanivelu Esto se usaría en el diseño de sus elementos de cuadrícula, no en GridView en sí. Quizás podría extender RelativeLayout a SquareRelativeLayout con este fragmento.
jdamcd
1
si hago eso en mi vista de cuadrícula, hago el cuadrado de GridView, no sus hijos.
Daniel Bo
4
Una respuesta completa estaría bien. ¿Está diciendo que amplíe la vista de cuadrícula?
StarWind0
56

O si desea extender una Vista, simplemente haga algo realmente simple como:

public class SquareImageView extends ImageView
{

    public SquareImageView(final Context context)
    {
        super(context);
    }

    public SquareImageView(final Context context, final AttributeSet attrs)
    {
        super(context, attrs);
    }

    public SquareImageView(final Context context, final AttributeSet attrs, final int defStyle)
    {
        super(context, attrs, defStyle);
    }


    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
    {
        final int width = getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec);
        setMeasuredDimension(width, width);
    }

    @Override
    protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh)
    {
        super.onSizeChanged(w, w, oldw, oldh);
    }
}

Vale la pena señalar que super.onMeasure()no es necesario. onMeasuredrequisito es que debes llamar setMeasuredDimension.

Chris.Jenkins
fuente
2
Funciona a la
perfección
2
@ Chris.Jenkins No estoy de acuerdo con su último comentario: si no llamo super.onMeasure(), no se dibujará un ImageView colocado dentro. Entonces parece que se requiere como primera línea del onMeasuremétodo.
cyrilchampier
@nerith, es esto en un dispositivo específico, ya que he estado usando este código durante mucho tiempo y nunca arruino los problemas. Interesado en ver cómo reproducir lo que está viendo.
Chris.Jenkins
@ Chris.Jenkins no en un dispositivo específico. Mi SquareFrameLayout se carga desde el xml. ¿Agrega a sus hijos programáticamente?
cyrilchampier
Oh, sí, esto no funcionará con vistas solo de grupos de vista. Quizás debería mencionar eso.
Chris.Jenkins
16

No estoy seguro de que esto sea posible con los widgets actuales. Su mejor opción podría ser poner su vista personalizada en un "SquareView" personalizado. Esta vista solo podría contener 1 vista secundaria y forzar la altura para que sea igual al ancho cuando se llama a su método onLayout.

Nunca intenté hacer algo así, pero creo que no debería ser demasiado difícil. Una solución alternativa (y tal vez un poco más fácil) podría ser subclasificar el diseño raíz de su vista personalizada (por ejemplo, si es un LinearLayout, haga un SquareLinearLayout) y usarlo como un contenedor en su lugar.

editar: Aquí hay una implementación básica que parece funcionar para mí:

package com.mantano.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

public class SquareView extends ViewGroup {

    public SquareView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int u, int r, int d) {
        getChildAt(0).layout(0, 0, r-l, d-u); // Layout with max size
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        View child = getChildAt(0);
        child.measure(widthMeasureSpec, widthMeasureSpec);
        int width = resolveSize(child.getMeasuredWidth(), widthMeasureSpec);
        child.measure(width, width); // 2nd pass with the correct size
        setMeasuredDimension(width, width);
    }
}

Está diseñado para tener un hijo único, pero ignoré todas las comprobaciones por simplicidad. La idea básica es medir al niño con los parámetros de ancho / alto establecidos por GridView (en mi caso, usa numColumns = 4 para calcular el ancho), y luego hacer una segunda pasada con las dimensiones finales, con alto = ancho. .. El diseño es simplemente un diseño simple, que coloca al niño único en (0, 0) con las dimensiones deseadas (derecha-izquierda, abajo-arriba).

Y aquí está el XML utilizado para los elementos de GridView:

<?xml version="1.0" encoding="utf-8"?>
<com.mantano.widget.SquareView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="wrap_content">

    <LinearLayout android:id="@+id/item" android:layout_width="fill_parent"
        android:layout_height="fill_parent" android:orientation="horizontal"
        android:gravity="center">

        <TextView android:id="@+id/text" android:layout_width="fill_parent"
            android:layout_height="wrap_content" android:text="Hello world" />
    </LinearLayout>
</com.mantano.widget.SquareView>

Usé un LinearLayout dentro de SquareView para que administre todas las posibilidades de gravedad, márgenes, etc.

No estoy seguro de qué tan bien (o mal) reaccionaría este widget a los cambios de orientación y dimensión, pero parece funcionar correctamente.

Gregory
fuente
Todo eso suena bien, el único problema es escribir correctamente onLayout (o onMeasure). Currenlty Establecí alto / ancho en ItemAdapter.getView () pero no me gusta esta solución.
anagaf
@anagaf Edité mi respuesta con un código que acabo de probar y que parece funcionar. Es bastante fácil de entender, por lo que debería ser fácil de solucionar en caso de que encuentres algunos fallos ^^ (no estoy seguro de si se suponía que debía editar o publicar una segunda respuesta, ¡lo siento si lo hice mal!)
Gregory
1
El diseño anterior no funciona --- no sé por qué, pero hay problemas sutiles de diseño con los niños que significan que, por ejemplo, TextViews no se ajusta y RelativeLayouts se confunden horriblemente. Sin embargo, encontré un equivalente aquí: stackoverflow.com/questions/2948212/… ... aunque todavía necesitaba ajustarlo un poco (el tamaño se basa en la dimensión más grande en lugar de solo en el ancho).
David dado
12

Termina siendo bastante fácil conseguir que el elemento de la cuadrícula quede cuadrado.

En su método "GetView" de su adaptador de red, simplemente haga:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    GridView grid = (GridView)parent;
    int size = grid.getRequestedColumnWidth();

    TextView text = new TextView(getContext());
    text.setLayoutParams(new GridView.LayoutParams(size, size));

    // Whatever else you need to set on your view

    return text;
}
SteveBorkman
fuente
Esta es la única solución que funcionó para mí ... ¡¡Muchas gracias !!
Zeba
4
Tuve que reemplazar "getRequestedColumnWidth" con "getColumnWidth" y funcionó para mí .. ¡Gracias!
Ayad Kara Kâhya
1
lo mismo aquí: tuve que reemplazar getRequestedColumnWidthcon getColumnWidthy trabajó para mí ... gracias!
Yahya
6

No sé si es la mejor manera, pero lo logro haciendo que mi vista personalizada anule onSizeChanged de esa manera:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    if (getLayoutParams() != null && w != h) {
        getLayoutParams().height = w;
        setLayoutParams(getLayoutParams());
    }
}

¡Utilizo esta vista personalizada dentro de LinearLayout y GridView y en ambos se muestra un cuadrado!

Luckcheese
fuente
Oh hombre, eres un salvavidas, he estado luchando con esto durante días. La respuesta de Gregory funciona bien a menos que tenga muchos hijos; luego, como señala un comentario, las TextViews no se ajustan y la mayoría de las vistas no reajustan sus límites. ¡Pero esto lo arregla!
Makario
6

¡Esto funcionó para mí!

Si eres como yo y no puedes usar getRequestedColumnWidth () porque tu minSdk es menor que 16, sugiero esta opción.

  1. en tu fragmento:

    DisplayMetrics dm = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm); int size = dm.widthPixels / nbColumns; CustomAdapter adapter = new CustomAdapter(list, size, getActivity());

  2. en su adaptador (en getView (int position, View convertView, ViewGroup parent))

    v.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, size));

  3. fragment.xml:

    <GridView android:id="@+id/gridView" android:layout_width="match_parent" android:layout_height="match_parent" android:horizontalSpacing="3dp" android:verticalSpacing="3dp" android:numColumns="4" android:stretchMode="columnWidth" />

  4. custom_item.xml: (por ejemplo)

    <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/picture" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" />

¡Espero que ayude a alguien!

Anne-Claire
fuente
5

si establece un número estático de columnas en el xml, puede tomar el ancho de la vista y dividirlo por el número de columnas.

si está utilizando auto_fit, será un poco complicado obtener el recuento de columnas (no hay un método getColumnCount ()), por cierto, esta pregunta debería ayudarlo de alguna manera.

este es el código que estoy usando en el getView(...)método del adaptador con un número fijo de columnas:

    item_side = mFragment.holder.grid.getWidth() / N_COL;
    v.getLayoutParams().height = item_side;
    v.getLayoutParams().width = item_side;
Mario Lenci
fuente
5

Tratar

<GridView
...
android:stretchMode="columnWidth" />

También puedes jugar con otros modos

Arte
fuente
4

Esto es lo que estoy haciendo para mostrar celdas cuadradas en un GridView. No estoy seguro de si desea celdas cuadradas o algo más.

GridView grid = new GridView( this );
grid.setColumnWidth( UIScheme.cellSize );
grid.setVerticalSpacing( UIScheme.gap );
grid.setStretchMode( GridView.STRETCH_COLUMN_WIDTH );
grid.setNumColumns( GridView.AUTO_FIT );

Y luego la vista que estoy creando para cada celda tengo que hacer lo siguiente:

cell.setLayoutParams( new GridView.LayoutParams(iconSize, iconSize) );
Sileria
fuente
3

Basado en la respuesta de SteveBorkman , que es API 16+:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    GridView grid = (GridView)parent;
    int size = grid.getColumnWidth();

    if (convertView == null){
        convertView = LayoutInflater.from(context).inflate(R.layout.your_item, null);
        convertView.setLayoutParams(new GridView.LayoutParams(size, size));
    }

     //Modify your convertView here

     return convertView;
}
Joaquín Iurchuk
fuente
1

En la línea de la respuesta de @ Gregory, tuve que envolver mi imagen en un LinearLayout para evitar que GridView manipulara sus dimensiones desde el cuadrado. Tenga en cuenta que el LinearLayout externo debe configurarse para tener la dimensión de la imagen, y el ancho de la columna GridView debe ser el ancho de la imagen MÁS cualquier margen. Por ejemplo:

    <!-- LinearLayout wrapper necessary to wrap image button in order to keep square;
    otherwise, the grid view distorts it into rectangle.

    also, margin top and right allows the indicator overlay to extend top and right.

    NB: grid width is set in filter_icon_page.xml, and must correspond to size + margin
    -->
<LinearLayout
        android:id="@+id/sport_icon_lay"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:orientation="vertical"

        android:layout_marginRight="6dp"
        android:layout_marginTop="6dp"
        >

    <ImageButton
            android:id="@+id/sport_icon"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:maxHeight="60dp"
            android:maxWidth="60dp"
            android:scaleType="fitCenter"
            />
</LinearLayout>

y el GridView como:

   <GridView
        android:id="@+id/grid"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:fillViewport="true"

        android:columnWidth="66dp"

        android:numColumns="auto_fit"
        android:verticalSpacing="25dp"
        android:horizontalSpacing="24dp"
        android:stretchMode="spacingWidth"

        />
larham1
fuente
-1

En tu actividad

 DisplayMetrics displaymetrics = new DisplayMetrics();
 getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
 valX = displaymetrics.widthPixels/columns_number;

en el CustomAdapter de GridView

 v.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, YourActivity.valX));
Boris Karloff
fuente