Skip to main content
added 8751 characters in body
Source Link
Xaver Kapeller
  • 49.9k
  • 11
  • 100
  • 86

EDIT:

There are a few things in your code that are not quite optimal. First and foremost, your Adapter should not contain the code to download the contacts. It just doesn't belong there, the only responsibility of an Adapter should be that he creates Views from a given data source. So first you should move the getAllContacts() method outside of the Adapter. I suggest you create static helper method for this, I took the liberty of modifying your code accordingly:

public class ContactsHelper { public static List<Contact> getAllContacts(Context context) { List<Contact> contactList = new ArrayList<Contact>(); String URL = "content://com.example.provider.Contacts/contacts"; Uri baseUri1 = Uri.parse(URL); String[] select = {ContactsContentProvider.PHONE_ID, ContactsContentProvider.STATUS}; String where = "((" + ContactsContentProvider.NEEO_USER + " NOTNULL) AND (" + ContactsContentProvider.NEEO_USER + " = 1 )AND (" + ContactsContentProvider.STATUS + " = 1 ))"; Cursor cursor = context.getContentResolver().query(baseUri1, select, where, null, "pid"); for (cursor.moveToFirst(); cursor.moveToNext(); cursor.isAfterLast()) { Log.w("Filtered IDS", "" + cursor.getString(cursor.getColumnIndex(ContactsContentProvider.PHONE_ID)) + "" + cursor.getString(cursor.getColumnIndex(ContactsContentProvider.STATUS))); } Uri baseUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; String[] projection = new String[]{ ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER}; String selection = "((" + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " NOTNULL) AND (" + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " != ' ' ))"; String[] selectionArgs = null; String sortOrder = ContactsContract.CommonDataKinds.Phone._ID + " COLLATE LOCALIZED ASC"; Cursor mCursor = context.getContentResolver().query(baseUri, projection, selection, selectionArgs, sortOrder); // Joinging Both Cursors CursorJoiner joiner = new CursorJoiner(cursor, new String[]{ContactsContentProvider.PHONE_ID}, mCursor, new String[]{ContactsContract.CommonDataKinds.Phone._ID}); for (CursorJoiner.Result joinerResult : joiner) { Contact cont = new Contact(); Log.e("Result", joinerResult.toString()); switch (joinerResult) { case LEFT: // handle case where a row in cursorA is unique break; case RIGHT: // handle case where a row in cursorB is unique cont.setID(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID))); cont.setName(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))); cont.setPhoneNumber(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))); cont.setStatus("0"); contactList.add(cont); break; case BOTH: // handle case where a row with the same key is in both cursors cont.setID(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID))); cont.setName(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))); cont.setPhoneNumber(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))); cont.setStatus(cursor.getString(cursor.getColumnIndex(ContactsContentProvider.STATUS))); contactList.add(cont); break; } } mCursor.close(); cursor.close(); return contactList; } } 

With this code you can get all contacts like this:

List<Contact> contacts = ContactsHelper.getAllContacts(getActivity()); 

After this we need to modify your Adapter so that notifyDateSetChanged() will work:

private class CustomAdapterForAllContacts extends BaseAdapter { private final List<Contact> contactsList; private final LayoutInflater inflater; public CustomAdapterForAllContacts(Context context, List<Contact> contacts) { this.inflater = LayoutInflater.from(context); this.contactsList = contacts; } @Override public int getCount() { return contactsList.size(); } @Override public Contact getItem(int position) { return contactsList.get(position); } @Override public long getItemId(int position) { return position; } // We expose the List so we can modify it from outside public List<Contact> contacts() { return this.contactsList; } private class SimpleViewHolder { private final SparseArray<View> viewArray = new SparseArray<View>(); private final View convertView; public SimpleViewHolder(View convertView) { this.convertView = convertView; } public View get(int id) { View view = this.viewArray.get(id, null); if(view == null) { view = this.convertView.findViewById(id); this.viewArray.put(id, view); } return view; } } @Override public View getView(int position, View convertView, ViewGroup viewGroup) { // By implementing the view holder pattern you only need to perform // findViewById() once. This will improve the performance of `ListView` // and reduce lag. SimpleViewHolder viewHolder; if (convertView == null) { convertView = this.inflater.inflate(R.layout.list_item, viewGroup, false); viewHolder = new SimpleViewHolder(convertView); convertView.setTag(viewHolder); } viewHolder = (SimpleViewHolder) convertView.getTag(); TextView contName = (TextView) viewHolder.get(R.id.nameText); TextView contNumber = (TextView) viewHolder.get(R.id.numberText); ImageView image = (ImageView) viewHolder.get(R.id.contact_image); Contact contact = getItem(position); String status = contact.getStatus(); if (contact.getStatus().equals("1")) { image.setBackgroundResource(com.example.mycontentprovider.R.drawable.person_empty_online); } else { image.setBackgroundResource(com.example.mycontentprovider.R.drawable.person_empty_offline); } contName.setText(contact.getName()); contNumber.setText(contact.getPhoneNumber()); return view; } } 

I have changed multiple things in this Adapter. First and foremost the List of Contacts is now final and I added a method contacts() to expose the List so we can modify the data in the Adapter from the outside. I also implemented the view holder pattern so your ListView scrolls faster and smoother!

I hope I haven't forgotten anything, but this should be all the changes you need. You can use the new Adapter like this:

List<Contact> contacts = ContactsHelper.getAllContacts(getActivity()); CustomAdapterForAllContacts adapter = new CustomAdapterForAllContacts(getActivity(), contacts); listView.setAdapter(adapter); 

If you want to update the ListView later on you need to modify the List inside the Adapter like this:

List<Contact> newData = ContactsHelper.getAllContacts(getActivity()); adapter.contacts().clear(); adapter.contacts().addAll(newData); adapter.notifyDataSetChanged(); 

EDIT:

There are a few things in your code that are not quite optimal. First and foremost, your Adapter should not contain the code to download the contacts. It just doesn't belong there, the only responsibility of an Adapter should be that he creates Views from a given data source. So first you should move the getAllContacts() method outside of the Adapter. I suggest you create static helper method for this, I took the liberty of modifying your code accordingly:

public class ContactsHelper { public static List<Contact> getAllContacts(Context context) { List<Contact> contactList = new ArrayList<Contact>(); String URL = "content://com.example.provider.Contacts/contacts"; Uri baseUri1 = Uri.parse(URL); String[] select = {ContactsContentProvider.PHONE_ID, ContactsContentProvider.STATUS}; String where = "((" + ContactsContentProvider.NEEO_USER + " NOTNULL) AND (" + ContactsContentProvider.NEEO_USER + " = 1 )AND (" + ContactsContentProvider.STATUS + " = 1 ))"; Cursor cursor = context.getContentResolver().query(baseUri1, select, where, null, "pid"); for (cursor.moveToFirst(); cursor.moveToNext(); cursor.isAfterLast()) { Log.w("Filtered IDS", "" + cursor.getString(cursor.getColumnIndex(ContactsContentProvider.PHONE_ID)) + "" + cursor.getString(cursor.getColumnIndex(ContactsContentProvider.STATUS))); } Uri baseUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; String[] projection = new String[]{ ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER}; String selection = "((" + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " NOTNULL) AND (" + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " != ' ' ))"; String[] selectionArgs = null; String sortOrder = ContactsContract.CommonDataKinds.Phone._ID + " COLLATE LOCALIZED ASC"; Cursor mCursor = context.getContentResolver().query(baseUri, projection, selection, selectionArgs, sortOrder); // Joinging Both Cursors CursorJoiner joiner = new CursorJoiner(cursor, new String[]{ContactsContentProvider.PHONE_ID}, mCursor, new String[]{ContactsContract.CommonDataKinds.Phone._ID}); for (CursorJoiner.Result joinerResult : joiner) { Contact cont = new Contact(); Log.e("Result", joinerResult.toString()); switch (joinerResult) { case LEFT: // handle case where a row in cursorA is unique break; case RIGHT: // handle case where a row in cursorB is unique cont.setID(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID))); cont.setName(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))); cont.setPhoneNumber(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))); cont.setStatus("0"); contactList.add(cont); break; case BOTH: // handle case where a row with the same key is in both cursors cont.setID(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID))); cont.setName(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))); cont.setPhoneNumber(mCursor.getString(mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))); cont.setStatus(cursor.getString(cursor.getColumnIndex(ContactsContentProvider.STATUS))); contactList.add(cont); break; } } mCursor.close(); cursor.close(); return contactList; } } 

With this code you can get all contacts like this:

List<Contact> contacts = ContactsHelper.getAllContacts(getActivity()); 

After this we need to modify your Adapter so that notifyDateSetChanged() will work:

private class CustomAdapterForAllContacts extends BaseAdapter { private final List<Contact> contactsList; private final LayoutInflater inflater; public CustomAdapterForAllContacts(Context context, List<Contact> contacts) { this.inflater = LayoutInflater.from(context); this.contactsList = contacts; } @Override public int getCount() { return contactsList.size(); } @Override public Contact getItem(int position) { return contactsList.get(position); } @Override public long getItemId(int position) { return position; } // We expose the List so we can modify it from outside public List<Contact> contacts() { return this.contactsList; } private class SimpleViewHolder { private final SparseArray<View> viewArray = new SparseArray<View>(); private final View convertView; public SimpleViewHolder(View convertView) { this.convertView = convertView; } public View get(int id) { View view = this.viewArray.get(id, null); if(view == null) { view = this.convertView.findViewById(id); this.viewArray.put(id, view); } return view; } } @Override public View getView(int position, View convertView, ViewGroup viewGroup) { // By implementing the view holder pattern you only need to perform // findViewById() once. This will improve the performance of `ListView` // and reduce lag. SimpleViewHolder viewHolder; if (convertView == null) { convertView = this.inflater.inflate(R.layout.list_item, viewGroup, false); viewHolder = new SimpleViewHolder(convertView); convertView.setTag(viewHolder); } viewHolder = (SimpleViewHolder) convertView.getTag(); TextView contName = (TextView) viewHolder.get(R.id.nameText); TextView contNumber = (TextView) viewHolder.get(R.id.numberText); ImageView image = (ImageView) viewHolder.get(R.id.contact_image); Contact contact = getItem(position); String status = contact.getStatus(); if (contact.getStatus().equals("1")) { image.setBackgroundResource(com.example.mycontentprovider.R.drawable.person_empty_online); } else { image.setBackgroundResource(com.example.mycontentprovider.R.drawable.person_empty_offline); } contName.setText(contact.getName()); contNumber.setText(contact.getPhoneNumber()); return view; } } 

I have changed multiple things in this Adapter. First and foremost the List of Contacts is now final and I added a method contacts() to expose the List so we can modify the data in the Adapter from the outside. I also implemented the view holder pattern so your ListView scrolls faster and smoother!

I hope I haven't forgotten anything, but this should be all the changes you need. You can use the new Adapter like this:

List<Contact> contacts = ContactsHelper.getAllContacts(getActivity()); CustomAdapterForAllContacts adapter = new CustomAdapterForAllContacts(getActivity(), contacts); listView.setAdapter(adapter); 

If you want to update the ListView later on you need to modify the List inside the Adapter like this:

List<Contact> newData = ContactsHelper.getAllContacts(getActivity()); adapter.contacts().clear(); adapter.contacts().addAll(newData); adapter.notifyDataSetChanged(); 
added 1728 characters in body
Source Link
Xaver Kapeller
  • 49.9k
  • 11
  • 100
  • 86

You justhave multiple options to solve this depending on how exactly you implemented your ListView and Adapter.

