web-dev-qa-db-fra.com

Comment actualiser la liste de diffusion Android?

Comment actualiser une Android ListView après avoir ajouté/supprimé des données dynamiques?

407
UMAR

Appelez notifyDataSetChanged() sur votre objet Adapter une fois que vous avez modifié les données de cet adaptateur. 

Certaines informations supplémentaires sur la manière et le moment d'appeler notifyDataSetChanged() peuvent être visualisées dans cette vidéo Google I/O .

509
Christopher Orr

Aussi, vous pouvez utiliser ceci:

myListView.invalidateViews();
197
Bobs

Veuillez ignorer toutes les réponses invalidate(), invalidateViews(), requestLayout(), ... à cette question.

La bonne chose à faire (et heureusement aussi marquée comme bonne réponse) est appeler notifyDataSetChanged() sur votre adaptateur .

Dépannage

Si appeler notifyDataSetChanged() ne fonctionne pas, toutes les méthodes de mise en page ne vous aideront pas non plus. Croyez-moi, la ListView a été correctement mise à jour. Si vous ne trouvez pas la différence, vous devez vérifier d’où proviennent les données de votre adaptateur.

S'il ne s'agit que d'une collection que vous gardez en mémoire, vérifiez que vous avez réellement supprimé ou ajouté le ou les éléments à la collection avant d'appeler la notifyDataSetChanged()

Si vous travaillez avec une base de données ou un service backend, vous devrez appeler la méthode pour récupérer les informations (ou manipuler les données en mémoire) avant d'appeler le notifyDataSetChanged().

Le problème est que notifyDataSetChanged ne fonctionne que si le jeu de données a été modifié. C’est donc l’endroit à regarder si vous ne trouvez pas les changements à venir. Déboguer si nécessaire. 

ArrayAdapter vs BaseAdapter

J'ai constaté que l'utilisation d'un adaptateur qui vous permet de gérer la collection, comme un BaseAdapter, fonctionne mieux. Certains adaptateurs comme ArrayAdapter gèrent déjà leur propre collection, ce qui rend plus difficile l'obtention de la collection appropriée pour les mises à jour. C'est vraiment juste une couche de difficulté supplémentaire inutile dans la plupart des cas.

Fil d'interface utilisateur

Il est vrai que cela doit être appelé à partir du thread d'interface utilisateur. D'autres réponses ont des exemples sur la façon d'y parvenir. Toutefois, cela n'est requis que si vous travaillez sur ces informations en dehors du thread d'interface utilisateur. Cela provient d'un service ou d'un thread non UI. Dans des cas simples, vous mettrez à jour vos données à partir d'un clic de bouton ou d'une autre activité/fragment. Donc, toujours dans le fil de l'interface utilisateur. Pas besoin de toujours insérer ce runOnUiTrhead dans.

Exemple de projet rapide

Peut être trouvé à https://github.com/hanscappelle/so-2250770.git . Il suffit de cloner et d’ouvrir le projet dans Android Studio (gradle). Ce projet a une MainAcitivity créant une ListView avec toutes les données aléatoires. Cette liste peut être actualisée en utilisant le menu Actions. 

L'implémentation d'adaptateur que j'ai créée pour cet exemple ModelObject expose la collecte de données.

public class MyListAdapter extends BaseAdapter {

    /**
     * this is our own collection of data, can be anything we 
     * want it to be as long as we get the abstract methods 
     * implemented using this data and work on this data 
     * (see getter) you should be fine
     */
    private List<ModelObject> mData;

    /**
     * our ctor for this adapter, we'll accept all the things 
     * we need here
     *
     * @param mData
     */
    public MyListAdapter(final Context context, final List<ModelObject> mData) {
        this.mData = mData;
        this.mContext = context;
    }

    public List<ModelObject> getData() {
        return mData;
    }

    // implement all abstract methods here
}

Code de la MainActivity 

public class MainActivity extends Activity {

    private MyListAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ListView list = (ListView) findViewById(R.id.list);

