Question

Comment rafraîchir un Android ListView après l'ajout / suppression de données dynamiques?

Était-ce utile?

La solution

notifyDataSetChanged() sur votre objet Adapter une fois que vous « ai modifié les données de cette carte.

Quelques précisions supplémentaires sur la façon / quand appeler notifyDataSetChanged() peuvent être consultés dans cette vidéo Google I / O

Autres conseils

Vous pouvez également utiliser ceci:

myListView.invalidateViews();

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

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

Dépannage

Si vous appelez notifyDataSetChanged() ne fonctionne pas toutes les méthodes de mise en page ne sera pas utile non plus. Croyez-moi, le ListView a été correctement mis à jour. Si vous ne parvenez pas à trouver la différence, vous devez vérifier où les données de votre adaptateur vient.

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

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

La chose est ce notifyDataSetChanged ne fonctionne que si l'ensemble de données a changé. Alors, qui est le lieu de regarder si vous ne trouvez pas les changements à venir à travers. Débogage si nécessaire.

ArrayAdapter vs BaseAdapter

Je ne trouve que travailler avec un adaptateur qui vous permet de gérer la collection, comme un BaseAdapter fonctionne mieux. Certains adaptateurs comme le ArrayAdapter gèrent déjà leur propre collection qui rend plus difficile à obtenir à la collecte appropriée pour les mises à jour. Il est vraiment juste une couche supplémentaire inutile de difficulté dans la plupart des cas.

Thread UI

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. Cependant, cela est uniquement nécessaire si vous travaillez sur ces informations à l'extérieur du thread d'interface utilisateur. C'est d'un service ou d'un fil non d'interface utilisateur. Dans les cas simples, vous mettrez à jour vos données à partir d'un bouton clic ou une autre activité / fragment. Ainsi, toujours dans le thread d'interface utilisateur. Pas besoin de pop toujours dans runOnUiTrhead.

Exemple rapide du projet

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

La mise en œuvre de l'adaptateur que j'ai créé pour cet exemple ModelObject expose la collection 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 du 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

Une autre belle post sur la puissance de listviews se trouve ici: http: // www. vogella.com/articles/AndroidListView/article.html

Appel runnable chaque fois que vous voulez:

runOnUiThread(run);

OnCreate(), vous définissez votre fil runnable:

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

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

  

Appel notifyDataSetChanged () sur votre adaptateur.

     

Quelques précisions supplémentaires sur la façon / quand appeler notifyDataSetChanged () peut être vu dans cette vidéo Google I / O.

notifyDataSetChanged () ne fonctionnait pas correctement dans mon cas [j'ai appelé le notifyDataSetChanged d'une autre classe]. Juste dans le cas i édité ListView dans l'activité de fonctionnement (fil). Cette vidéo grâce à Christopher a donné la touche finale.

Dans ma deuxième classe i utilisé

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

pour la mise à jour acces () de mon activité. Cette mise à jour

myAdapter.notifyDataSetChanged();

pour dire l'adaptateur pour rafraîchir la vue. A travaillé bien pour autant que je peux dire.

Si vous utilisez SimpleCursorAdapter essayer d'appeler requery () sur l'objet de curseur.

si vous n'êtes pas encore satisfait de rafraîchissements ListView, vous pouvez regarder cet extrait, ceci est pour le chargement du listView de DB, fait ce que vous avez à faire est de recharger simplement le ListView, après avoir effectué une opération CRUD Ce ne est pas une meilleure façon de code, mais il rafraîchira ListView que vous le souhaitez ..

Il fonctionne pour moi .... si u trouver une meilleure solution, s'il vous plaît Share ...

.......
......
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();

Les solutions proposées par les personnes dans le fonctionnement de ce poste ou non dépendant principalement 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 ce questions pour tous les appareils que j'ai créé ma propre méthode sur dans mon adaptateur et utiliser à l'intérieur de la méthode ajout et de suppression hérite de ArrayAdapter qui mettent à jour vos données sans problème.

Mon code: Utiliser ma propre RaceResult de classe de données, 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!!!

Si vous voulez maintenir votre position de défilement lorsque vous actualisez, et vous pouvez faire ceci:

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, s'il vous plaît voir ListView Android:. Maintenir votre position de défilement lorsque vous actualisez

Il suffit d'utiliser myArrayList.remove(position); l'intérieur d'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();
        }
    });

Vous devez utiliser un seul objet de cette liste des données whoose vous gonflez sur ListView. Si la référence est le changement alors notifyDataSetChanged() travail does't .Whenever Vous supprimez des éléments de la vue de la liste également les supprimer de la liste que vous utilisez si elle est un ArrayList <> ou autre chose, puis appelez notifyDataSetChanged() sur l'objet de votre classe d'adaptateur.

Voici donc voir comment je l'ai réussi 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();
}
}

Après la suppression des données de la vue de la 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 en classe DatabaseHelper sera ressemble quelque peu

