Android ListView not refreshing after notifyDataSetChanged - android

My ListFragment code
public class ItemFragment extends ListFragment {
private DatabaseHandler dbHelper;
private static final String TITLE = "Items";
private static final String LOG_TAG = "debugger";
private ItemAdapter adapter;
private List<Item> items;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.item_fragment_list, container, false);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.setHasOptionsMenu(true);
super.onCreate(savedInstanceState);
getActivity().setTitle(TITLE);
dbHelper = new DatabaseHandler(getActivity());
items = dbHelper.getItems();
adapter = new ItemAdapter(getActivity().getApplicationContext(), items);
this.setListAdapter(adapter);
}
#Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter.notifyDataSetChanged();
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
if(dbHelper != null) { //item is edited
Item item = (Item) this.getListAdapter().getItem(position);
Intent intent = new Intent(getActivity(), AddItemActivity.class);
intent.putExtra(IntentConstants.ITEM, item);
startActivity(intent);
}
}
}
My ListView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
But this does not refresh the ListView. Even after restarting app the updated items are not shown. My ItemAdapter extends BaseAdapter
public class ItemAdapter extends BaseAdapter{
private LayoutInflater inflater;
private List<Item> items;
private Context context;
public ProjectListItemAdapter(Context context, List<Item> items) {
super();
inflater = LayoutInflater.from(context);
this.context = context;
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ItemViewHolder holder = null;
if(convertView == null) {
holder = new ItemViewHolder();
convertView = inflater.inflate(R.layout.list_item, parent,false);
holder.itemName = (TextView) convertView.findViewById(R.id.topText);
holder.itemLocation = (TextView) convertView.findViewById(R.id.bottomText);
convertView.setTag(holder);
} else {
holder = (ItemViewHolder) convertView.getTag();
}
holder.itemName.setText("Name: " + items.get(position).getName());
holder.itemLocation.setText("Location: " + items.get(position).getLocation());
if(position % 2 == 0) {
convertView.setBackgroundColor(context.getResources().getColor(R.color.evenRowColor));
} else {
convertView.setBackgroundColor(context.getResources().getColor(R.color.oddRowColor));
}
return convertView;
}
private static class ItemViewHolder {
TextView itemName;
TextView itemLocation;
}
}
Can someone help please?

Look at your onResume method in ItemFragment:
#Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); // reload the items from database
adapter.notifyDataSetChanged();
}
what you just have updated before calling notifyDataSetChanged() is not the adapter's field private List<Item> items; but the identically declared field of the fragment. The adapter still stores a reference to list of items you passed when you created the adapter (e.g. in fragment's onCreate).
The shortest (in sense of number of changes) but not elegant way to make your code behave as you expect is simply to replace the line:
items = dbHelper.getItems(); // reload the items from database
with
items.addAll(dbHelper.getItems()); // reload the items from database
A more elegant solution:
1) remove items private List<Item> items; from ItemFragment - we need to keep reference to them only in adapter
2) change onCreate to :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setHasOptionsMenu(true);
getActivity().setTitle(TITLE);
dbHelper = new DatabaseHandler(getActivity());
adapter = new ItemAdapter(getActivity(), dbHelper.getItems());
setListAdapter(adapter);
}
3) add method in ItemAdapter:
public void swapItems(List<Item> items) {
this.items = items;
notifyDataSetChanged();
}
4) change your onResume to:
#Override
public void onResume() {
super.onResume();
adapter.swapItems(dbHelper.getItems());
}

You are assigning reloaded items to global variable items in onResume(), but this will not reflect in ItemAdapter class, because it has its own instance variable called 'items'.
For refreshing ListView, add a refresh() in ItemAdapter class which accepts list data i.e items
class ItemAdapter
{
.....
public void refresh(List<Item> items)
{
this.items = items;
notifyDataSetChanged();
}
}
update onResume() with following code
#Override
public void onResume()
{
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
**adapter.refresh(items);**
}

