web-dev-qa-db-fra.com

Android ListView avec différentes dispositions pour chaque ligne

J'essaie de déterminer le meilleur moyen d'avoir un seul ListView contenant différentes dispositions pour chaque ligne. Je sais comment créer une ligne personnalisée + un adaptateur de tableau personnalisé pour prendre en charge une ligne personnalisée pour la vue liste entière, mais comment puis-je implémenter de nombreux styles de ligne différents dans ListView?

337
w.donahue

Puisque vous savez combien de types de mise en page vous auriez, il est possible d'utiliser ces méthodes.

getViewTypeCount() - cette méthode retourne des informations sur le nombre de types de lignes que vous avez dans votre liste.

getItemViewType(int position) - renvoie le type de mise en page à utiliser en fonction de la position

Ensuite, vous gonflez la présentation uniquement si elle est nulle et déterminez le type à l'aide de getItemViewType.

Regardez ce tutoriel pour plus d'informations.

Pour réaliser certaines optimisations de la structure que vous avez décrites dans un commentaire, je suggérerais:

  • Stockage de vues dans un objet appelé ViewHolder. Cela augmenterait la vitesse car vous n’auriez pas à appeler findViewById() à chaque fois dans la méthode getView. Voir List14 dans les démos de l'API .
  • Créez une présentation générique qui conformera toutes les combinaisons de propriétés et masquera certains éléments si la position actuelle ne l’a pas.

J'espère que cela vous aidera. Si vous pouviez fournir au fichier XML une structure de données et des informations indiquant exactement comment vous souhaitez le mapper en ligne, je serais en mesure de vous conseiller plus précisément. En pixel.

409
Cristian

Je sais comment créer une ligne personnalisée + un adaptateur de baie personnalisé pour prendre en charge une ligne personnalisée pour la totalité de la vue liste. Mais comment une listview peut-elle prendre en charge de nombreux styles de lignes différents?

Vous connaissez déjà les bases. Vous devez simplement demander à votre adaptateur personnalisé de renvoyer une présentation/vue différente en fonction des informations de ligne/curseur fournies.

Un ListView peut prendre en charge plusieurs styles de ligne car il dérive de AdapterView :

Un AdapterView est une vue dont les enfants sont déterminés par un adaptateur.

Si vous examinez Adapter , vous verrez les méthodes permettant d’utiliser des vues spécifiques à une ligne:

abstract int getViewTypeCount()
// Returns the number of types of Views that will be created ...

abstract int getItemViewType(int position)
// Get the type of View that will be created ...

abstract View getView(int position, View convertView, ViewGroup parent)
// Get a View that displays the data ...

Les deux dernières méthodes indiquez la position afin que vous puissiez l'utiliser pour Déterminez le type de vue, vous devez utiliser pour cette ligne.


Bien sûr, vous n'utilisez généralement pas directement AdapterView et Adapter, mais utilisez ou dérivez de l'une de leurs sous-classes. Les sous-classes de l'adaptateur peuvent ajouter des fonctionnalités supplémentaires qui modifient la manière d'obtenir des présentations personnalisées pour différentes lignes. La vue utilisée pour une ligne donnée étant gérée par l'adaptateur, l'astuce consiste à faire en sorte que l'adaptateur renvoie la vue souhaitée pour une ligne donnée. La procédure à suivre varie selon l’adaptateur utilisé.

Par exemple, pour utiliser ArrayAdapter,

  • écrasez getView() pour gonfler, peupler et retourner la vue souhaitée pour la position donnée. La méthode getView() inclut une vue de réutilisation des opportunités via le paramètre convertView.

Mais utiliser des dérivés de CursorAdapter,

  • remplacez newView() pour gonfler, remplir et renvoyer la vue souhaitée pour l'état actuel du curseur (c'est-à-dire la "rangée" actuelle) [vous devez également remplacer bindView pour que le widget puisse réutiliser les vues]

Cependant, pour utiliser SimpleCursorAdapter,

  • définissez un SimpleCursorAdapter.ViewBinder avec une méthode setViewValue() pour gonfler, remplir et renvoyer la vue souhaitée pour une ligne donnée (état actuel du curseur) et une "colonne" de données. La méthode peut définir uniquement les vues "spéciales" et différer le comportement standard de SimpleCursorAdapter pour les liaisons "normales".

Recherchez des exemples/tutoriels spécifiques pour le type d’adaptateur que vous utilisez.

62
Bert F

Jetez un oeil dans le code ci-dessous.

Tout d'abord, nous créons des mises en page personnalisées. Dans ce cas, quatre types.

even.xml

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:background="#ff500000"
    Android:layout_height="match_parent">

    <TextView
        Android:id="@+id/text"
        Android:textColor="@Android:color/white"
        Android:layout_width="match_parent"
        Android:layout_gravity="center"
        Android:textSize="24sp"
        Android:layout_height="wrap_content" />

 </LinearLayout>

odd.xml

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:background="#ff001f50"
    Android:gravity="right"
    Android:layout_height="match_parent">

    <TextView
        Android:id="@+id/text"
        Android:textColor="@Android:color/white"
        Android:layout_width="wrap_content"
        Android:layout_gravity="center"
        Android:textSize="28sp"
        Android:layout_height="wrap_content"  />

 </LinearLayout>

white.xml

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:background="#ffffffff"
    Android:gravity="right"
    Android:layout_height="match_parent">

    <TextView
        Android:id="@+id/text"
        Android:textColor="@Android:color/black"
        Android:layout_width="wrap_content"
        Android:layout_gravity="center"
        Android:textSize="28sp"
        Android:layout_height="wrap_content"   />

 </LinearLayout>