public boolean deleteContact(long rowId) {

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

}

Je n'ai pas pu obtenir notifyDataSetChanged () pour travailler sur la mise à jour de mon SimpleAdapter, donc au lieu que j'ai essayé d'abord enlever toutes les vues qui ont été attachés à la mise en page de parent à l'aide removeAllViews (), puis en ajoutant le ListView, et qui a travaillé, me permettant 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);

Pour moi, après avoir changé l'information dans rien de base de données SQL pourrait actualiser la liste (pour être de liste extensible spécifique) donc si notifyDataSetChanged () ne fonctionne pas, vous pouvez essayer d'effacer votre première liste 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();

Je espère que ça fait sens pour vous.

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

J'étais quand même, dans un fragment, je voulais remplir un ListView (en un seul TextView) avec l'adresse MAC des appareils BLE scannés pendant un certain temps.

Ce que je l'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();
    }
}

Alors le ListView a commencé à remplir de manière dynamique avec l'adresse MAC des appareils trouvés.

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 rafraîchi, ou voulez-vous dire que les vues de l'enfant doivent être mises à jour de sorte que vous pouvez programatically appeler getChildAt (int) et obtenir le point de vue correspondant à ce qui est dans l'adaptateur.

Si vous souhaitez que l'affichage de l'interface rafraîchie, puis appelez notifyDataSetChanged () sur l'adaptateur. L'interface graphique sera mise à jour lorsque la prochaine redessinée.

Si vous voulez être en mesure d'appeler getChildAt (int) et une vue qui reflète ce qui est ce qui est dans l'adaptateur, puis appeler à layoutChildren (). Cela entraînera la vue des enfants à reconstruire à partir des données de l'adaptateur.

J'ai eu un ArrayList que je voulais afficher dans un listview. ArrayList contient des éléments de mysql. I overrided OnRefresh procédé et en ce que la méthode je tablelayout.removeAllViews (); et ensuite répété le processus pour obtenir des données de nouveau à partir de la base de données. Mais avant cela assurez-vous d'effacer votre ArrayList ou tout autre structre de données ou encore de nouvelles données se joint à l'ancien ..

Si vous souhaitez mettre à jour l'interface utilisateur ListView d'un service, puis faire l'adaptateur statique dans votre activité principale et faire ceci:

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

        MainActivity.listView.setAdapter(MainActivity.adapter);
    }
}    

Si vous allez par des lignes de guidage Android et que vous utilisez le ContentProviders pour obtenir des données de Database et que vous affichez dans le ListView en utilisant la CursorLoader et CursorAdapters, alors vous toutes les modifications apportées aux données connexes seront automatiquement répercutées dans la ListView.

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

Mais quand vous ne l'utilisez ces tout, alors vous devez dire à l'adaptateur lorsque l'ensemble de données est en train de changer. vous devez également repeupler / recharger votre ensemble de données (liste dire) et vous devez appeler notifyDataSetChanged() sur l'adaptateur.

travail notifyDataSetChanged()wont s'il n'y a pas les changements dans la DATSET. Voici le commentaire ci-dessus dans la méthode docs -

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

Je ne ai pu obtenir que notifyDataSetChanged en obtenant de nouvelles données de l'adaptateur, la réinitialisation, puis l'adaptateur pour la vue de la liste, puis faire l'appel comme ceci:

    expandableAdapter = baseFragmentParent.setupEXLVAdapter();
    baseFragmentParent.setAdapter(expandableAdapter);
    expandableAdapter.notifyDataSetChanged(); 

autre option est la méthode de onWindowFocusChanged, mais que son sensible et a besoin d'un certain 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
    }

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

list.getAdapter().notifyDataSetChanged()

pour mettre à jour votre liste.

Si je parlais de mon scénario ici, non des réponses ci-dessus ne sera pas travaillé parce que j'avais une activité qui affiche la liste des valeurs db avec un bouton de suppression et quand un bouton de suppression est pressé, je voulais supprimer cet élément de la liste .

La chose fraîche était, je ne l'ai pas utilisé vue recycleur mais un simple affichage de la liste et ce point de vue de la liste initialisé dans la classe d'adaptateur. Ainsi, l'appel de la notifyDataSetChanged() ne fera rien à l'intérieur de la classe d'adaptation et même dans la classe d'activité où l'objet de l'adaptateur est initialisé parce que la méthode de suppression était dans la classe de l'adaptateur.

Alors, la solution était de supprimer l'objet de l'adaptateur dans la méthode de la classe adaptateur getView (pour supprimer uniquement cet objet spécifique, mais si vous voulez supprimer tous, appelez clear()).

Pour vous d'obtenir une idée, ce qui était mon regard de code comme,

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 méthodes de remove() et getItem() sont héritent de la classe de l'adaptateur

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

Voilà comment je mis l'adaptateur à la 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);

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

myListView.setAdapter(new MyListAdapter(...));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top