In onResume() change this line
items = dbHelper.getItems(); //reload the items from database
to
items.addAll(dbHelper.getItems()); //reload the items from database
The problem is that you're never telling your adapter about the new items list. If you don't want to pass a new list to your adapter (as it seems you don't), then just use items.addAll after your clear(). This will ensure you are modifying the same list that the adapter has a reference to.

If the adapter is already set, setting it again will not refresh the listview. Instead first check if the listview has a adapter and then call the appropriate method.
I think its not a very good idea to create a new instance of the adapter while setting the list view. Instead, create an object.
BuildingAdapter adapter = new BuildingAdapter(context);
if(getListView().getAdapter() == null){ //Adapter not set yet.
setListAdapter(adapter);
}
else{ //Already has an adapter
adapter.notifyDataSetChanged();
}
Also you might try to run the refresh list on UI Thread:
activity.runOnUiThread(new Runnable() {
public void run() {
//do your modifications here
// for example
adapter.add(new Object());
adapter.notifyDataSetChanged()
}
});

If you want to update your listview doesn't matter if you want to do that on onResume(), onCreate() or in some other function, first thing that you have to realize is that you won't need to create a new instance of the adapter, just populate the arrays with your data again.
The idea is something similar to this :
private ArrayList<String> titles;
private MyListAdapter adapter;
private ListView myListView;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
myListView = (ListView) findViewById(R.id.my_list);
titles = new ArrayList<String>()
for(int i =0; i<20;i++){
titles.add("Title "+i);
}
adapter = new MyListAdapter(this, titles);
myListView.setAdapter(adapter);
}
#Override
public void onResume(){
super.onResume();
// first clear the items and populate the new items
titles.clear();
for(int i =0; i<20;i++){
titles.add("New Title "+i);
}
adapter.notifySetDataChanged();
}
So depending on that answer you should use the same List<Item> in your Fragment. In your first adapter initialization you fill your list with the items and set adapter to your listview. After that in every change in your items you have to clear the values from the main List<Item> items and than populate it again with your new items and call notifySetDataChanged();.
That's how it works : ).

An answer from AlexGo did the trick for me:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
messages.add(m);
adapter.notifyDataSetChanged();
getListView().setSelection(messages.size()-1);
}
});
List Update worked for me before when the update was triggered from a GUI event, thus being in the UI thread.
However, when I update the list from another event/thread - i.e. a call from outside the app, the update would not be in the UI thread and it ignored the call to getListView. Calling the update with runOnUiThread as above did the trick for me. Thanks!!

Try this
#Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter = new ItemAdapter(getActivity(), items);//reload the items from database
adapter.notifyDataSetChanged();
}

adpter.notifyDataSetInvalidated();
Try this in onPause() method of Activity class.

If your list is contained in the Adapter itself, calling the function that updates the list should also call notifyDataSetChanged().
Running this function from the UI Thread did the trick for me:
The refresh() function inside the Adapter
public void refresh(){
//manipulate list
notifyDataSetChanged();
}
Then in turn run this function from the UI Thread
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.refresh()
}
});

Try like this:
this.notifyDataSetChanged();
instead of:
adapter.notifyDataSetChanged();
You have to notifyDataSetChanged() to the ListView not to the adapter class.

adapter.setNotifyDataChanged()
should do the trick.

Related

Listview not updating after arraylist is updated?