        // create some dummy data here
        List<ModelObject> objects = getRandomData();
        // and put it into an adapter for the list
        mAdapter = new MyListAdapter(this, objects);
        list.setAdapter(mAdapter);

        // mAdapter is available in the helper methods below and the 
        // data will be updated based on action menu interactions

        // you could also keep the reference to the Android ListView 
        // object instead and use the {@link ListView#getAdapter()} 
        // method instead. However you would have to cast that adapter 
        // to your own instance every time
    }

    /**
     * helper to show what happens when all data is new
     */
    private void reloadAllData(){
        // get new modified random data
        List<ModelObject> objects = getRandomData();
        // update data in our adapter
        mAdapter.getData().clear();
        mAdapter.getData().addAll(objects);
        // fire the event
        mAdapter.notifyDataSetChanged();
    }

    /**
     * helper to show how only changing properties of data 
     * elements also works
     */
    private void scrambleChecked(){
        Random random = new Random();
        // update data in our adapter, iterate all objects and 
        // resetting the checked option
        for( ModelObject mo : mAdapter.getData()) {
            mo.setChecked(random.nextBoolean());
        }
        // fire the event
        mAdapter.notifyDataSetChanged();
    }
}

Plus d'information

Un autre article de Nice sur le pouvoir de listViews se trouve ici: http://www.vogella.com/articles/AndroidListView/article.html

147
hcpl

Appelez exécutable quand vous le souhaitez:

runOnUiThread(run);

OnCreate(), vous définissez votre thread exécutable:

run = new Runnable() {
    public void run() {
        //reload content
        arraylist.clear();
        arraylist.addAll(db.readAll());
        adapter.notifyDataSetChanged();
        listview.invalidateViews();
        listview.refreshDrawableState();
    }
};
42
Marckaraujo

j'ai eu quelques problèmes avec l'actualisation dynamique de mon listview.

Appelez notifyDataSetChanged () sur votre adaptateur.

Quelques détails supplémentaires sur comment et quand appeler notifyDataSetChanged () peuvent être visualisés dans cette vidéo Google I/O.