  1. By calling notifyDataSetChanged()
  2. By resetting the Adapter

Updating with notifyDataSetChanged()

This is the best solution there is, but you need to modify the List the Adapter is using for this to work. For example if you use an ArrayAdapter like this:

String[] dataSource = new String[] { "A", "B", "C", ... }; ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, R.layout.example, dataSource); 

As you can see the String[] is the dataSource for our Adapter. We can modify the array like this, but those changes will not immediately be reflected in the ListView:

dataSource[0] = "some new String value"; 

Only once we call notifyDataSetChanged() on yourwill the AdapterListView like thisbe updated:

adapter.notifyDataSetChanged(); 

From the documentation:

public void notifyDataSetChanged ()

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


 

But DO NOT confuse notifyDataSetChanged() with notifyDataSetInvalidated(), because notifyDataSetInvalidated() does something completely different and would just mess up your ListView.

From the documentation:

public void notifyDataSetInvalidated ()

Notifies the attached observers that the underlying data is no longer valid or available. Once invoked this adapter is no longer valid and should not report further data set changes.


Updating by resetting the Adapter

This is pretty straight forward. Every time you set a new Adapter the ListView will update itself. If you cannot use notifyDataSetChanged() for some reason then you have to do it like this. Just create a new Adapter every time you want to update your ListView:

ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, R.layout.example, newData); 

And use setAdapter() to set it to the ListView:

listView.setAdapter(adapter); 

This will always update the ListView but there are a few problems with this solution. First and foremost every time you update the ListView this way it would scroll back to the top which can be quite annoying when there are frequent updates or when there is a lot of content in the ListView.


I hope I could help you and if you have any further questions please feel free to ask!

You just need to call notifyDataSetChanged() on your Adapter like this:

adapter.notifyDataSetChanged(); 

From the documentation:

public void notifyDataSetChanged ()

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


 

But DO NOT confuse notifyDataSetChanged() with notifyDataSetInvalidated(), because notifyDataSetInvalidated() does something completely different and would just mess up your ListView.

From the documentation:

public void notifyDataSetInvalidated ()

Notifies the attached observers that the underlying data is no longer valid or available. Once invoked this adapter is no longer valid and should not report further data set changes.

You have multiple options to solve this depending on how exactly you implemented your ListView and Adapter.