I've seen many other posts in this context, but none helped. Problem is when i click on the addField button, the listview inside dialog adds new view just once. But at other clicks it doesn't get updated though The adapter works correctly (I mean the getView is called and also the arrayList in the adapter is changed in size).
I've used notifyDataSetChanged() in the adapter class. No result! I used an instance of adpater class in activity and called myAdapter.notifyDataSetChanged(). No result!
here is my code:
public class MainActivity extends Activity {
ListView lvfid;
FieldAdapter fAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//some code
showFid();
}
private void showFiD(){
final ArrayList <HashMap<String,String>> al = new ArrayList<HashMap<String,String>>();
final Dialog dialog = new Dialog(MainActivity.this , R.style.DialogTheme);
dialog.setContentView(R.layout.field_dialog);
dialog.setCancelable(true);
dialog.show();
Button addField = (Button) dialog.findViewById(R.id.btnfidField);
lvfid = (ListView) dialog.findViewById(R.id.lvfid);
//Another plan i have tested, but no result:
//fAdapter = new FieldAdapter(dialog.getContext(),al);
//lvfid.setAdapter(fAdapter);
addField.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
HashMap<String,String> h = new HashMap<String,String>();
h.put("name", "");
h.put("value", "");
al.add(h);
lvfid.setAdapter(new FieldAdapter(dialog.getContext(), al));
//fAdapter.notifyDataSetChanged();
}
});
}
public class FieldAdapter extends BaseAdapter{
private ArrayList <HashMap <String,String>> arrayList;
private LayoutInflater inflater;
public FieldAdapter(Context context, ArrayList<HashMap <String,String>> arrayList) {
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.arrayList = arrayList;
}
#Override
public int getCount() {
return arrayList.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup arg2) {
View view = null;
if (convertView == null)
view = inflater.inflate(R.layout.field_inflater, null);
else
view = convertView;
Holder holder = new Holder();
holder.edName = (EditText) view.findViewById(R.id.edfidName);
holder.edValue = (EditText) view.findViewById(R.id.edfidValue);
//holder.edName.setText(arrayList.get(position).get("name"));
//holder.edValue.setText(arrayList.get(position).get("value"));
return view;
}
private class Holder {
private EditText edName;
private EditText edValue;
}
}
}
UPDATE:
I'm sorry i took everybody's time. The stupid problem was that my listview was inside a scrollview where it must not be! I hope this helps others who have the same issue!
Use the notifyDataSetChanged() every time the list is updated.
I had the same problem. I was facing with this problem when I wanted to update the listView after onDateChanged(). I resolved it with an extra ArrayList variable and an extra custom adapter in your case FieldAdapter variable.
Update your listView with a refresh adapter and array list variables after any operations. For example after button clicks.
1. Define a counter.
2. Check when the button is clicked, increase counter by one
3. Every time check the counter, if counter is greater than 1 it means the button is clicked more than once so update the list view with new variables (arrayList, FeildAdapter)
I recommend you to define the variables as a private class fields and name them such as refreshedArrayList and refreshedAdapter
Option
Try to use the ArrayAdapter. And don't create a new adapter every
time you click on the button.
only call the add(T o) function from the ArrayAdapter.
Other Option:
add a add function to your FieldAdapter in the add field adapter add the object to your arraylist and call notifiyDataSetChanged(the adapter has aslo one and the adapter notifies all his observers)
also don't create a new adapter every time you click the button.
public class MainActivity extends Activity {
ListView lvfid;
FieldAdapter fAdapter;
ArrayList <HashMap<String,String>> al ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//some code
al = new ArrayList<HashMap<String,String>>();
lvfid.setAdapter(new FieldAdapter(MainActivity.this , al));
showFid();
}
private void showFiD(){
final Dialog dialog = new Dialog(MainActivity.this , R.style.DialogTheme);
dialog.setContentView(R.layout.field_dialog);
dialog.setCancelable(true);
dialog.show();
Button addField = (Button) dialog.findViewById(R.id.btnfidField);
lvfid = (ListView) dialog.findViewById(R.id.lvfid);
//Another plan i have tested, but no result:
//fAdapter = new FieldAdapter(dialog.getContext(),al);
//lvfid.setAdapter(fAdapter);
addField.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
HashMap<String,String> h = new HashMap<String,String>();
h.put("name", "");
h.put("value", "");
al.add(h);
fAdapter.notifyDataSetChanged();
}
});
}
I'm sorry i took everybody's time! The stupid problem was just my listview was inside a scrollview where it must not be! after getting the listview out of scrollview the problem got solved.

Updating Recycler Listview dynamically using Database

