Comment actualiser Android listview?

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

397
demandé sur Alexander Farber 2010-02-12 12:31:07

25 réponses

Appel notifyDataSetChanged() sur votre objet Adapter Une fois que vous avez modifié les données dans cet adaptateur.

Quelques détails supplémentaires sur comment / quand appeler notifyDataSetChanged() peuvent être consultés dans Cette Vidéo d'E / S Google .

500
répondu Christopher Orr 2017-01-25 11:52:03

Vous pouvez également utiliser ceci:

myListView.invalidateViews();

Profitez!

192
répondu Bobs 2013-05-15 08:09:18

S'il vous Plaît ignorer toutes les invalidate(), invalidateViews(), requestLayout(), ... les réponses à cette question.

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

Dépannage

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

Si c'est juste une collection vous gardez en mémoire vérifier que vous avez réellement supprimé ou ajouté l'élément (s) à la collection avant d'appeler le notifyDataSetChanged().

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

La chose est que notifyDataSetChanged ne fonctionne que si l'ensemble de données a changé. C'est donc l'endroit à regarder si vous ne trouvez pas de changements à venir. Déboguer si nécessaire.

ArrayAdapter vs BaseAdapter

J'ai trouvé que travailler avec 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'accès à la bonne collection pour les mises à jour. C'est vraiment juste une couche supplémentaire inutile de difficulté dans la plupart des cas.

Thread de L'interface utilisateur

Il est vrai que cela doit être appelé à partir du thread de L'interface utilisateur. D'autres réponses ont des exemples sur la façon dont pour atteindre cet objectif. Cependant, cela n'est nécessaire que si vous travaillez sur ces informations en dehors du thread de L'interface utilisateur. C'est à partir d'un service ou d'un thread d'INTERFACE utilisateur. Dans les cas simples, vous mettrez à jour vos données à partir d'un clic de bouton ou d'une autre activité/fragment. Donc, toujours dans le thread de L'interface utilisateur. Pas besoin de toujours pop 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 un bâtiment de MainAcitivity a ListView avec toutes les données aléatoires. Cette liste peut être actualisée à l'aide du menu action.

L'implémentation de l'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'Informations

Un autre article intéressant sur la puissance des listViews se trouve ici: http://www.vogella.com/articles/AndroidListView/article.html

145
répondu hcpl 2018-01-29 20:04:54

Appelez runnable quand vous voulez:

runOnUiThread(run);

OnCreate(), vous définissez votre exécutable fil de discussion:

run = new Runnable() {
    public void run() {
        //reload content
        arraylist.clear();
        arraylist.addAll(db.readAll());
        adapter.notifyDataSetChanged();
        listview.invalidateViews();
        listview.refreshDrawableState();
    }
};
40
répondu Marckaraujo 2016-05-20 20:53:42

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 / quand appeler notifyDataSetChanged () peuvent être consultés dans cette vidéo d'E/S Google.

NotifyDataSetChanged () n'a pas fonctionné correctement dans mon cas[ j'ai appelé notifyDataSetChanged d'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 merci à Christopher a donné à la finale de l'indice.

Dans ma deuxième classe, j'ai utilisé

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

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

myAdapter.notifyDataSetChanged();

Pour indiquer à L'Adaptateur d'actualiser la vue. A bien fonctionné autant que je peux dire.

11
répondu Wille 2011-12-06 09:45:23

Si vous utilisez SimpleCursorAdapter essayez d'appeler requery() sur le Curseur de l'objet.

9
répondu freehacker 2011-06-25 08:46:41

Si vous n'êtes pas encore satisfait du rafraîchissement ListView, vous pouvez regarder cet extrait, c'est pour charger le listView à partir de DB, en fait ce que vous devez faire est simplement de recharger le ListView, après avoir effectué une opération CRUD Ce n'est pas un meilleur moyen de coder, mais il actualisera le ListView comme vous le souhaitez..

Ça marche pour moi....si vous trouvez une meilleure solution, veuillez partager...

.......
......
do your CRUD Operations..
......
.....
DBAdapter.open();
DBAdapter.insert_into_SingleList();
// Bring that DB_results and add it to list as its contents....
                                            ls2.setAdapter(new ArrayAdapter(DynTABSample.this,
    android.R.layout.simple_list_item_1,                        DBAdapter.DB_ListView));
                                            DBAdapter.close();
