0

I have an activity that extends ListActivity, a list of "concepts" (let's call this list "C") and an onItemClickListener defined for this list. Whenever I click a "concept", no matter which one, the app must display another list. I have the following code to change the displayed list:

if(position == 0) change_list("adapter1");
else if (position == 1) change_list("adapter2");
else if (position == 2) change_list("adapter3");
else if (position == 3) change_list("adapter4");
else if (position == 4) change_list("adapter5");

Where position is the position of the clicked element in C

The function change_list performs setListAdapter(parameter) depending on the parameter I pass. If I click the first element of C (the first concept), a list related to the first concept must appear. However, after calling setListAdapter(adapter), the data related to this concept is displayed, and also part of the C's list data.

For example: let's suppose C has these concepts:

  • A B C D E

and I click "A", which would lead to display a list with the following data: {a1,a2}

That's the final result:

  • a1 a2 C D E

And then, when I interact with another element on screen or I scroll down the list, the "ghost" data disappears and only the correct data remains on screen, just like this:

  • a1 a2

To make things worse, when I want to display list C again, nothing strange happens. Everything is displayed correctly.

At any time incorrect data is stored where it doesn't have to. One function my app must allow is to generate a txt file , and the generated txt file contains exactly the data I introduced. No data is corrupted or duplicated. I also tried using notifyDataSetChanged() and other functions, but I didn't solve the problem.

EDIT :

Here goes the xml code used for the main list of the activity:

    <ListView
    android:id="@android:id/list"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#FF0000"
    android:layout_below="@+id/afegir"/>

And an example of code in which I determine which contents must be displayed on screen:

 else if(comprovar_concepte_actiu() == 1){

            pnt = mydbhandler.getStoredValues("despeses1");
            pnt.moveToFirst();

            if(pnt.moveToFirst()){
                do{     

                    adapter_mostrar.add(pnt.getString(pnt.getColumnIndex("nom"))); 
                }while(pnt.moveToNext());                          
            } 

            adapter_mostrar.notifyDataSetChanged();
  }

Where comprovar_concepte_actiu() returns and integer that tells which concept has been clicked in the main list C and adapter_mostrar is the single adapter I'm using now, instead of using multiple adapters (which made me use setListAdapter)

At the beginning of the activity, I call this.setListAdapter(adapter_mostrar). That's all I have.

EDIT 2 :

https://www.dropbox.com/s/7twgy043lkxb2x5/conceptes.java?dl=0

Here is a link to my conceptes.java activity. Press CTRL+F once opened and search "this is where I call.. " and you will directly get to the function where the change of list displayed on screen starts

I haven't found a solution yet. Any idea will be totally appreciated

cmn8
  • 1
  • 1
  • Posting the code for "adapter_mostrar.add()" may help. I guess I was too abstract with what I said before: you have to **clear** the data from the adapter, then add the data into it. Posting the code for your entire adapter may help, as if it still doesn't work there's probably an issue with how you are storing - and using - the data as well. – DragonJawad Dec 08 '14 at 19:03
  • As you can see, the data comes from a table in a database. I get the data with a cursor and then add the data stored in a column named "nom" into the adapter_mostrar. The data return from the database with getStoredValues() is done properly from another class called database_handle. –  Dec 08 '14 at 19:19
  • I meant, the data contained within the adapter. The key to getting notifyDataSetChanged() is the data stored and used by the adapter. – DragonJawad Dec 08 '14 at 19:31
  • @christianmn: Everything makes more sense now. Thank you for posting the activity's code. The problem here is related to that you're using an ArrayAdapter rather than a custom adapter that extends BaseAdapter. So, you either can create your own adapter and manage the data (which will save headaches later down the road) or read sources like this [http://stackoverflow.com/questions/18523107/clear-arrayadapter-not-working-properly-android] to learn how to clear the ArrayAdapter properly. If you need help with the BaseAdapter, feel free to message me. – DragonJawad Dec 08 '14 at 20:49

1 Answers1

0

The problem here is that - when you set a new adapter - the old data is still drawn. In other words, there has been no command to "refresh" the listView. However, the new adapter will be commanded to draw its own views. What ultimately occurs is that the old items are still there, the new items are redrawn, but when scrolled away the new adapter won't redraw/recreate the old items.

The solution is to simply refresh the adapter. However, there are two ways to go about this:

  • Add a new adapter every time and use myListView.invalidateViews(); or something similar [This is probably the easiest solution to implement, although probably not the best in the long run]
  • Change the dataset of the adapter and use notifyDataSetChanged() [on the adapter]

The latter option is a far better idea. You should use a single adapter and simply change its data over time. Once its dataset is changed, then tell the adapter that such a thing happened so it refreshes. However, you should read more here on all the different thoughts and processes about it, rather than take my opinion on it.

Edit: There's apparently some very nicely, thought out answers around. Here's another one, that tells you more specifically about the differences between these two: Is there any difference between ListView.invalidateViews() and Adapter.notifyDataSetChanged()?

Edit2: With the onClickListener in mind, invalidateViews() will most likely not work, as it'll probably still draw the old views to "finish" the click (ie, draw the highlighting).

Changing the data directly inside a single adapter and using Adapter.notifyDataSetChanged() is your best bet, as it'll know to redraw everything from a single adapter and use only the current data defined by this single adapter.

Best to leave the data specifics (and defining what to draw based off of that data) up to what actually knows the data, rather than a higher up container that knows nothing specific about the actual data.

Community
  • 1
  • 1
DragonJawad
  • 1,846
  • 3
  • 20
  • 28
  • I tried this.getListView().invalidateViews() from the function change_list but it hasn't been successful. I might try to use only one adapter or something like this. I call change_list from the onItemClick function. Is that wrong? – cmn8 Dec 08 '14 at 17:59
  • Hmm, depends on how your code is laid out. Inside the change_list function, you may need to do [insertActivityClassName].this.getListView().invalidateViews()- if it can't access the function. Is it that the list doesn't get "refreshed" or that you couldn't run the function? Also, make sure to invalidate the views only after setting the adapter. – DragonJawad Dec 08 '14 at 18:05
  • I can run change_list (and now I added classname.this.getListView.invalidateViews, but it still doesn't work). The info of the new displayed list gets overlapped with part of the data of the previously displayed list. The new list is displayed correctly only if I click a button or do something else. – cmn8 Dec 08 '14 at 18:11
  • Oh wait, the onClickListener is a part of the list itself, isn't it? So invalidateViews() is probably not a feasible answer as the button you clicked probably still plays a part. To put it into a more concrete albeit probably less factual answer: When you click on the list, it's still "using" the old view(s) and thus still has to redraw it/them along with the new views. I'll edit my answer, but if you want to keep your current listener behaviour, you will most likely have to use a single adapter. – DragonJawad Dec 08 '14 at 18:15
  • I have an onItemClickListener for an xml element with id "@+id/list" in the activity. I use that listener for every list I have (one main list with five elements and five lists that are displayed on screen when I click an element from the main list) Now I clear the single adapter and fill it with the new information, but the previous information still appears on screen. That's why I used setListAdapter, because I had one adapter per sublist, and I just wanted to swap between lists/adapters. – cmn8 Dec 08 '14 at 18:44
  • Did you call notifyDataSetChanged() after setting the data? Also, posting your xml, the code you just tried for setting the adapter, how you set the list at first, and such would help clarify. – DragonJawad Dec 08 '14 at 18:48
  • I have just edited the post, if you need more information please let me know – cmn8 Dec 08 '14 at 18:58