29

I have a database in a server and from a Tablet I take some values from one table in the database. I load this information correctly into a list but I would like to know why when there is a change, nothing happens even if I use notifyDataSetChanged();. I must say that for loading the loading data y use the AsyncTaskClass So, my problem is that I don't know if use the notifyDataSetChanged(); method correctly ,because if there's is a change I would like to refresh the image. Here is some part of the code of the class:

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.all_candidatos);


        candidatosList = new ArrayList<HashMap<String, String>>();

        new CargarCandidatos().execute();
    }


//  public void timer(){
//       new CountDownTimer(tiempo, 100) {
//
//              public void onTick(long millisUntilFinished) {
//                  
//              }
//
//              public void onFinish() {
//              //  new CargarCandidatos().execute();
//
//              }
//           }.start();}



    /**
     * Background Async Task to Load all product by making HTTP Request
     * */
    class CargarCandidatos extends AsyncTask<String, String, String> {

        /**
         * Before starting background thread Show Progress Dialog
         * */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            pDialog = new ProgressDialog(Monitorizacion.this);
            pDialog.setMessage("Loading ...");
            pDialog.setIndeterminate(false);
            pDialog.setCancelable(false);
            pDialog.show();
        }

        /**
         * getting All products from url
         * */
        protected String doInBackground(String... args) {
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            JSONObject json = jParser.makeHttpRequest(url_candidatos, "GET", params);

            Log.d("Candidatos: ", json.toString());

            try {
                int success = json.getInt(TAG_SUCCESS);

                if (success == 1) {

                    candidatos = json.getJSONArray(TAG_CANDIDATOS);

                    for (int i = 0; i < candidatos.length(); i++) {
                        JSONObject c = candidatos.getJSONObject(i);

                        // Storing each json item in variable
                        String nserie = c.getString(TAG_NSERIE);
                        String dni = c.getString(TAG_DNI);
                        String nombre = c.getString(TAG_NOMBRE);
                        String test = c.getString(TAG_TEST);
                        String pregunta = c.getString(TAG_PREGUNTA);
                        String bateria = c.getString(TAG_BATERIA);

                        // creating new HashMap
                        HashMap<String, String> map = new HashMap<String, String>();

                        // adding each child node to HashMap key => value
                        map.put(TAG_NSERIE, nserie);
                        map.put(TAG_DNI, dni);
                        map.put(TAG_NOMBRE, nombre);
                        map.put(TAG_TEST, test);
                        map.put(TAG_PREGUNTA, pregunta);
                        map.put(TAG_BATERIA, bateria);

                        // adding HashList to ArrayList
                        candidatosList.add(map);
                    }
                } 
            } catch (JSONException e) {
                e.printStackTrace();
            }

            return null;
        }

        /**
         * After completing background task Dismiss the progress dialog
         * **/
        protected void onPostExecute(String file_url) {
            pDialog.dismiss();
            runOnUiThread(new Runnable() {
                public void run() {
                    /**
                     * Updating parsed JSON data into ListView
                     * */
                    adapter = new SimpleAdapter(
                            Monitorizacion.this, candidatosList,
                            R.layout.list_item, new String[] { TAG_NSERIE,
                                    TAG_DNI, TAG_NOMBRE, TAG_TEST, TAG_PREGUNTA, TAG_BATERIA},
                            new int[] { R.id.id, R.id.dni, R.id.nombre, R.id.test, R.id.pregunta, R.id.bateria});
                    setListAdapter(adapter);
                    adapter.notifyDataSetChanged();
                //  timer();
                }
            });

        }

    }
}
Pratik Butani
  • 60,504
  • 58
  • 273
  • 437
Katherine99
  • 980
  • 4
  • 21
  • 40
  • any crashes ? you don want that runOnUIThread simply set the adapter. Make sure that candidatosList is having some values. – Triode Apr 23 '13 at 09:35
  • Hi Triode, I load all the database values in the screen and the activity is not crashing. the problem is that even the database gets updated, the UI doesn not refresh with the new values. – Katherine99 Apr 23 '13 at 09:38
  • @Katherine99 if my answer solved ur problem, u can mark answer as selected. :) – Sazzad Hissain Khan Dec 25 '15 at 19:45

8 Answers8

119

One of the main reasons notifyDataSetChanged() won't work for you - is,

Your adapter loses reference to your list.

When you first initialize the Adapter it takes a reference of your arrayList and passes it to its superclass. But if you reinitialize your existing arrayList it loses the reference, and hence, the communication channel with Adapter.