5
répondu Nandagopal T 2010-08-25 11:34:15

Les solutions proposées par les personnes dans ce post fonctionne 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" de votre appareil android.

Pour résoudre ces questions pour tous les périphériques, j'ai créé ma propre méthode dans mon adaptateur et l'utilise dans la méthode add and remove hérite de ArrayAdapter qui vous met à jour les 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);
        }
    }

ResultsGp.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
répondu AntuanSoft 2014-08-01 08:32:31

Si vous souhaitez conserver votre position de défilement lors de l'actualisation, et vous pouvez le faire:

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 les informations détaillées, veuillez consulter Android ListView: Maintenir votre position de défilement lorsque vous actualisez.

1
répondu Brandon Yang 2014-08-01 05:16:08

Utilisez simplement myArrayList.remove(position); dans un écouteur:

  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
répondu Steffo Dimfelt 2018-02-13 22:33:12

, Vous devez utiliser un seul objet de cette liste whoose données vous sont gonflage sur ListView. Si la référence est change alors notifyDataSetChanged() ne fonctionne pas .Chaque fois que vous supprimez des éléments de la vue liste, supprimez-les également de la liste que vous utilisez, Qu'il s'agisse D'un ArrayList ou D'autre chose, appelez notifyDataSetChanged() sur l'objet de Votre classe d'adaptateur.

Alors voici comment je l'ai géré dans mon adaptateur voir ci-dessous

public class CountryCodeListAdapter extends BaseAdapter implements OnItemClickListener{

private Context context;
private ArrayList<CountryDataObject> dObj;
private ViewHolder holder;
private Typeface itemFont;
private int selectedPosition=-1;
private ArrayList<CountryDataObject> completeList;

public CountryCodeListAdapter(Context context, ArrayList<CountryDataObject> dObj) {
    this.context = context;
    this.dObj=dObj;
    completeList=new  ArrayList<CountryDataObject>();
    completeList.addAll(dObj);
    itemFont=Typeface.createFromAsset(context.getAssets(), "CaviarDreams.ttf");
}

@Override
public int getCount() {
    return dObj.size();
}

@Override
public Object getItem(int position) {
    return dObj.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
    if(view==null){
        holder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.states_inflator_layout, null);
        holder.textView = ((TextView)view.findViewById(R.id.stateNameInflator));
        holder.checkImg=(ImageView)view.findViewById(R.id.checkBoxState);
        view.setTag(holder);
    }else{
        holder = (ViewHolder) view.getTag();
    }
    holder.textView.setText(dObj.get(position).getCountryName());
    holder.textView.setTypeface(itemFont);

    if(position==selectedPosition)
     {
         holder.checkImg.setImageResource(R.drawable.check);
     }
     else
     {
         holder.checkImg.setImageResource(R.drawable.uncheck);
     }
    return view;
}
private class ViewHolder{
    private TextView textView;
    private ImageView checkImg;
}

public void getFilter(String name) {
    dObj.clear();
    if(!name.equals("")){
    for (CountryDataObject item : completeList) {
        if(item.getCountryName().toLowerCase().startsWith(name.toLowerCase(),0)){
            dObj.add(item);
        }
    }
    }
    else {
        dObj.addAll(completeList);
    }
    selectedPosition=-1;
    notifyDataSetChanged();
    notifyDataSetInvalidated(); 
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id) {
    Registration reg=(Registration)context;
    selectedPosition=position;
    reg.setSelectedCountryCode("+"+dObj.get(position).getCountryCode());
    notifyDataSetChanged();
}
}
1
répondu ADM 2018-07-24 16:16:54

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 deleteContact méthode DatabaseHelper classe sera un peu ressemble à

public boolean deleteContact(long rowId) {

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

}
0
répondu user1031852 2012-04-08 09:10:56

Je n'ai pas pu obtenir notifyDataSetChanged() pour travailler sur la mise à jour de mon SimpleAdapter, donc j'ai d'abord essayé de supprimer toutes les vues attachées à la disposition parente en utilisant removeAllViews (), puis en ajoutant ListView, et cela a fonctionné, me permettant de mettre à jour L'interface utilisateur:

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
répondu Chasden 2013-08-22 16:38:27