I saw all the questions which is similar to my question ( in this , this , this and this link )
I had myAdapter.notifyDataSetChanged() in my Activity but it doesn't work
I have 3 classes,
DBHelper - For storing and getting Database contents ( NO ISSUES HERE )
SimpleRecyclerAdapter - Adapter for RecyclerList
ThirdActivity
What i did in ThirdActivity :
I have TextBox to get data and store it in Database and a Button. In
the Onclicklistener of Button, i specified code to
get text from textbox
add it into table using DBHelper
retrive data as ArrayList from DBHelper
myAdapter.notifyDataSetChanged()
When i click the Button, I got Data in LogCat which i specified inside OnclickListener but it is not reflected to the listview.
Here is my code,
ThirdActivity:
public class ThirdActivity extends AppCompatActivity{
private DrawerLayout mDrawerLayout;
DbHelper dbHelper;
EditText et;
Button addButton;
RecyclerView rv;
ArrayList<String> myNotesList;
SimpleRecycler3Adapter adapter3;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.thirdactivity);
myNotesList = new ArrayList<>();
et=(EditText) findViewById(R.id.et);
addButton=(Button)findViewById(R.id.addButton);
rv = (RecyclerView) findViewById(R.id.dbListrv);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getBaseContext());
rv.setLayoutManager(linearLayoutManager);
rv.setHasFixedSize(true);
adapter3 = new SimpleRecycler3Adapter(myNotesList);
rv.setAdapter(adapter3);
dbHelper = new DbHelper(this, null, null, 1);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("DB", "Constructor");
String note=et.getText().toString();
dbHelper.addNote(note);
printData();
}
});
}
public void printData(){
Log.d("DB","Constructor");
myNotesList=dbHelper.databasetostring();
Log.d("DB","Data came"+myNotesList.get(myNotesList.size()-1));
// adapter3 = new SimpleRecycler3Adapter(myNotesList);
// rv.setAdapter(adapter3);
adapter3.notifyDataSetChanged();
}
}
SimpleRecyclerViewAdapter :
public class SimpleRecycler3Adapter extends RecyclerView.Adapter<SimpleRecycler3Adapter.NotesHolder> {
private ArrayList<String> myNotesList=new ArrayList<String>();
String TAG="ThirdAdapter kbt";
RecyclerView rv;
public SimpleRecycler3Adapter(ArrayList<String> myList) {
Log.d(TAG,"Constructor");
Log.d(TAG,"Not null");
int i = 0;
while (i < myNotesList.size()) {
myNotesList.add(myList.get(i).toString());
}
Log.d(TAG,"finish");
}
#Override
public NotesHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Log.d(TAG,"On create started");
View view2 = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recyclerlist3_item, viewGroup, false);
Log.d(TAG,"ADAP STAR ONCR second switch 2nd line");
NotesHolder viewHolder2 = new NotesHolder(view2);
Log.d(TAG,"ADAP STAR ONCR second switch 3nd line");
return viewHolder2;
}
#Override
public void onBindViewHolder(NotesHolder notesHolder, int i) {
Log.d(TAG, "ONBIND SECOND i value is " + i);
// notesHolder.thumbnail.setImageResource(R.drawable.placeholder);
notesHolder.dblistitem.setText(myNotesList.get(i));
Log.d(TAG,"ONBIND second title issssss");
}
#Override
public int getItemCount() {
return myNotesList.size();
}
class NotesHolder extends RecyclerView.ViewHolder {
protected ImageView thumbnail;
protected TextView dblistitem;
public NotesHolder(View itemView) {
super(itemView);
Log.d(TAG, "JSON Inside HOLDER");
rv=(RecyclerView)itemView.findViewById(R.id.dbListrv);
// thumbnail = (ImageView) itemView.findViewById(R.id.thumbnail);
dblistitem = (TextView) itemView.findViewById(R.id.dblistitem);
}
}
}
You're not updating the myNotesList that is in adapter class but in activity class. But the adapter uses it's local myNotesList.
So on button click, update myNotesList of adapter with latest data available and notify the adapter.
EDIT
Pass the latest data to adapter. Have this method in adapter class and call this before notifyDataSetChanged();
public void updateNotes(ArrayList<String> notesList) {
myNotesList = notesList;
}
1.you are intializing your dbhelper after setting adapter to listview so it couldn't contain any data initially
2.for updating recycler view data list do as follows
myNotesList.clear();
myNotesList.addAll(dbHelper.databasetostring());
adapter3.notifyDataSetChanged();
You have a problem in your SimpleRecyclerViewAdapter, just change this:
while (i < myNotesList.size()) {
myNotesList.add(myList.get(i).toString());
}
For this:
myNotesList = myList;
And in your activity's printData() change:
myNotesList=dbHelper.databasetostring();
for this:
myNotesList.clear();
myNotesList.addAll(dbHelper.databasetostring());
adapter3.notifyDataSetChanged();
Explanation:
First you initialize myNotesList variable:
myNotesList = new ArrayList<>();
Then you initialize adapter3
adapter3 = new SimpleRecycler3Adapter(myNotesList);
But your adapter is not saving the reference, instead you're copying its data into another variable:
while (i < myNotesList.size()) {
myNotesList.add(myList.get(i).toString());
}
Doing that, if you change myNotesList variable in your activity will not modify your adapter's dataset.
In your method printData() you change myNotesList variable. Which will not touch your adapter or its data
public void printData(){
Log.d("DB","Constructor");
myNotesList=dbHelper.databasetostring();
Log.d("DB","Data came"+myNotesList.get(myNotesList.size()-1));
// adapter3 = new SimpleRecycler3Adapter(myNotesList);
// rv.setAdapter(adapter3);
adapter3.notifyDataSetChanged();
}
You can't change myNotesList by changing myList.
public SimpleRecycler3Adapter(ArrayList<String> myList) {
Log.d(TAG,"Constructor");
Log.d(TAG,"Not null");
// int i = 0;
// while (i < myNotesList.size()) {
// myNotesList.add(myList.get(i).toString());
// }
this.myNotesList = myList;
Log.d(TAG,"finish");
}
Not a good idea to call notifyDataSetChanged() when you know exactly what changed in your data collection.
See this implementation here.
They have even documented to use notifyDataSetChanged() as a last resort in this doc.
You get nice animations for free if you use methods like notifyItemInserted() and the rest.
Also do not go on replacing the collection object entirely, see the implmentation link that has been attached.

How to detect change in listview from mainactivity