notifyDataSetChanged () ne fonctionnait pas correctement dans mon cas [j'ai appelé le notifyDataSetChanged depuis une autre classe]. Juste dans le cas où j'ai édité le ListView dans l'activité en cours d'exécution (Thread). Cette vidéo grâce à Christopher a donné l’allusion finale.

Dans ma deuxième classe j'ai utilisé

Runnable run = new Runnable(){
     public void run(){
         contactsActivity.update();
     }
};
contactsActivity.runOnUiThread(run);

accéder à la mise à jour () de mon activité. Cette mise à jour comprend

myAdapter.notifyDataSetChanged();

indiquer à l'adaptateur d'actualiser la vue . a bien fonctionné dans la mesure du possible.

11
Wille

Si vous utilisez SimpleCursorAdapter essayez d'appeler requery () sur l'objet Cursor.

9
freehacker

si vous n'êtes toujours pas satisfait de ListView Refreshment, vous pouvez consulter cet extrait, destiné au chargement du listView à partir de la base de données. En réalité, vous devez simplement recharger le ListView après avoir effectué une opération CRUD meilleure façon de coder, mais cela actualisera le ListView à votre guise.

Cela fonctionne pour moi .... si vous trouvez une meilleure solution, s'il vous plaît partager ...

.......
......
 effectuez vos opérations CRUD .......................... DBAdapter. open (); 
 DBAdapter.insert_into_SingleList (); 
 // Apportez ce DB_results et ajoutez-le à la liste en tant que son contenu ....
 ls2.setAdapter (nouvel ArrayAdapter (DynTABSample.this, 
 Android.R.layout.simple_list_item_1, DBAdapter.DB_ListView)); 
 DBAdapter.close (); 
5
Nandagopal T

Les solutions proposées par les utilisateurs de ce post fonctionnent ou non principalement en fonction de la version Android de votre appareil. Par exemple, pour utiliser la méthode AddAll, vous devez mettre Android: minSdkVersion = "10" dans votre appareil Android.

Pour résoudre cette question pour tous les périphériques, j'ai créé ma propre méthode dans mon adaptateur et je l'utilise à l'intérieur de la méthode d'ajout et de suppression héritée de ArrayAdapter qui met à jour vos données sans problèmes.

Mon code: En utilisant ma propre classe de données RaceResult, vous utilisez votre propre modèle de données.

ResultGpRowAdapter.Java

public class ResultGpRowAdapter extends ArrayAdapter<RaceResult> {

    Context context;
    int resource;
    List<RaceResult> data=null;

        public ResultGpRowAdapter(Context context, int resource, List<RaceResult> objects)           {
        super(context, resource, objects);

        this.context = context;
        this.resource = resource;
        this.data = objects;
    }

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

        ........
        }

        //my own method to populate data           
        public void myAddAll(List<RaceResult> items) {

        for (RaceResult item:items){
            super.add(item);
        }
    }

RésultatsGp.Java

public class ResultsGp extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...........
    ...........
    ListView list = (ListView)findViewById(R.id.resultsGpList); 

    ResultGpRowAdapter adapter = new ResultGpRowAdapter(this,  R.layout.activity_result_gp_row, new ArrayList<RaceResult>()); //Empty data

   list.setAdapter(adapter);

   .... 
   ....
   ....
   //LOAD a ArrayList<RaceResult> with data

   ArrayList<RaceResult> data = new ArrayList<RaceResult>();
   data.add(new RaceResult(....));
   data.add(new RaceResult(....));
   .......

   adapter.myAddAll(data); //Your list will be udpdated!!!
2
AntuanSoft

Il suffit d'utiliser myArrayList.remove(position); dans un auditeur:

  myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override public void onItemClick(AdapterView<?> parent, Android.view.View view, int position, long id) {
           myArrayList.remove(position);
           myArrayAdapter.notifyDataSetChanged();
        }
    });
1
Steffo Dimfelt

Si vous souhaitez conserver votre position de défilement lors de l'actualisation, procédez comme suit:

if (mEventListView.getAdapter() == null) {
    EventLogAdapter eventLogAdapter = new EventLogAdapter(mContext, events);
    mEventListView.setAdapter(eventLogAdapter);
} else {
    ((EventLogAdapter)mEventListView.getAdapter()).refill(events);
}

public void refill(List<EventLog> events) {
    mEvents.clear();
    mEvents.addAll(events);
    notifyDataSetChanged();
}

Pour plus d'informations, consultez Android ListView: Conservez votre position de défilement lorsque vous actualisez .

1
Brandon Yang

Si vous souhaitez mettre à jour la vue liste de l'interface utilisateur à partir d'un service, réglez l'adaptateur dans votre activité principale et procédez comme suit: 

@Override
public void onDestroy() {
    if (MainActivity.isInFront == true) {
        if (MainActivity.adapter != null) {
            MainActivity.adapter.notifyDataSetChanged();
        }

        MainActivity.listView.setAdapter(MainActivity.adapter);
    }
}    
0
Mayank Saini

J'ai eu un ArrayList que je voulais afficher dans une liste. ArrayList contenait des éléments de mysql. J'ai remplacé la méthode onRefresh et dans cette méthode, j'ai utilisé tablelayout.removeAllViews (); et ensuite répète le processus pour récupérer les données de la base de données . Mais avant cela, assurez-vous d'effacer votre ArrayList ou toute autre structure de données, sinon de nouvelles données seront ajoutées à l'ancienne.

0
Kshitij G

Pour moi, après avoir modifié les informations de la base de données SQL, rien ne pouvait actualiser la vue liste (pour être une vue liste développable spécifique). Par conséquent, si notifyDataSetChanged () ne résout pas le problème, vous pouvez essayer d’effacer votre liste d’abord et de la rajouter après cet appel notifyDataSetChanged (). Par exemple

