GridView-Elemente quadratisch machen

78

Ich möchte, dass meine Gegenstände GridViewquadratisch sind. Es gibt 2 Spalten und die Elementbreite ist fill_parent(z. B. nehmen sie so viel horizontalen Platz wie möglich ein. Die Elemente sind benutzerdefinierte Ansichten.

Wie stelle ich die Höhe der Elemente so ein, dass sie ihrer variablen Breite entsprechen?

Anagaf
quelle

Antworten:

107

Es gibt eine einfachere Lösung, wenn GridView-Spalten gestreckt werden. Überschreiben Sie einfach das onMeasure des GridView-Elementlayouts mit ...

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
jdamcd
quelle
Funktioniert super. Nur ein paar Dinge, die ich in der Antwort übersehen habe: 1. Die Höhe wird ignoriert und die Breite wird als Höhe übergeben. 2. Die Methode sollte im Element und im Raster selbst überschrieben werden.
AlikElzin-Kilaka
12
Über 1: das macht es quadratisch - "
mDroidd
2
@KarthikPalanivelu Dies wird im Layout Ihrer Rasterelemente verwendet, nicht in der Rasteransicht selbst. Mit diesem Snippet können Sie RelativeLayout möglicherweise auf SquareRelativeLayout erweitern.
jdamcd
1
Wenn ich das in meiner Gridview mache, mache ich das GridView-Quadrat, nicht seine Kinder.
Daniel Bo
3
Eine vollständige Antwort wäre schön. Wollen Sie die Rasteransicht erweitern?
StarWind0
56

Oder wenn Sie eine Ansicht erweitern möchten, tun Sie einfach etwas ganz Einfaches wie:

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

Es ist erwähnenswert, dass dies super.onMeasure()nicht benötigt wird. onMeasuredVoraussetzung ist, dass Sie anrufen müssen setMeasuredDimension.

Chris.Jenkins
quelle
2
Funktioniert wie Charme .. Danke Chris :)
Karthik Andhamil
2
@ Chris.Jenkins Ich bin mit Ihrem letzten Kommentar nicht einverstanden: Wenn ich nicht super.onMeasure()anrufe, wird eine darin platzierte ImageView nicht gezeichnet. Es scheint also als erste Zeile der onMeasureMethode erforderlich zu sein .
Cyrilchampier
@nerith, ist dies auf einem bestimmten Gerät, da ich diesen Code seit sehr langer Zeit verwende und nie in Probleme versinke. Interessiert zu sehen, wie man reproduziert, was man sieht.
Chris.Jenkins
@ Chris.Jenkins nicht auf einem bestimmten Gerät. Mein SquareFrameLayout wird aus der XML geladen. Fügen Sie Ihre Kinder programmatisch hinzu?
Cyrilchampier
Oh ja, das funktioniert nicht nur mit Ansichtsgruppenansichten. Vielleicht sollte ich das erwähnen.
Chris.Jenkins
16

Ich bin mir nicht sicher, ob dies mit den aktuellen Widgets möglich ist. Am besten fügen Sie Ihre benutzerdefinierte Ansicht in eine benutzerdefinierte "SquareView" ein. Diese Ansicht kann nur eine untergeordnete Ansicht enthalten und die Höhe so erzwingen, dass sie der Breite entspricht, wenn die onLayout-Methode aufgerufen wird.

Ich habe nie versucht, so etwas zu tun, aber ich denke, es sollte nicht zu schwierig sein. Eine alternative (und möglicherweise etwas einfachere) Lösung könnte darin bestehen, das Stammlayout Ihrer benutzerdefinierten Ansicht in Unterklassen zu unterteilen (z. B. wenn es sich um ein LinearLayout handelt, ein SquareLinearLayout zu erstellen) und dieses stattdessen als Container zu verwenden.

edit: Hier ist eine grundlegende Implementierung, die für mich zu funktionieren scheint:

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

Es ist so konzipiert, dass es ein einzigartiges Kind hat, aber ich habe der Einfachheit halber alle Überprüfungen ignoriert. Die Grundidee besteht darin, das untergeordnete Element mit den von GridView festgelegten Breiten- / Höhenparametern zu messen (in meinem Fall wird numColumns = 4 zur Berechnung der Breite verwendet) und anschließend einen zweiten Durchgang mit den endgültigen Abmessungen mit height = width durchzuführen. .. Das Layout ist nur ein einfaches Layout, das das eindeutige untergeordnete Element bei (0, 0) mit den gewünschten Abmessungen (rechts-links, unten-oben) anordnet.

Und hier ist das XML, das für die GridView-Elemente verwendet wird:

<?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>

Ich habe ein LinearLayout in SquareView verwendet, damit es alle Schwerkraftmöglichkeiten, Ränder usw. verwaltet.