Summary: I need a way to trigger my calculate() function within my main activity when an item is added or removed from my ListView
Background:
My android application fills a listview with list items. A list item contains a textview and an imagebutton (delete) that removes the item from the list on click. I use a custom adapter to keep track of changes in the list. This all works fine.
In my main acticity, some calculations take place based on the values in the list in a function called calulate(). I want to call this function whenever an item is added or deleted from the list. However, I don't know if this is possible and how to implement such a function.
I noticed that it is possible to add an observer using registerDataSetObserver() that will be notified when notifyDataSetChanged() is called. However, I'm not sure if this is what I need and how to implement this. Any help or suggestions are more than welcome.
Here is my CustomListAdapter:
public class CustomListAdapter extends BaseAdapter {
static final String TAG = "CustomListAdapter";
private Context context;
ArrayList <String> listArray;
LayoutInflater inflater;
public CustomListAdapter(Context context, List <String> inputArray) {
super();
this.context = context;
this.listArray = (ArrayList<String>) inputArray;
}
#Override
public int getCount() {
return listArray.size(); // total number of elements in the list
}
#Override
public String getItem(int i) {
return listArray.get(i); // single item in the list
}
#Override
public long getItemId(int i) {
return i; // index number
}
#Override
public View getView(int position, View convertView, final ViewGroup parent) {
View V = convertView;
if(V == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
V = vi.inflate(R.layout.selected_drug_list_item, null);
}
//place text in textview
String listItem = listArray.get(position);
TextView textView = (TextView) V.findViewById(R.id.selectedDrugName);
textView.setText(listItem);
ImageButton deleteSelectedDrugButton = (ImageButton) V.findViewById(R.id.deleteSelectedDrugButton);
deleteSelectedDrugButton.setTag(position);
//Listener for the delete button. Deletes item from list.
deleteSelectedDrugButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
//re
Integer index = (Integer) view.getTag();
listArray.remove(index.intValue());
notifyDataSetChanged();
}
});
return V;
}
public void add(String input) {
listArray.add(input);
notifyDataSetChanged();
Log.v(TAG, input + " added to list");
}
public void remove(String input){
listArray.remove(input);
notifyDataSetChanged();
Log.v(TAG, input + " added to list");
}
}
Here is how my ListView is initialized in my onCreate() method.
selectionListView = (ListView) findViewById(R.id.selectionListView);
selectionAdapter = new CustomListAdapter(this,myListItems);
selectionListView.setAdapter(selectionAdapter);
If any other code fragment is required, I'll happily provide it.
You may create Interfece that will be implemented by Your Main Activity and passed to Adapter (eg. in constructor)
public interface SomeInterface
{
public void foo();
}
Add SomeInterface object in Your Adapter
SomeInterface responder=null;
public CustomListAdapter(Context context, List <String> inputArray, SomeInterface responder) {
super();
this.context = context;
this.listArray = (ArrayList<String>) inputArray;
this.responder=responder;
}
public void add(String input) {
listArray.add(input);
notifyDataSetChanged();
Log.v(TAG, input + " added to list");
responder.foo();
}
public void remove(String input){
listArray.remove(input);
notifyDataSetChanged();
Log.v(TAG, input + " added to list");
responder.foo();
}
and implements SomeInterface in Your MainActivity
public class MainActivity extends Activity implements SomeInterface
{
...
public void foo()
{
//do whatever
}
private initializeAdapter()
{
CustomListAdapter adapter=new Adapter(this, someArray, this);
}
}
You can create a callback interface with a simple method, like stuffHappened(). Then, let your activity implement that interface. Now you can add a constructor argument which has as type the callback interface, pass the activity in, keep it as a member variable on the adapter and call the stuffHappened() method whenever you need to send feedback to your activity.

updating/refreshing List with custom ListAdapter