private List<List<SomeNewArray>> arrayList;
List<SomeNewArray> array1= getArrayList(...);
List<SomeNewArray> array2= getArrayList(...);
arrayList.clear();
arrayList.add(array1);
arrayList.add(array2);
notifyDataSetChanged();

J'espère que cela a du sens pour vous.

0
Aivus

Si je parle de mon scénario ici, aucune des réponses ci-dessus ne fonctionnera, car j’ai eu une activité montrant la liste des valeurs de la base de données avec un bouton de suppression. Lorsque vous appuyez sur un bouton de suppression, je souhaite supprimer cet élément de la liste.

La chose intéressante était que je n'utilisais pas la vue recycleur mais une vue liste simple et cette vue liste initialisée dans la classe d'adaptateur. Ainsi, l'appel de notifyDataSetChanged() ne fera rien dans la classe d'adaptateur et même dans la classe d'activité où l'objet d'adaptateur est initialisé car la méthode delete était dans la classe d'adaptateur.

La solution consistait donc à supprimer l'objet de l'adaptateur dans la méthode getView de la classe d'adaptateur (pour supprimer uniquement cet objet spécifique mais si vous souhaitez tout supprimer, appelez clear()).

À vous de vous faire une idée, à quoi ressemblait mon code,

public class WordAdapter extends ArrayAdapter<Word> {
  Context context;

  public WordAdapter(Activity context, ArrayList<Word> words) {}
    //.......

    @NonNull
    @Override
    public View getView(final int position, View convertView, ViewGroup group) {
      //.......
     ImageButton deleteBt = listItemView.findViewById(R.id.Word_delete_bt);
     deleteBt.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             if (vocabDb.deleteWord(currentWord.id)) {
                //.....
             } else{
                //.....
             }
             remove(getItem(position)); // <---- here is the trick ---<
             //clear() // if you want to clear everything
         }
    });
 //....

Remarque: ici, les méthodes remove() et getItem() sont héritées de la classe Adapter.

  • remove() - pour supprimer l'élément spécifique sur lequel l'utilisateur a cliqué
  • getItem(position) - est d’obtenir l’élément (ici, c’est mon objet Word que j’ai ajouté à la liste) à partir de la position cliquée.

Voici comment je règle l'adaptateur sur listview dans la classe d'activité,

    ArrayList<Word> wordList = new ArrayList();
    WordAdapter adapter = new WordAdapter(this, wordList);

    ListView list_view = (ListView) findViewById(R.id.activity_view_words);
    list_view.setAdapter(adapter);
0
Blasanka

Si vous utilisez les repères Android et que vous utilisez ContentProviders pour obtenir les données de Database et que vous les affichez dans ListView à l’aide des CursorLoader et CursorAdapters, toutes les modifications apportées aux données associées seront automatiquement répercutées dans le ListView.

Votre getContext().getContentResolver().notifyChange(uri, null); sur le curseur dans ContentProvider suffira à refléter les modifications. Pas besoin de travail supplémentaire.

Mais lorsque vous n'utilisez pas tous ces éléments, vous devez indiquer à l'adaptateur la modification du jeu de données. De plus, vous devez re-peupler/recharger votre jeu de données (liste par exemple), puis appeler notifyDataSetChanged() sur l'adaptateur.

notifyDataSetChanged()wont fonctionne s'il n'y a pas de changements dans le jeu de données . Voici le commentaire au-dessus de la méthode dans docs-

/**
 * Notifies the attached observers that the underlying data has been changed
 * and any View reflecting the data set should refresh itself.
 */
0
Sanjeet A

J'étais le même quand, dans un fragment, je voulais remplir un ListView (dans un seul TextView) avec l'adresse mac des périphériques BLE analysés au fil du temps.

Ce que j'ai fait était ceci: 