When creating and adding a new list to the Adapter. Always follow these guidelines:

  1. Initialise the arrayList while declaring it globally.
  2. Add the List to the adapter directly without checking for null and empty values. Set the adapter to the list directly (don't check for any condition). Adapter guarantees you that wherever you make changes to the data of the arrayList it will take care of it, but never lose the reference.
  3. Always modify the data in the arrayList itself (if your data is completely new then you can call adapter.clear() and arrayList.clear() before actually adding data to the list) but don't set the adapter i.e If the new data is populated in the arrayList than just adapter.notifyDataSetChanged()

Stay true to the Documentation.

Emil
  • 140
  • 1
  • 8
Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256
  • 4
    thank you so much, my problem was not having trust in the power of the adapter. – J.Vassallo Feb 15 '15 at 14:36
  • 2
    Thank you very much, with your explanation I've understood many things about adapter and list view also found solutions for many of my doubts. – Sai Jul 18 '15 at 10:52
  • How do I achieve the same if the dataset is a cursor? I cannot clear the cursor and add all items again. – Rohan Taneja Jul 15 '17 at 10:49
  • you can use CursorAdapter. Please see https://github.com/codepath/android_guides/wiki/Populating-a-ListView-with-a-CursorAdapter. – Sazzad Hissain Khan Jul 16 '17 at 01:55
  • everyone talks about arraylist! what if your data is Cursor type? a new query (different query) has been made to the db and now I set the adapter with the new cursor and notify change wont work! and adapter in the second call has a null corsor! – Araz Jul 10 '22 at 08:21
5

The thing you need to edit is put your

runOnUiThread(new Runnable() {
                public void run() {
                    /**
                     * Updating parsed JSON data into ListView
                     * */
                    adapter = new SimpleAdapter(
                            Monitorizacion.this, candidatosList,
                            R.layout.list_item, new String[] { TAG_NSERIE,
                                    TAG_DNI, TAG_NOMBRE, TAG_TEST, TAG_PREGUNTA, TAG_BATERIA},
                            new int[] { R.id.id, R.id.dni, R.id.nombre, R.id.test, R.id.pregunta, R.id.bateria});
                    setListAdapter(adapter);
                    adapter.notifyDataSetChanged();
                //  timer();
                }
            });

into the OnCreate(). and return the list candidatosList from Asynctask. than set timer for updating candidatosList list.

user1621629
  • 765
  • 7
  • 19
  • Sorry, it continues whithout refreshing – Katherine99 Apr 23 '13 at 10:00
  • Hi if I use candidatosList.clear(); the list view does not conatin any data when i run the activity – Katherine99 Apr 23 '13 at 10:17
  • please make sure that your data in `candidatosList` will be updated everytime. If it was same as before than you can delete single item and set `adapter.notifyDataSetChanged();` and then `setListAdapter(adapter);` – user1621629 May 01 '13 at 13:16
  • Thanks works for me after adding runOnUiThread(new Runnable()) ; , but want to know how it works ? – h_patel Feb 22 '17 at 08:23
5

It might be worth checking if you have an empty override for registerDataSetObserver(). Android Studio added one for me without implementing the call to super. Adding it in as follows was enough to get my listView working again:

@Override
    public void registerDataSetObserver(DataSetObserver observer) {
        super.registerDataSetObserver(observer);    
    }
head in the codes
  • 1,159
  • 12
  • 24
3

An adapter define the comportement of the layout ! -> setListAdapter() : Define the adapter for a ListView/GridView/Gallery... but you need to specify the data !

I recommend to you, to initialize 'setListAdapter' in the 'onCreate' or in the constructor. After you set the data into the adapter (exemple : adapter.setItem(yourData))

And NOW ! You should to call notifyDataSetChanged ! Because you have changed the data but the view isn't refresh and notifydatasetchanged() reload the content of the view (ListView/GridView/Gallery...)

For a good practice and understand correctly I recommend to you to use a 'custom adapter' using 'baseAdapter'

Read and do this tutorial (I haver learn with this): http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/

Read the documentation : http://developer.android.com/reference/android/widget/BaseAdapter.html

VincentLamoute
  • 800
  • 1
  • 9
  • 16
2

The update function should be called from UI thread. My answer is actually similar to @user1621629's answer with that difference that I am using rxJava, so here's working code that solve this problem for me:

this.subscriber = myAdapter.getSubscriber(); // keep for unsubscribe in destroy
dataSource.subscribeOn(Schedulers.computation()).observeOn(AndroidSchedulers.mainThread()).subscribe(this.subscriber);

So I set all execution in order to get data for the list to computation thread, but showing result in UI thread.

Here's how I create subscriber for this:

public class MyListAdapter extends RecyclerView.Adapter<LocationListAdapter.ViewHolder> {


private List<ListItem> mDataset = new ArrayList<>();

public Subscriber<ListItem[]> getSubscriber() {
    return Subscribers.create(new Action1<ListItem[]>() {
        @Override
        public void call(ListItem[] listItems) {
            mDataset.clear();
            mDataset.addAll(Arrays.asList(listItems));
            notifyDataSetChanged();
        }
    });
}
......
Community
  • 1
  • 1
msangel
  • 9,895
  • 3
  • 50
  • 69
1

As Hissain describes above,

you need to maintain a reference to the list

Here's how I got it to work:

  1. Let the list being sent to the adapter be set as an instance member in the activity

  2. In the logic that performs a change to the data, make sure it updates the same list instance that the activity passed to the adapter

  3. Then calling .notifyDataSetChanged(); worked

Remember that listView position starts at 1, so you will have to do (listViewPosition - 1) for your your java.util.List

Community
  • 1
  • 1
Gene Bo
  • 11,284
  • 8
  • 90
  • 137
1

I dont have much reputation to comment on Mr. Hissain answer.It is correct but I want to mention one more thing that reference to the list should not change. If data source underlying is changing, dont change the reference to new list. Actions only need to be done on the same list object. To do the same,clear the list using clear() and then add data to the same list using add() or addALL() and then call notifyDataSetChanged(). eg. On first initialization of the list

list = dataSource.getList();

then one can add and remove the content from the list and call notifyDataSetChanged() it works fine but if in the code, one tries to change the reference to the other object. Like

list = dataSource.getList();

where getList() returns the new list everytime, hence the reference changes to some other list object and calling notifyDataSetChnaged does not have impact on the list.But if getList() returns the same list object, it works fine.

Raghav Sharma
  • 780
  • 10
  • 18
0

If everything you set fine and still not working then your list... Is it Mutablekind of the List or not...!

private val demoList: MutableList<AnyClass> = mutableListOf()

once you define your list like above mutable manner then you can get the method

.add
.addAll
.remove

etc...

else if you have created normal list then that will not work as notifyDataSetChanged