2009-11-15 5 views
157

Sur Android, comment puis-je un ListView qui filtre en fonction de l'entrée utilisateur, où les éléments affichés sont mis à jour dynamiquement en fonction de la valeur TextView?Comment faire pour mettre à jour dynamiquement un ListView sur Android

Je cherche quelque chose comme ceci:

------------------------- 
| Text View    | 
------------------------- 
| List item    | 
| List item    | 
| List item    | 
| List item    | 
|      | 
|      | 
|      | 
|      | 
------------------------- 
+7

Je nomine pour la réouverture. J'ai mis à jour le texte pour en faire une question plus pertinente, et il s'agit clairement d'une ressource communautaire précieuse, car les réponses sont toujours en cours. – Hamy

+0

Veuillez rouvrir la question. C'est clairement utile. – SilentNot

Répondre

286

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> 

Ceci affichera tout correctement, avec un bon EditText au-dessus du ListView. Ensuite, créez une ListActivity comme vous le feriez normalement, mais ajoutez un appel setContentView() dans la méthode onCreate() afin que nous utilisions notre disposition récemment déclarée. Rappelez-vous que nous avons identifié le ListView spécialement, avec android:id="@android:id/list". Cela permet au ListActivity de savoir quel ListView nous voulons utiliser dans notre disposition 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()); 
    } 

Exécution de l'application devrait maintenant montrer votre ListView précédente, avec une jolie boîte ci-dessus. Pour que cette boîte fasse quelque chose, nous devons en prendre l'entrée et faire en sorte que cette entrée filtre la liste. Alors que beaucoup de gens ont essayé de le faire manuellement, la plupart des classesListViewAdapter viennent avec un objet Filter qui peut être utilisé pour effectuer le filtrage automagiquement. Nous avons juste besoin de canaliser l'entrée du EditText dans 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 ListAdapter à une variable pour faire ce travail - je l'ai sauvé ArrayAdapter<String> de plus tôt dans une variable appelée « adaptateur '. L'étape suivante consiste à obtenir l'entrée de EditText. Cela nécessite 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 «œil». Tant que l'utilisateur n'a pas choisi 'wyw' ou 'eye', votre OnKeyListener ne recevra aucun événement clé. Certains peuvent préférer cette solution, mais j'ai trouvé cela frustrant. Je voulais tous les événements clés, j'ai donc eu le choix de filtrer ou de ne pas filtrer. La solution est un TextWatcher. Il suffit de créer et d'ajouter un TextWatcher au EditText, et de passer le ListAdapterFilter à chaque fois que le texte change. N'oubliez pas d'enlever le TextWatcher dans 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); 
} 
+7

Y at-il un moyen simple de filtrer ListView dans "contient" au lieu de "commence avec" la mode comme le fait cette solution? –

+12

Viktor - Si les mots qui vous intéressent sont séparés par des espaces, alors il le fera automatiquement. Sinon, pas vraiment. Probablement le moyen le plus simple serait de sous-classer l'adaptateur en l'étendant et de surcharger la méthode getFilter pour renvoyer un objet Filter que vous avez défini. Voir http://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/ArrayAdapter.java#L449 pour comprendre comment fonctionne ArrayFilter par défaut - il serait simple de copier 95% de ce code et modifier les lignes 479 et 486 – Hamy

+0

qu'est-ce que getStringArrayList(), je lance ce code, mais ne peux pas obtenir l'effet de filtre, peux-tu m'aider – pengwang

10

L'exécution du programme entraîne la fermeture d'une force.

Je swaped la ligne:

android:id="@+building_list/search_box"

avec

android:id="@+id/search_box"

pourrait-il être le problème? A quoi sert le '@ + building_list'?

+0

Johe, quand vous dites R.idquelque chose, le quelque chose existe dans id parce que vous avez 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é avec une compilation de test, donc j'ai probablement laissé au moins une erreur quelque part – Hamy

+0

Salut Hamy, vous avez référencé "@ + building_list/search_box" dans le code avec "filterText = (EditText) findViewById (R.id. Barre de recherche);" C'est pourquoi je me demandais. – j7nn7k

+1

Le forceclose occorrs lors du démarrage de taper. Une partie de l'erreur: ERROR/AndroidRuntime (188): java.lang.NullPointerException 02-11 07: 30: 29.828: ERROR/AndroidRuntime (188): à xxx.com .ListFilter $ 1.onTextChanged (ListFilter.java:46) 02-11 07: 30: 29.828: ERROR/AndroidRuntime (188): à l'adresse android.widget.TextView.sendOnTextChanged (TextView.java:6102) 02-11 07: 30: 29.828: ERROR/AndroidRuntime (188): à l'adresse android.widget.TextView.handleTextChanged (TextView.java:6143) 02-11 07: 30: 29.828: ERROR/AndroidRuntime (188): at android.widget.TextView $ ChangeWatcher.onTextChanged (TextView.java:6286) – j7nn7k

4

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

donc avant filtrage (début d'activité) i créé une copie de sauvegarde de la liste .. (juste une autre liste contenant les mêmes données)

sur le filtrage, le filtre et listadapter est reliée à la liste primaire.

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

, ce qui assurait dans mon cas, que la liste a été immédiatement mis à jour et même sur la suppression de caractères-terme-recherche la liste est restaurée avec succès dans tous les cas :)

merci pour cette solution de toute façon.

Questions connexes