Ich bin nicht sicher, wie gut (oder schlecht) dieses Widget auf Orientierungs- und Dimensionsänderungen reagieren würde, aber es scheint richtig zu funktionieren.

Gregory
quelle
Das klingt alles gut, das einzige Problem ist, onLayout (oder onMeasure) richtig zu schreiben. Aktuell Ich habe Höhe / Breite in ItemAdapter.getView () festgelegt, aber diese Lösung gefällt mir nicht.
Anagaf
@anagaf Ich habe meine Antwort mit Code bearbeitet, den ich gerade getestet habe und der zu funktionieren scheint. Es ist ziemlich leicht zu verstehen, daher sollte es leicht zu beheben sein, falls Sie Pannen finden ^^ (nicht sicher, ob ich eine zweite Antwort bearbeiten oder posten sollte, sorry, wenn ich es falsch gemacht habe!)
Gregory
1
Das obige Layout funktioniert nicht --- Ich weiß nicht warum, aber es gibt subtile Layoutprobleme bei den Kindern, die dazu führen, dass beispielsweise TextViews nicht umbrochen werden und RelativeLayouts schrecklich verwirrt werden. Allerdings habe ich hier ein Äquivalent gefunden: stackoverflow.com/questions/2948212/… ... obwohl ich es noch ein wenig anpassen musste (die Größe basiert auf der größten Abmessung und nicht nur auf der Breite).
David gegeben
12

Es ist ziemlich einfach, das Gitterelement quadratisch zu machen.

Führen Sie in Ihrer "GetView" -Methode Ihres Grid-Adapters einfach Folgendes aus:

@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
quelle
Dies ist die einzige Lösung, die für mich funktioniert hat ... Vielen Dank !!
Zeba
3
musste "getRequestedColumnWidth" durch "getColumnWidth" ersetzen und es hat bei mir funktioniert .. danke!
Ayad Kara Kâhya
1
hier gilt das gleiche: Ich hatte zu ersetzen getRequestedColumnWidthmit getColumnWidthund arbeitete für mich ... danke!
Yahya
6

Ich weiß nicht, ob dies der beste Weg ist, aber ich schaffe es, dass meine benutzerdefinierte Ansicht onSizeChanged auf diese Weise überschreibt:

@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());
    }
}

Ich verwende diese benutzerdefinierte Ansicht in einem LinearLayout und einer GridView und in beiden wird sie quadratisch angezeigt!

Luckcheese
quelle
Oh Mann, du bist ein Lebensretter, ich habe seit Tagen damit gekämpft. Gregorys Antwort funktioniert gut, es sei denn, Sie haben viele Kinder. Wie ein Kommentar hervorhebt, werden TextViews dann nicht umgebrochen und die meisten Ansichten passen ihre Grenzen nicht neu an. Aber das behebt es!
Makario
6

Das hat bei mir funktioniert!

Wenn Sie wie ich sind und getRequestedColumnWidth () nicht verwenden können, weil Ihr minSdk kleiner als 16 ist, empfehle ich diese Option.

  1. in deinem Fragment:

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

  2. in Ihrem Adapter (in 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: (zum Beispiel)

    <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" />

Hoffe es wird jemandem helfen!

Anne-Claire
quelle
5

Wenn Sie eine statische Anzahl von Spalten in der XML festlegen, können Sie die Breite der Ansicht durch die Anzahl der Spalten dividieren.

Wenn Sie verwenden, auto_fitwird es etwas schwierig sein, die Spaltenanzahl zu ermitteln (es gibt keine Methode getColumnCount ()). Diese Frage sollte Ihnen übrigens irgendwie helfen.

Dies ist der Code, den ich in der getView(...)Methode des Adapters mit einer festen Anzahl von Spalten verwende:

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

Versuchen

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

Sie können auch mit anderen Modi spielen

Kunst
quelle
4

Dies ist, was ich tue, um quadratische Zellen in einer GridView anzuzeigen. Ich bin mir nicht sicher, ob Sie quadratische Zellen oder etwas anderes wollen.

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

Und dann muss ich für die Ansicht, die ich für jede Zelle erstelle, Folgendes tun:

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

Basierend auf SteveBorkmans Antwort , die API 16+ lautet:

@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;
}
Joaquin Iurchuk
quelle
1

In Anlehnung an @ Gregorys Antwort musste ich mein Bild in ein LinearLayout einbinden, um zu verhindern, dass GridView seine Abmessungen vom Quadrat aus manipuliert. Beachten Sie, dass das äußere LinearLayout so eingestellt sein sollte, dass es die Dimension des Bildes aufweist, und dass die Breite der GridView-Spalte der Breite des Bildes und eines beliebigen Randes entspricht. Zum Beispiel:

    <!-- 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>

und das GridView wie:

   <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
quelle
-1

In Ihrer Tätigkeit

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

im CustomAdapter der GridView

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