web-dev-qa-db-fra.com

Comment avoir un GridView qui adapte sa hauteur lorsque des éléments sont ajoutés

J'essaie d'afficher une liste de chaînes de plus en plus dynamique avec une case à cocher dans un GridView, lui-même dans un TableLayout.

Je peux afficher ces chaînes "cochées" bien dans une rangée. Mon problème survient lorsque je laisse l'utilisateur ajouter dynamiquement de nouvelles chaînes dans GridView.

J'ai créé un adaptateur personnalisé qui reçoit la liste des chaînes. Disons que nous avons n chaînes. L'adaptateur renvoie 'n + 1' pour le nombre d'éléments. Dans getView, il retourne:

  • une vue avec un LinearLayout, ayant elle-même un CheckBox et un EditText pour les n premiers éléments,
  • un LinearLayout avec un simple bouton, avec une légende '+' pour l'élément 'n + 1'.

Jusqu'ici tout va bien. Lorsque le bouton '+' est cliqué, j'ajoute une chaîne vide à la liste des chaînes et appelle notifyDataSetChanged dans l'adaptateur. 

Le GridView se redessine avec un élément supplémentaire. MAIS il garde sa hauteur d'origine et crée une barre de défilement verticale. J'aimerais que le GridView augmente sa hauteur (c'est-à-dire qu'il prenne plus de place à l'écran et affiche tous les éléments).

J'ai essayé de changer l'écran en LinearLayout vertical au lieu de TableLayout, mais les résultats sont les mêmes.

Voici la disposition de mon écran:

<ScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:scrollbars="none">
<LinearLayout
    Android:layout_width="fill_parent"
    Android:layout_height="wrap_content"
    Android:orientation="vertical"
    >

    <!-- other lines omitted -->

    <LinearLayout Android:layout_width="fill_parent" 
                  Android:layout_height="wrap_content" >
        <TextView
            Android:layout_width="wrap_content" 
            Android:layout_height="wrap_content"
            Android:text="@string/wine_rack_explanation"
            Android:layout_gravity="center_vertical" />
          <GridView
            Android:layout_width="fill_parent" 
            Android:layout_height="wrap_content"
            Android:id="@+id/gvRack"
            Android:columnWidth="90dp"    
            Android:numColumns="auto_fit" />
    </LinearLayout>

<!-- other lines omitted -->

</LinearLayout>
</ScrollView>

La case à cocher + élément de chaîne de l'adaptateur est définie comme suit:

<LinearLayout
  xmlns:Android="http://schemas.Android.com/apk/res/Android"
  Android:orientation="horizontal"

  Android:layout_width="wrap_content"
  Android:layout_height="wrap_content">
    <CheckBox Android:layout_width="wrap_content"
              Android:layout_height="wrap_content"
              Android:id="@+id/cbCoordinate"
              Android:checked="true"
              />
    <EditText Android:id="@+id/txtCoordinate"
              Android:layout_width="wrap_content"
              Android:layout_height="wrap_content" />
</LinearLayout>

Le bouton «+» est défini comme suit:

<LinearLayout
  xmlns:Android="http://schemas.Android.com/apk/res/Android"
  Android:orientation="horizontal"
  Android:layout_width="wrap_content"
  Android:layout_height="wrap_content">

    <Button Android:id="@+id/btnAddBottle"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="@string/add_bottle_caption" />
</LinearLayout>

J'ai essayé d'appeler invalidate ou invalideViews sur GridView après l'ajout d'un élément. Aussi appelé invalider sur TableLayout et TableRow (dans la disposition précédente de l'écran). Sans succès.

Aucune idée pourquoi le GridView refuse d'étendre sa hauteur?

(Notez que je suis tout à fait disposé à utiliser un autre groupe de vues que le GridView)

27
Timores

Utilisez ce code

public class MyGridView  extends GridView {

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

    public MyGridView(Context context) {
        super(context);
    }

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

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }
}
49
Raj008

J'ai fait comme ça:

Ce code définira GridView height selon nombre de enfant .

Remarque: 5 est le nombre de colonnes.

private void setDynamicHeight(GridView gridView) {
        ListAdapter gridViewAdapter = gridView.getAdapter();
        if (gridViewAdapter == null) {
            // pre-condition
            return;
        }

        int totalHeight = 0;
        int items = gridViewAdapter.getCount();
        int rows = 0;

        View listItem = gridViewAdapter.getView(0, null, gridView);
        listItem.measure(0, 0);
        totalHeight = listItem.getMeasuredHeight();

        float x = 1;
        if( items > 5 ){
            x = items/5;
            rows = (int) (x + 1);
            totalHeight *= rows;
        }

        ViewGroup.LayoutParams params = gridView.getLayoutParams();
        params.height = totalHeight;
        gridView.setLayoutParams(params);
    }

J'espère que ceci vous aidera.

8
Hiren Patel

Lorsque onMeasure () est appelé pour GridView, si heightMode est MeasureSpec.UNSPECIFIED, la vue grille sera aussi grande que tous ses éléments contenus (plus un remplissage).

Pour vous assurer que c'est le cas, vous pouvez créer une vue personnalisée rapide étendant GridView et incluant le remplacement suivant: 

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
    }