  1. By calling notifyDataSetChanged()
  2. By resetting the Adapter

Updating with notifyDataSetChanged()

This is the best solution there is, but you need to modify the List the Adapter is using for this to work. For example if you use an ArrayAdapter like this:

String[] dataSource = new String[] { "A", "B", "C", ... }; ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, R.layout.example, dataSource); 

As you can see the String[] is the dataSource for our Adapter. We can modify the array like this, but those changes will not immediately be reflected in the ListView:

dataSource[0] = "some new String value"; 

Only once we call notifyDataSetChanged() will the ListView be updated:

adapter.notifyDataSetChanged(); 

From the documentation:

public void notifyDataSetChanged ()

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

But DO NOT confuse notifyDataSetChanged() with notifyDataSetInvalidated(), because notifyDataSetInvalidated() does something completely different and would just mess up your ListView.

From the documentation:

public void notifyDataSetInvalidated ()

Notifies the attached observers that the underlying data is no longer valid or available. Once invoked this adapter is no longer valid and should not report further data set changes.


Updating by resetting the Adapter

This is pretty straight forward. Every time you set a new Adapter the ListView will update itself. If you cannot use notifyDataSetChanged() for some reason then you have to do it like this. Just create a new Adapter every time you want to update your ListView:

ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, R.layout.example, newData); 

And use setAdapter() to set it to the ListView:

listView.setAdapter(adapter); 

This will always update the ListView but there are a few problems with this solution. First and foremost every time you update the ListView this way it would scroll back to the top which can be quite annoying when there are frequent updates or when there is a lot of content in the ListView.


I hope I could help you and if you have any further questions please feel free to ask!

Source Link
Xaver Kapeller
  • 49.9k
  • 11
  • 100
  • 86

You just need to call notifyDataSetChanged() on your Adapter like this:

adapter.notifyDataSetChanged(); 

From the documentation:

public void notifyDataSetChanged ()

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


But DO NOT confuse notifyDataSetChanged() with notifyDataSetInvalidated(), because notifyDataSetInvalidated() does something completely different and would just mess up your ListView.

From the documentation:

public void notifyDataSetInvalidated ()

Notifies the attached observers that the underlying data is no longer valid or available. Once invoked this adapter is no longer valid and should not report further data set changes.