Comment mettre à jour dynamiquement un ListView sur Android [fermé]

159

Sur Android, comment puis-je ListViewfiltrer en fonction de l'entrée de l'utilisateur, où les éléments affichés sont mis à jour dynamiquement en fonction de la TextViewvaleur?

Je cherche quelque chose comme ça:

-------------------------
| Text View             |
-------------------------
| List item             |
| List item             |
| List item             |
| List item             |
|                       |
|                       |
|                       |
|                       |
-------------------------
Hamy
la source
7
Je propose une réouverture. J'ai mis à jour le texte pour en faire plus une question, et c'est clairement une ressource communautaire précieuse car les réponses arrivent toujours
Hamy
Veuillez rouvrir la question. C'est clairement utile.
SilentNot

Réponses:

286

Tout d'abord, vous devez créer une mise en page XML qui a à la fois un EditText et un ListView.

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <!-- Pretty hint text, and maxLines -->
    <EditText android:id="@+building_list/search_box" 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="type to filter"
        android:inputType="text"
        android:maxLines="1"/>

    <!-- Set height to 0, and let the weight param expand it -->
    <!-- Note the use of the default ID! This lets us use a 
         ListActivity still! -->
    <ListView android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1" 
         /> 

</LinearLayout>

Cela mettra tout en place correctement, avec un joli EditText au-dessus de ListView. Ensuite, créez un ListActivity comme vous le feriez normalement, mais ajoutez un setContentView()appel à la onCreate()méthode afin que nous utilisions notre disposition récemment déclarée. N'oubliez pas que nous avons identifié le ListViewspécialement, avec android:id="@android:id/list". Cela permet au ListActivityde savoir ce que ListViewnous voulons utiliser dans notre mise en page déclarée.

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

        setContentView(R.layout.filterable_listview);

        setListAdapter(new ArrayAdapter<String>(this,
                       android.R.layout.simple_list_item_1, 
                       getStringArrayList());
    }

L'exécution de l'application maintenant devrait afficher votre précédente ListView, avec une belle boîte au-dessus. Pour que cette boîte fasse quelque chose, nous devons en extraire l'entrée et faire en sorte que cette entrée filtre la liste. Bien que beaucoup de gens aient essayé de le faire manuellement, la plupart des ListView Adapter classes sont livrées avec un Filterobjet qui peut être utilisé pour effectuer le filtrage automatiquement. Nous avons juste besoin de canaliser l'entrée du EditTextvers le Filter. Il s'avère que c'est assez facile. Pour exécuter un test rapide, ajoutez cette ligne à votre onCreate()appel

adapter.getFilter().filter(s);

Notez que vous devrez enregistrer votre fichier ListAdapterdans une variable pour que cela fonctionne - j'ai enregistré mon ArrayAdapter<String>de plus tôt dans une variable appelée «adaptateur».

L'étape suivante consiste à obtenir l'entrée du EditText. Cela demande un peu de réflexion. Vous pouvez ajouter un OnKeyListener()à votre EditText. Cependant, cet écouteur ne reçoit que certains événements clés . Par exemple, si un utilisateur entre «wyw», le texte prédictif recommandera probablement «eye». Tant que l'utilisateur ne choisit pas «wyw» ou «eye», vous OnKeyListenerne recevrez pas d'événement clé. Certains préfèrent peut-être cette solution, mais je l'ai trouvée frustrante. Je voulais chaque événement clé, donc j'avais le choix entre filtrer ou non filtrer. La solution est un TextWatcher. Créez simplement et ajoutez un TextWatcherà EditText, et transmettez la ListAdapter Filterdemande de filtre à chaque fois que le texte change. N'oubliez pas de supprimer le TextWatcherin OnDestroy()! Voici la solution finale:

private EditText filterText = null;
ArrayAdapter<String> adapter = null;

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

    setContentView(R.layout.filterable_listview);

    filterText = (EditText) findViewById(R.id.search_box);
    filterText.addTextChangedListener(filterTextWatcher);

    setListAdapter(new ArrayAdapter<String>(this,
                   android.R.layout.simple_list_item_1, 
                   getStringArrayList());
}