I am aware that there are plenty of similar questions, but they all have in common, that their solutions dont work with my list :(
I am trying to get my userList refreshing itself via the custom ArrayAdapter, when the database-contents are changed. In my case when i reset();
here my snippets (partial code):
MainActivity.java
public class MainActivity extends ListActivity {
private MyUserListAdapter myUserlistAdapter;
public ArrayList<User> myUserList = new ArrayList<User>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//shortened the list-filling. but it works properly!
User user = db.readUser(int);
myUserList.add(user);
myUserlistAdapter = new MyUserListAdapter(this, R.layout.row_main, myUserList);
setListAdapter(myUserlistAdapter);
//now when reset-button is hit, the listview should refresh itself
bReset.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//this is what is posted in most questions, but it does nothing for me
//myUserlistAdapter.notifyDataSetChanged();
//getListView().invalidateViews();
} });
and here myUserListAdapter.java:
public class MyUserListAdapter extends ArrayAdapter<User>{
private Context context;
private ArrayList<User> userList;
public MyUserListAdapter(Context context,
int textViewResourceId, ArrayList<User> userList) {
super(context, textViewResourceId, userList);
this.context = context;
this.userList = userList;
}
public View getView(int position, View v, ViewGroup parent) {
if (v == null) {
LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(R.layout.row_main, null);
}
User user = getItem(position);
TextView tvUser = (TextView) v.findViewById(R.id.tvUser);
ImageView ivVoted = (ImageView) v.findViewById(R.id.ivVoted);
tvUser.setText(user.getName());
//abfrage ob hasVoted() = true muss noch eingebaut werden.
if (user.getVoted().equals("1"))
ivVoted.setImageResource(R.drawable.redcheck);
else
ivVoted.setImageResource(R.drawable.greencheck);
return v;
}
}
User.java is just a simple object-class. think its not the troublemaker here!
any help is appreciated!!! thx :-)
I am trying to get my userList refreshing itself via the custom
ArrayAdapter, when the database-contents are changed.
Since you are using ArrayAdapter and not CursorAdapter when you update data in database your adapter won't refresh itself. Whenever you want to update ListView you need to provide new datasource for Adapter.
One possible solution is to create setter in adapter subclass that will change datasource of adapter.
Pseudo code:
/* setter in adapter subclass */
public void changeDataSource(ArrayList<User> newUserList) {
this.userList = newUserList;
}
Then call adapter.notifyDataSetChanged(); for ListView update.
Try this:
arrayAdapter.clear()
for(Object o : objects)
arrayAdapter.add(o)
clear() and add() call to notifyDataSetChanged() itself.

notifyDataSetChange not working from custom adapter