black.xml

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:background="#ff000000"
    Android:layout_height="match_parent">

    <TextView
        Android:id="@+id/text"
        Android:textColor="@Android:color/white"
        Android:layout_width="wrap_content"
        Android:layout_gravity="center"
        Android:textSize="33sp"
        Android:layout_height="wrap_content"   />

 </LinearLayout>

Ensuite, nous créons l'élément listview. Dans notre cas, avec une chaîne et un type.

public class ListViewItem {
        private String text;
        private int type;

        public ListViewItem(String text, int type) {
            this.text = text;
            this.type = type;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }

    }

Après cela, nous créons un détenteur de vue. Il est vivement recommandé car Android OS conserve la référence de la disposition pour réutiliser votre élément lorsqu'il disparaît et qu'il réapparaît à l'écran. Si vous n'utilisez pas cette approche, chaque fois que votre élément apparaît à l'écran Android le système d'exploitation en crée un nouveau et entraîne la perte de mémoire de votre application.

public class ViewHolder {
        TextView text;

        public ViewHolder(TextView text) {
            this.text = text;
        }

        public TextView getText() {
            return text;
        }

        public void setText(TextView text) {
            this.text = text;
        }

    }

Enfin, nous créons notre adaptateur personnalisé en remplaçant les méthodes getViewTypeCount () et getItemViewType (int position).

public class CustomAdapter extends ArrayAdapter {

        public static final int TYPE_ODD = 0;
        public static final int TYPE_EVEN = 1;
        public static final int TYPE_WHITE = 2;
        public static final int TYPE_BLACK = 3;

        private ListViewItem[] objects;

        @Override
        public int getViewTypeCount() {
            return 4;
        }

        @Override
        public int getItemViewType(int position) {
            return objects[position].getType();
        }

        public CustomAdapter(Context context, int resource, ListViewItem[] objects) {
            super(context, resource, objects);
            this.objects = objects;
        }

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

            ViewHolder viewHolder = null;
            ListViewItem listViewItem = objects[position];
            int listViewItemType = getItemViewType(position);


            if (convertView == null) {

                if (listViewItemType == TYPE_EVEN) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_even, null);
                } else if (listViewItemType == TYPE_ODD) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_odd, null);
                } else if (listViewItemType == TYPE_WHITE) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_white, null);
                } else {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_black, null);
                }

                TextView textView = (TextView) convertView.findViewById(R.id.text);
                viewHolder = new ViewHolder(textView);

                convertView.setTag(viewHolder);

            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            viewHolder.getText().setText(listViewItem.getText());

            return convertView;
        }

    }

Et notre activité ressemble à ceci:

private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main); // here, you can create a single layout with a listview

        listView = (ListView) findViewById(R.id.listview);

        final ListViewItem[] items = new ListViewItem[40];

        for (int i = 0; i < items.length; i++) {
            if (i == 4) {
                items[i] = new ListViewItem("White " + i, CustomAdapter.TYPE_WHITE);
            } else if (i == 9) {
                items[i] = new ListViewItem("Black " + i, CustomAdapter.TYPE_BLACK);
            } else if (i % 2 == 0) {
                items[i] = new ListViewItem("EVEN " + i, CustomAdapter.TYPE_EVEN);
            } else {
                items[i] = new ListViewItem("ODD " + i, CustomAdapter.TYPE_ODD);
            }
        }

        CustomAdapter customAdapter = new CustomAdapter(this, R.id.text, items);
        listView.setAdapter(customAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView adapterView, View view, int i, long l) {
                Toast.makeText(getBaseContext(), items[i].getText(), Toast.LENGTH_SHORT).show();
            }
        });

    }
}

maintenant, créez une liste à l'intérieur de mainactivity.xml comme ceci

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="true"
    tools:context="com.example.shivnandan.gygy.MainActivity">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            Android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </Android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <ListView
        Android:layout_width="match_parent"

        Android:layout_height="match_parent"

        Android:id="@+id/listView"
        Android:layout_alignParentRight="true"
        Android:layout_alignParentEnd="true"


        Android:layout_marginTop="100dp" />

</Android.support.design.widget.CoordinatorLayout>
42
shiv

Dans votre adaptateur de baie personnalisé, vous substituez la méthode getView (), comme vous le connaissez sans doute. Il vous suffit ensuite d'utiliser une instruction switch ou une instruction if pour renvoyer une certaine vue personnalisée en fonction de l'argument de position transmis à la méthode getView. Android est intelligent en ce sens qu'il ne vous donnera qu'une convertView du type approprié pour votre position/rangée; vous n'avez pas besoin de vérifier qu'il est du type correct. Vous pouvez aider Android à cela en redéfinissant les méthodes getItemViewType () et getViewTypeCount () de manière appropriée.

14
Jems

Si nous devons afficher un type de vue différent dans une vue en liste, il est préférable d’utiliser getViewTypeCount () et getItemViewType () dans l’adaptateur au lieu de basculer entre les vues VIEW.GONE et VIEW.VISIBLE peut être une tâche très coûteuse dans getView () qui affecter le défilement de la liste.

Cochez cette option pour l'utilisation de getViewTypeCount () et getItemViewType () dans Adapter.

Lien: the-use-of-getviewtypecount

4
kyogs

ListView était destiné à des cas d'utilisation simples, comme la même vue statique pour tous les éléments de ligne.
Étant donné que vous devez créer ViewHolders et utiliser de manière significative getItemViewType(), et afficher de manière dynamique différents xml de disposition d'élément de ligne, vous devriez essayer de le faire en utilisant RecyclerView , qui est disponible. dans Android API 22. Elle offre un meilleur support et une meilleure structure pour plusieurs types de vues.

Découvrez ceci tutoriel sur l'utilisation de RecyclerView pour faire ce que vous cherchez.

1
Phileo99