4
Graham

Cela fonctionne pour moi:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/root"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:background="@drawable/background" >

        <!-- MORE LAYOUTS IF YOU WANT -->

    <ScrollView
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:layout_weight="1"
    Android:fillViewport="true" >

    <RelativeLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content" >

                <!-- MORE LAYOUTS IF YOU WANT -->

       <GridView
            Android:id="@+id/gridview"
            Android:layout_width="wrap_content"
            Android:layout_height="match_parent"
            Android:layout_marginBottom="10dp"
            Android:layout_marginLeft="20dp"
            Android:layout_marginRight="20dp"
            Android:layout_marginTop="5dp"
            Android:gravity="center"
            Android:horizontalSpacing="10dp"
            Android:numColumns="auto_fit"
            Android:stretchMode="columnWidth"
            Android:verticalSpacing="10dp" >
       </GridView>

             <!-- MORE LAYOUTS IF YOU WANT -->

    </RelativeLayout>

    </ScrollView>

</RelativeLayout>
4
Neonigma

La solution de Graham n'a pas fonctionné pour moi. Cependant celui-ci (avec un peu plus de piratage) a: https://stackoverflow.com/a/8483078/479478

1
Eric Chen

Cela fonctionne pour moi

public class MyGridView extends GridView {
    public MyGridView(Context context) {
        super(context);
    }

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

    public MyGridView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

        // set childs height to same value as child that has highest value
        for(int i=0; i<getChildCount(); i++) {
            MyLinearLayout child = (MyLinearLayout) getChildAt(i);
            AbsListView.LayoutParams params = (AbsListView.LayoutParams) child.getLayoutParams();
            params.height = MyLinearLayout.maxHeight;
            child.setLayoutParams(params);
        }

        // calculate total height and apply to the grid
        double numRow = Math.ceil((double) getCount() / (double) getNumColumns());
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) this.getLayoutParams();
        params.height = (int) numRow *  MyLinearLayout.maxHeight;
        this.setLayoutParams(params);

        // invalidate after change grid's height
        this.invalidate();
    }
}

Classe MyLinearLayout

public class MyLinearLayout extends LinearLayout {
    public static int maxHeight = 0;

    public MyLinearLayout(Context context) {
        super(context);
    }

    public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (maxHeight<this.getMeasuredHeight()) maxHeight=this.getMeasuredHeight();
    }
}

Le GridView contient des enfants de MyLinearLayout, et voici la disposition

<?xml version="1.0" encoding="utf-8"?>
<com.mysystem.view.MyLinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
              Android:layout_width="100dp"
              Android:layout_height="100dp"
              Android:background="#ddd"
              Android:padding="0.5dp"
              Android:layout_gravity="top"
              Android:gravity="center_horizontal"
              Android:orientation="vertical">

    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:background="#fff"
        Android:padding="20dp"
        Android:orientation="vertical">

        <ImageView
            Android:id="@+id/img_menu"
            Android:layout_width="30dp"
            Android:layout_gravity="center_horizontal"
            Android:layout_height="30dp"
            Android:layout_marginTop="5dp"/>

        <TextView
            Android:id="@+id/tv_menu"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layout_marginTop="5dp"
            Android:textAlignment="center"
            Android:textColor="#000"
            Android:text="Title"
            Android:textSize="10sp"/>

    </LinearLayout>
</com.mysystem.view.MyLinearLayout>
0