When I repopulate my ListView, I call a specific method from my Adapter.
Problem:
When I call updateReceiptsList from my Adapter, the data is refreshed, but my ListView doesn't reflect the change.
Question:
Why doesn't my ListView show the new data when I call notifyDataSetChanged?
Adapter:
public class ReceiptListAdapter extends BaseAdapter {
public List<Receipt> receiptlist;
private Context context;
private LayoutInflater inflater;
private DateHelpers dateH;
public ReceiptListAdapter(Activity activity, Context mcontext, List<Receipt> rl) {
context = mcontext;
receiptlist = rl;
Collections.reverse(receiptlist);
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
dateH = new DateHelpers();
}
#Override
public int getCount() {
try {
int size = receiptlist.size();
return size;
} catch(NullPointerException ex) {
return 0;
}
}
public void updateReceiptsList(List<Receipt> newlist) {
receiptlist = newlist;
this.notifyDataSetChanged();
}
#Override
public Receipt getItem(int i) {
return receiptlist.get(i);
}
#Override
public long getItemId(int i) {
return receiptlist.get(i).getReceiptId() ;
}
private String getPuntenString(Receipt r) {
if(r.getPoints().equals("1")) {
return "1 punt";
}
return r.getPoints()+" punten";
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
final Receipt receipt = receiptlist.get(position);
ReceiptViewHolder receiptviewholder;
Typeface tf_hn = Typeface.createFromAsset(context.getAssets(), "helveticaneue.ttf");
Typeface tf_hn_bold = Typeface.createFromAsset(context.getAssets(), "helveticaneuebd.ttf");
if (vi == null) { //convertview==null
receiptviewholder = new ReceiptViewHolder();
vi = inflater.inflate(R.layout.view_listitem_receipt, null);
vi.setOnClickListener(null);
vi.setOnLongClickListener(null);
vi.setLongClickable(false);
receiptviewholder.shop = (TextView) vi.findViewById(R.id.tv_listitemreceipt_shop);
receiptviewholder.date = (TextView) vi.findViewById(R.id.tv_listitemreceipt_date);
receiptviewholder.price = (TextView) vi.findViewById(R.id.tv_listitemreceipt_price);
receiptviewholder.points = (TextView) vi.findViewById(R.id.tv_listitemreceipt_points);
receiptviewholder.shop.setTypeface(tf_hn_bold);
receiptviewholder.price.setTypeface(tf_hn_bold);
vi.setTag(receiptviewholder);
}else{//convertview is not null
receiptviewholder = (ReceiptViewHolder)vi.getTag();
}
receiptviewholder.shop.setText(receipt.getShop());
receiptviewholder.date.setText(dateH.timestampToDateString(Long.parseLong(receipt.getPurchaseDate())));
receiptviewholder.price.setText("€ "+receipt.getPrice());
receiptviewholder.points.setText(getPuntenString(receipt));
vi.setClickable(false);
return vi;
}
public static class ReceiptViewHolder {
public TextView shop;
public TextView date;
public TextView price;
public TextView points;
}
public Object getFilter() {
// XXX Auto-generated method stub
return null;
}
}
--EDIT:
found Workaround
Just to have some functional code i do now:
listview.setAdapter( new ReceiptListAdapter(activity,mcontext, -new dataset-);
Works, but not how it is supposed to work.
Change your method from
public void updateReceiptsList(List<Receipt> newlist) {
receiptlist = newlist;
this.notifyDataSetChanged();
}
To
public void updateReceiptsList(List<Receipt> newlist) {
receiptlist.clear();
receiptlist.addAll(newlist);
this.notifyDataSetChanged();
}
So you keep the same object as your DataSet in your Adapter.
I have the same problem, and i realize that. When we create adapter and set it to listview, listview will point to object somewhere in memory which adapter hold, data in this object will show in listview.
adapter = new CustomAdapter(data);
listview.setadapter(adapter);
if we create an object for adapter with another data again and notifydatasetchanged():
adapter = new CustomAdapter(anotherdata);
adapter.notifyDataSetChanged();
this will do not affect to data in listview because the list is pointing to different object, this object does not know anything about new object in adapter, and notifyDataSetChanged() affect nothing.
So we should change data in object and avoid to create a new object again for adapter
As I have already explained the reasons behind this issue and also how to handle it in a different answer thread Here. Still i am sharing the solution summary here.
One of the main reasons notifyDataSetChanged() won't work for you - is,
Your adapter loses reference to your list.
When creating and adding a new list to the Adapter. Always follow these guidelines:
Initialise the arrayList while declaring it globally.
Add the List to the adapter directly with out 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 loose the
reference.
Always modify the data in the arrayList itself (if your data is completely new
than 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()
Hope this helps.
Maybe try to refresh your ListView:
receiptsListView.invalidate().
EDIT: Another thought came into my mind. Just for the record, try to disable list view cache:
<ListView
...
android:scrollingCache="false"
android:cacheColorHint="#android:color/transparent"
... />
I had the same problem using ListAdapter
I let Android Studio implement methods for me and this is what I got:
public class CustomAdapter implements ListAdapter {
...
#Override
public void registerDataSetObserver(DataSetObserver observer) {
}
#Override
public void unregisterDataSetObserver(DataSetObserver observer) {
}
...
}
The problem is that these methods do not call super implementations so notifyDataSetChange is never called.
Either remove these overrides manually or add super calls and it should work again.
#Override
public void registerDataSetObserver(DataSetObserver observer) {
super.registerDataSetObserver(observer);
}
#Override
public void unregisterDataSetObserver(DataSetObserver observer) {
super.unregisterDataSetObserver(observer);
}
If adapter is set to AutoCompleteTextView then notifyDataSetChanged() doesn't work.
Need this to update adapter:
myAutoCompleteAdapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_dropdown_item_1line, myList);
myAutoComplete.setAdapter(myAutoCompleteAdapter);
Refer: http://android-er.blogspot.in/2012/10/autocompletetextview-with-dynamic.html
class StudentAdapter extends BaseAdapter {
ArrayList<LichHocDTO> studentList;
private void capNhatDuLieu(ArrayList<LichHocDTO> list){
this.studentList.clear();
this.studentList.addAll(list);
this.notifyDataSetChanged();
}
}
You can try. It work for me
If by any chance you landed on this thread and wondering why adapter.invaidate() or adapter.clear() methods are not present in your case then maybe because you might be using RecyclerView.Adapter instead of BaseAdapter which is used by the asker of this question. If clearing the list or arraylist not resolving your problem then it may happen that you are making two or more instances of the adapter for ex.:
MainActivity
...
adapter = new CustomAdapter(list);
adapter.notifyDataSetChanged();
recyclerView.setAdapter(adapter);
...
and
SomeFragment
...
adapter = new CustomAdapter(newList);
adapter.notifyDataSetChanged();
...
If in the second case you are expecting a change in the list of inflated views in recycler view then it is not gonna happen as in the second time a new instance of the adapter is created which is not attached to the recycler view. Setting notifyDataSetChanged in the second adapter is not gonna change the content of recycer view. For that make a new instance of the recycler view in SomeFragment and attach it to the new instance of the adapter.
SomeFragment
...
recyclerView = new RecyclerView();
adapter = new CustomAdapter();
recyclerView.setAdapter(adapter);
...
Although, I don't recommend making multiple instances of the same adapter and recycler view.
In my case I simply forget to add in my fragment mRecyclerView.setAdapter(adapter)
Add this code
runOnUiThread(new Runnable() { public void run() {
adapter = new CustomAdapter(anotherdata);
adapter.notifyDataSetChanged();
}
});
I made a very noob mistake that I was setting the adapter of RecyclerView before initialzing the adapter itself like this.
// Assuume oneOffJobTasksListRVAdapter is declared already
recyclerView.setAdapter(oneOffJobTasksListRVAdapter);
oneOffJobTasksListRVAdapter = new OneOffJobTasksListRVAdapter();
Switching the lines fixed my issue.
oneOffJobTasksListRVAdapter = new OneOffJobTasksListRVAdapter();
recyclerView.setAdapter(oneOffJobTasksListRVAdapter);
If you're using a custom adapter you have to add
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
to your custom adapter methods, then you only need to call notifyDataSetChanged() after you change your data, like replace, remove or add a new item
ArrayList <String> items;
int position=1;
items.set(position,"Changed Item");
items.remove(position);
items.add("New item");
notifyDataSetChanged();
I have the same problem
but I just finished it!!
you should change to
public class ReceiptListAdapter extends BaseAdapter {
public List<Receipt> receiptlist;
private Context context;
private LayoutInflater inflater;
private DateHelpers dateH;
private List<ReceiptViewHolder> receiptviewlist;
public ReceiptListAdapter(Activity activity, Context mcontext, List<Receipt> rl) {
context = mcontext;
receiptlist = rl;
receiptviewlist = new ArrayList<>();
receiptviewlist.clear();
for(int i = 0; i < receiptlist.size(); i++){
ReceiptViewHolder receiptviewholder = new ReceiptViewHolder();
receiptviewlist.add(receiptviewholder);
}
Collections.reverse(receiptlist);
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
dateH = new DateHelpers();
}
#Override
public int getCount() {
try {
int size = receiptlist.size();
return size;
} catch(NullPointerException ex) {
return 0;
}
}
public void updateReceiptsList(List<Receipt> newlist) {
receiptlist = newlist;
this.notifyDataSetChanged();
}
#Override
public Receipt getItem(int i) {
return receiptlist.get(i);
}
#Override
public long getItemId(int i) {
return receiptlist.get(i).getReceiptId() ;
}
private String getPuntenString(Receipt r) {
if(r.getPoints().equals("1")) {
return "1 punt";
}
return r.getPoints()+" punten";
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
final Receipt receipt = receiptlist.get(position);
ReceiptViewHolder receiptviewholder;
Typeface tf_hn = Typeface.createFromAsset(context.getAssets(), "helveticaneue.ttf");
Typeface tf_hn_bold = Typeface.createFromAsset(context.getAssets(), "helveticaneuebd.ttf");
if (vi == null) { //convertview==null
ReceiptViewHolder receiptviewholder = receiptviewlist.get(position);
vi = inflater.inflate(R.layout.view_listitem_receipt, null);
vi.setOnClickListener(null);
vi.setOnLongClickListener(null);
vi.setLongClickable(false);
receiptviewholder.shop = (TextView) vi.findViewById(R.id.tv_listitemreceipt_shop);
receiptviewholder.date = (TextView) vi.findViewById(R.id.tv_listitemreceipt_date);
receiptviewholder.price = (TextView) vi.findViewById(R.id.tv_listitemreceipt_price);
receiptviewholder.points = (TextView) vi.findViewById(R.id.tv_listitemreceipt_points);
receiptviewholder.shop.setTypeface(tf_hn_bold);
receiptviewholder.price.setTypeface(tf_hn_bold);
vi.setTag(receiptviewholder);
}else{//convertview is not null
receiptviewholder = (ReceiptViewHolder)vi.getTag();
}
receiptviewholder.shop.setText(receipt.getShop());
receiptviewholder.date.setText(dateH.timestampToDateString(Long.parseLong(receipt.getPurchaseDate())));
receiptviewholder.price.setText("€ "+receipt.getPrice());
receiptviewholder.points.setText(getPuntenString(receipt));
vi.setClickable(false);
return vi;
}
public static class ReceiptViewHolder {
public TextView shop;
public TextView date;
public TextView price;
public TextView points;
}
public Object getFilter() {
// XXX Auto-generated method stub
return null;
}
}
My case was different but it might be the same case for others
for those who still couldn't find a solution and tried everything above, if you're using the adapter inside fragment then the reason it's not working fragment could be recreating so the adapter is recreating everytime the fragment recreate
you should verify if the adapter and objects list are null before initializing
if(adapter == null){
adapter = new CustomListAdapter(...);
}
...
if(objects == null){
objects = new ArrayList<>();
}

Categories

Resources