private TextWatcher filterTextWatcher = new TextWatcher() {

    public void afterTextChanged(Editable s) {
    }

    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
    }

    public void onTextChanged(CharSequence s, int start, int before,
            int count) {
        adapter.getFilter().filter(s);
    }

};

@Override
protected void onDestroy() {
    super.onDestroy();
    filterText.removeTextChangedListener(filterTextWatcher);
}
Hamy
la source
7
Existe-t-il un moyen simple de filtrer ListView en mode "contient" au lieu de "commence par" comme le fait cette solution?
Viktor Brešan
12
Viktor - Si les mots qui vous intéressent sont séparés par des espaces, il le fera automatiquement. Sinon, pas vraiment. Le moyen le plus simple serait probablement de sous-classer l'adaptateur en l'étendant et de remplacer la méthode getFilter pour renvoyer un objet Filter que vous définissez. Voir github.com/android/platform_frameworks_base/blob/master/core/… pour comprendre comment fonctionne le ArrayFilter par défaut - il serait simple de copier 95% de ce code et de changer les lignes 479 et 486
Hamy
2
Hamy, excellente rédaction! J'ai une question cependant: j'ai implémenté cela et après chaque lettre que je tape, le ListView disparaît pendant quelques secondes, puis revient, filtré. Avez-vous vécu cela? Mon intuition est que c'est parce que j'ai plus de 600 éléments dans la liste, avec des fonctions toString () non triviales.
lowellk
2
En mode paysage sur un écran plus petit, le clavier EditText + prend tout l'écran et la ListView n'est pas visible! Toute solution?
Martin Konicek
4
Est-ce vraiment nécessaire? > N'oubliez pas de supprimer le TextWatcher dans OnDestroy ()
Jojo
10

l'exécution du programme provoquera une fermeture forcée.

J'ai échangé la ligne:

android: id = "@ + liste_immeuble / boîte_recherche"

avec

android: id = "@ + id / search_box"

Est-ce que cela pourrait être le problème? À quoi sert la '@ + building_list'?

j7nn7k
la source
Johe, quand tu dis R.id.quelque chose, le quelque chose existe dans id parce que tu as dit android: id = "@ + id / search_box". Si vous dites android: id = "@ + building_list / search_box" alors dans le code, vous pouvez appeler findViewById (R.building_list.search_box); Quelle est l'exception de niveau supérieur que vous obtenez? Ce code est copié sans compilation de test, donc j'ai probablement laissé au moins une erreur quelque part
Hamy
Salut Hamy, vous avez référencé "@ + building_list / search_box" dans le code avec "filterText = (EditText) findViewById (R.id.search_box);" C'est pourquoi je me posais la question.
j7nn7k
1
La fermeture forcée se produit au début de la saisie. Une partie de l'erreur: ERROR / AndroidRuntime (188): java.lang.NullPointerException 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): at xxx.com.ListFilter $ 1. onTextChanged (ListFilter.java:46) 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): à android.widget.TextView.sendOnTextChanged (TextView.java:6102) 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): sur android.widget.TextView.handleTextChanged (TextView.java:6143) 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): sur android.widget.TextView $ ChangeWatcher.onTextChanged (TextView.java : 6286)
j7nn7k
Johe, Oups - il semble que j'essayais de modifier le code pour me débarrasser de la @ + building_list (parce que c'est un point de confusion) mais je ne l'ai pas attrapé partout. Merci pour le conseil! Le simple fait de le changer en @ + id devrait résoudre le problème
Hamy
4

J'ai eu un problème avec le filtrage, les résultats ont été filtrés, mais pas restaurés !

donc avant le filtrage (début de l'activité) j'ai créé une sauvegarde de liste .. (juste une autre liste, contenant les mêmes données)

lors du filtrage, le filtre et l'adaptateur de liste sont connectés à la liste principale.

mais le filtre lui-même utilisait les données de la liste sauvegardée.

cela garantissait dans mon cas que la liste était mise à jour immédiatement et même en supprimant les caractères des termes de recherche, la liste était restaurée avec succès dans tous les cas :)

merci pour cette solution de toute façon.

cV2
la source