Pour moi, après avoir modifié les informations dans la base de données sql, rien ne pourrait actualiser la vue de liste (pour être une vue de liste extensible spécifique), donc si notifyDataSetChanged () n'aide pas, vous pouvez essayer d'effacer votre liste en premier et l'ajouter à nouveau 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
répondu Aivus 2013-10-30 20:54:52

Tout en utilisant SimpleCursorAdapter peut appeler changeCursor (newCursor) sur l'adaptateur.

0
répondu Vihaan Verma 2014-02-06 18:17:40

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 pendant un certain 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();
    }
}

, Puis la liste a commencé à remplir dynamiquement avec l'adresse mac des périphériques.

0
répondu jsanmarb 2014-09-15 23:52:07

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 voulez-vous dire que les vues enfants doivent être actualisées de sorte que vous pouvez appeler par programmation getChildAt(int) et obtenir la vue correspondant à ce qui est dans l'Adaptateur.

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

Si vous voulez pouvoir appeler getChildAt (int) et obtenir une vue cela reflète ce qui est dans l'adaptateur, puis appelle layoutChildren (). Cela entraînera la reconstruction de la vue enfant à partir des données de l'adaptateur.

0
répondu dazed 2015-01-19 15:29:22

J'avais un ArrayList que je voulais afficher dans un listview. ArrayList contenait des éléments de mysql. J'ai remplacé la méthode onRefresh et dans cette méthode j'ai utilisé tablelayout.removeAllViews (); puis a répété 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 n'importe quelle structure de données ou bien de nouvelles données seront ajoutées à l'ancienne..

0
répondu Kshitij G 2015-03-03 17:04:00

Si vous souhaitez mettre à jour L'interface utilisateur listview à partir d'un service, rendez l'adaptateur statique 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
répondu Mayank Saini 2015-04-25 20:02:42

Si vous utilisez des lignes de guidage android et que vous utilisez le ContentProviders pour obtenir des données à partir de Database et que vous les affichez dans le ListView en utilisant le CursorLoader et CursorAdapters, toutes les modifications apportées aux données associées seront automatiquement reflétées dans ListView.

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

Mais lorsque vous n'utilisez pas tout cela, vous devez indiquer à l'adaptateur quand l'ensemble de données change. Également vous devez re-remplir / recharger votre jeu de données (disons list) et ensuite vous devez appeler notifyDataSetChanged() sur l'adaptateur.

notifyDataSetChanged()ne fonctionnera pas s'il n'y a pas les changements dans le datset. Voici le commentaire ci-dessus 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
répondu Sanjeet A 2015-10-31 16:32:55

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

    expandableAdapter = baseFragmentParent.setupEXLVAdapter();
    baseFragmentParent.setAdapter(expandableAdapter);
    expandableAdapter.notifyDataSetChanged(); 
0
répondu Kristy Welsh 2016-03-11 21:11:09

Sur une autre option est la méthode onWindowFocusChanged, mais bien sûr c'est sensible et a besoin d'un codage supplémentaire pour qui est intéressé

 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
répondu Sam 2018-07-21 18:54:19

Considérez que vous avez passé une liste à votre adaptateur.
Utilisation:

list.getAdapter().notifyDataSetChanged()

Pour mettre à jour votre liste.

0
répondu Malv 2018-07-29 16:56:46

Si j'ai parlé de mon scénario ici, non des réponses ci-dessus ne fonctionnera pas parce que j'ai eu une activité qui montre la liste des valeurs de base de données avec un bouton de suppression et quand un bouton de suppression est pressé, je voulais supprimer cet élément de la liste.

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

Donc, la solution était de supprimer l'objet de la carte dans l'adaptateur classe getView méthode(à seulement supprimer cet objet spécifique, mais si vous souhaitez supprimer tous les, appelez - clear()).

Pour 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
         }
    });
 //....

Note: ici les méthodes remove() et getItem() sont héritées de la classe Adaptateur.

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

Voici comment j'ai défini l'adaptateur sur listview dans la classe activity,

    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
répondu Blasanka 2018-10-08 17:47:02

Le plus simple est de simplement faire un nouvel Adaper et de laisser tomber l'ancien:

myListView.setAdapter(new MyListAdapter(...));
-1
répondu user1712200 2017-11-28 15:45:52