public class Fragment01 extends Android.support.v4.app.Fragment implements ...
{
    private ListView                listView;
    private ArrayAdapter<String>    arrayAdapter_string;

...

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    ...
    this.listView= (ListView) super.getActivity().findViewById(R.id.fragment01_listView);
    ...
    this.arrayAdapter_string= new ArrayAdapter<String>(super.getActivity(), R.layout.dispositivo_ble_item, R.id.fragment01_item_textView_titulo);
    this.listView.setAdapter(this.arrayAdapter_string);
}


@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
    ...
    super.getActivity().runOnUiThread(new RefreshListView(device));
}


private class RefreshListView implements Runnable
{
    private BluetoothDevice bluetoothDevice;

    public RefreshListView(BluetoothDevice bluetoothDevice)
    {
        this.bluetoothDevice= bluetoothDevice;
    }

    @Override
    public void run()
    {
        Fragment01.this.arrayAdapter_string.add(new String(bluetoothDevice.toString()));
        Fragment01.this.arrayAdapter_string.notifyDataSetChanged();
    }
}

Ensuite, ListView a commencé à peupler de manière dynamique l'adresse MAC des périphériques trouvés.

0
jsanmarb

tout en utilisant SimpleCursorAdapter peut appeler changeCursor (newCursor) sur l’adaptateur.

0
Vihaan Verma

NotifyDataSetChanged () ne fonctionnait pas avec la mise à jour de SimpleAdapter. J'ai donc d'abord essayé de supprimer toutes les vues associées à la présentation parent à l'aide de removeAllViews (), puis d'ajouter ListView. UI:

LinearLayout results = (LinearLayout)findViewById(R.id.results);
ListView lv = new ListView(this);
ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row, 
                new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } );

for (...) { 
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("name", name);
    map.put("dept", dept);
    list.add(map);
}

lv.setAdapter(adapter);
results.removeAllViews();     
results.addView(lv);
0
Chasden

une autre option est la méthode onWindowFocusChanged, mais elle est sensible et nécessite un codage supplémentaire pour ceux qui sont intéressés.

 override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)

       // some controls needed
        programList = usersDBHelper.readProgram(model.title!!)
        notesAdapter = DailyAdapter(this, programList)
        notesAdapter.notifyDataSetChanged()
        listview_act_daily.adapter = notesAdapter
    }
0
Sam

Je n'ai pu obtenir notifyDataSetChanged qu'en obtenant de nouvelles données d'adaptateur, puis en réinitialisant l'adaptateur pour la vue liste, puis en effectuant l'appel comme suit: 

    expandableAdapter = baseFragmentParent.setupEXLVAdapter();
    baseFragmentParent.setAdapter(expandableAdapter);
    expandableAdapter.notifyDataSetChanged(); 
0
Kristy Welsh

Je pense que cela dépend de ce que vous entendez par rafraîchissement. Voulez-vous dire que l'affichage de l'interface graphique doit être actualisé ou que les vues enfant doivent être actualisées afin que vous puissiez appeler par programmation getChildAt (int) et obtenir la vue correspondant à ce qui se trouve dans l'adaptateur.

Si vous souhaitez que l'affichage de l'interface graphique soit actualisé, appelez notifyDataSetChanged () sur l'adaptateur. L'interface graphique sera actualisée lors de la prochaine redessin.

Si vous voulez pouvoir appeler getChildAt (int) et obtenir une vue reflétant ce qui se trouve dans l'adaptateur, appelez layoutChildren (). Cela entraînera la reconstruction de la vue enfant à partir des données de l'adaptateur.

0
dazed

Après avoir supprimé des données de la vue liste, vous devez appeler refreshDrawableState(). Voici l'exemple:

final DatabaseHelper db = new DatabaseHelper (ActivityName.this);

db.open();

db.deleteContact(arg3);

mListView.refreshDrawableState();

db.close();

et la méthode deleteContact dans la classe DatabaseHelper ressemblera un peu à 

public boolean deleteContact(long rowId) {

   return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;

}
0
user1031852