I have a simple RecyclerView for displaying items. Currently, from the RecyclerView.Adapter, I can delete items successfully using the following.
private void removeItem(int pos) {
filteredDataSet.remove(pos);
notifyItemRemoved(pos);
notifyItemRangeChanged(pos, getItemCount());
}
I call it from the onClick() function in the ViewHolder.The animations work, the view is updated, everything works. Pretty standard RecyclerView.
However, what I'd like to do is have the user verify the item deletion via a Dialog. Here's the basic setup for the Dialog (leaving out unnecessary code):
...
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setTitle("Delete this item?");
builder.setView(layout);
final int itemPos = pos;
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeItem(itemPos);
}
});
...
So, I'm just moving the method call into the onClickListener of the Dialog.
The problem I'm having is that when the RecyclerView animates away the removed item, it animates it back in the exact same position, and the list stays the same. Like it's still there.
But, if I scroll down I get a out of bounds error:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position
Which means it's not actually there, and when go back and come into the View again, it's gone. So, it seems like it's cached and not updating the adapter or dataset. I read that it needs called on the main thread, so I modified my method to this:
private void removeItem(int pos) {
final int itemPos = pos;
Handler handler = new Handler(Looper.getMainLooper());
Runnable runnable = new Runnable() {
#Override
public void run() {
filteredDataSet.remove(itemPos);
notifyItemRemoved(itemPos);
notifyItemRangeChanged(itemPos, getItemCount());
}
};
handler.post(runnable);
}
It's still not working, and I'm at a loss. I suspect it's a thread issue, but not sure where to turn from here.
This problem was solved by using a callback to trigger the removeItem() function when the item is deleted from the database. Apparently, adapter was being notified before the item was actually deleted from the database.
I'm using DBFlow to perform queries, so the solution is only applicable to DBFlow based solutions. This is called from the view that holds the RecyclerView:
public void deleteItem(int pos, long id) {
DatabaseDefinition database = FlowManager.getDatabase(AppDatabase.class);
//hold id to remove later
final long tempId = id;
final int tempPos = pos;
final ItemModel currentItem = getItem(id);
Transaction transaction = database.beginTransactionAsync(new ITransaction() {
#Override
public void execute(DatabaseWrapper databaseWrapper) {
currentItem.delete();
}
}).success(new Transaction.Success() {
#Override
public void onSuccess(Transaction transaction) {
mAdapter.removeItem(tempPos); //called here
}
}).error(new Transaction.Error() {
#Override
public void onError(Transaction transaction, Throwable error) {
Log.d("delete error", "item not deleted");
}
}).build();
transaction.execute();
}
if your removing from onClick from ViewHolder class then use getAdapterPosition() to get clicked location
if this code not working can you tell what is onItemClick() from your ViewHolder
I am looking to integrate MergeAdapter into my project and I am having an issue trying to retrieve which section the user has clicked. I want to set it up so when user clicks any item in any section, the section number is returned so I know which section the user is in. The app I am working on requires this.
Code below sets up 3 sections with some data in each section.
public class SectionTesting extends Activity
{
ListView listView;
private MergeAdapter mergeAdapter = null;
private static final String[] items =
{ "One", "Two", "Three" };
Context context;
#Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.mergelayout);
context = this;
listView = (ListView) findViewById(R.id.mergeListView);
mergeAdapter = new MergeAdapter();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, new ArrayList<String>(
Arrays.asList(items)));
TextView sectionHeader = new TextView(this);
sectionHeader.setText("Section One");
mergeAdapter.addView(sectionHeader);
mergeAdapter.addAdapter(adapter);
TextView sectionHeaderTwo = new TextView(this);
sectionHeaderTwo.setText("Section Two");
mergeAdapter.addView(sectionHeaderTwo);
mergeAdapter.addAdapter(adapter);
TextView sectionHeaderThree = new TextView(this);
sectionHeaderThree.setText("Section Three");
mergeAdapter.addView(sectionHeaderThree);
mergeAdapter.addAdapter(adapter);
listView.setAdapter(mergeAdapter);
listView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View v, int position,
long arg3)
{
Toast.makeText(
context,
"You clicked Section "
+ mergeAdapter.getSectionForPosition(position),
Toast.LENGTH_SHORT).show();
}
});
}
}
I was hoping getSectionForPosition method would return the value I require, but it just returns 0 everytime. I tried calling the method getSections().length as well to check it was returning the correct number of sections, but again it comes back as 0 everytime.
Any help would be much appreciated!!
Edit
I managed to come up with this messy solution. In the MergeAdapter class, under the addView method, I added this line of code
view.setId(1000);
Then I added this method here
public int getSectionNumber(int position)
{
int section = 0;
for (ListAdapter piece : getPieces())
{
int size = piece.getCount();
if (position < size)
{
return section-=1;
}
position -= size;
if(size == 1)
{
if(piece.getItem(0) instanceof TextView)
{
TextView tv = (TextView) piece.getItem(0);
if(tv.getId() == 1000)
{
section++;
}
}
}
}
return (-1);
}
The code seems to work fine, its just a bit sloppy I think. If anyone can come up with a cleaner solution that would be much appreciated, if not, then I will just add this as the answer when I can and accept it
You have at least two problems:
In order to use getSectionForPosition(), your Adapter has to implement the SectionIndexer interface. ArrayAdapter<> does not. All MergeAdapter does is try to use any SectionIndexer implementations passed to it, and you have passed zero such implementations.
You are trying to reuse the same Adapter instance several times inside of the MergeAdapter, and I have no idea if that will work and certainly do not recommend it.
To address these, create individual adapters per section, and have those adapters implement SectionIndexer.
I want to know if is it a good way to use the same adapter for more than one listview.
in my code i have many listviews and each one contains the same UL components like imageview and textview, so is it good to use `MyAdapter extends BaseAdapter` for each of them ? or it is better to make adapter for each one?
if i have to use one adapter, how to handle the different onclick actions for the button, imageview and textview for each listview ?
class MyAdapter extends BaseAdapter {
public MyAdapter() {
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return data.size();
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
return null;
}
}
It will make no difference resource-wise either way, as you will have to create a new instance of the adapter for each listview anyway. But trying to incorporate the features of two different adapters into one even just sounds overly complex. I would say for clarity of design, just make two different adapters. It'll make your life so much easier in the long run when it comes to debugging as well.
Keep in mind this is when the behaviors of each list are different, if the lists are supposed to function the same go ahead and use the same adapter for each.
Are you talking about reusing the instance of the adapter or its class? The class can be reused ad infinatum.
The instance, however, is safer not to be reused. The reason for this is you will likely have collsions or artifacts from the previous AdapterView. Adapter creation is menial, so why not just be safe and create a new one for each AdapterView?
This is a really good question I often struggle with. Seems so unnecessary duplicating so much adapter code just for different actions. I still struggle with this questions as a design issue, so my answer is not intended to provide an answer on that. However, for the part of the question about reusing the adapter or not, what I do if I wish to reuse a list/adapter is this:
For each type of list I create a global constant value to act as an identifier for that type of list. When I create a new instance of the adapter I supply the requestId/listTypeId to the adapter:
//first i create the constants somewhere globally
TYPE_ID_A = 0;
TYPE_ID_B = 1;
TYPE_ID_C = 2
//then i feed them to my adapter and set the clickListener on my list
mList.setAdapter(new MyListAdapter(mContext, listData, TYPE_ID_A));
mList.setOnItemClickListener(this);
In my adapter I set this typeId as a member variable and further then create a public function to return this id:
public class MyListAdapter extends ArrayAdapter<JSONArray> {
private final Context mContext;
private final JSONArray mItems;
private final int mListType;
//assign the values in the constructor of the adapter
public SearchListAdapter(Context context, JSONArray items, int listType) {
super(context, R.layout.item_filter_list);
mItems = items;
mContext = context;
mListType = listType;
}
//function to return the list id
public int getListType(){
return mListType;
}
}
Finally, inside my onClick listener I call this function inside my adapter to return the listTypeId which I then compare the global constants to identify what do to further:
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
MyListAdapter adapter = (MyListAdapter) adapterView.getAdapter();
int listType = adapter.getListType(); //get the listTypeId now
//now see which list type was clicked:
switch(listType){
case(TYPE_ID_A):
//to action for list A
break;
case(TYPE_ID_B):
//to action for list B
break;
}
}
This works for me but I dont think its great. If any one has another proper design pattern please let us know!
UPDATE
The original question i asked was about my long id value but because you guys were right in the way u said i had the correct id i removed my error. Thanks for the help. read my answer for more detail.
1) My app uses the local android SQLiteDatabase and has three tables. I have no problems for two of the tables but turns out my third one is presenting some issues because of my column declarations are public static final string COLUMN_NAME = "name"; ,etc.
My Activities are not extending the ListActivity so that I can have custom lists and listviews for each activity.
I am getting my listview by listview = (ListView) findViewById(R.id.myList); and adding a listener to the listview by listview.setOnItemClickListener(ListListener); Then here is my method for the list listener:
OnItemClickListener ListListener = new OnItemClickListener(){
public void onItemClick(AdapterView<?> arg0, View v, int position,
final long id)
{
AlertDialog.Builder dialog = new AlertDialog.Builder(ExerciseList.this)
.setIcon(R.drawable.edit)
.setTitle("Update Selected Exercise")
.setMessage("Would you like to update the current Exercise? Click continue to proceed.")
.setPositiveButton("Continue", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
final Intent i = new Intent(getBaseContext(), AddExercise.class);
i.putExtra(ExerciseDbAdapter.KEY_ROW_ID, id);
startActivityForResult(i, EDIT_EXERCISE);
}
})
.setNegativeButton("Back", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
dialog.show();
}
};
This above method is just a working on list item click listener!
Intent.putExtra("Key",value) is right way to put the data in intent so
i.putExtra("INSERT THE KEY HERE",ExerciseDbAdapter.KEY_ROW_ID, id);
Okay guys so i found the issue with my application and you were all right. I was getting the correct row id from the application.
I however was passing another data member through my intent causing the setRowIdFromIntent() method to change the id from null to 0. or from not null to 0.
Basically no matter what the value i was passing it was being set to 0 from my setRowIdFromIntent() method because of the data member i passed through. Therefore the above code is almost irrelevant to my problem.
So if you want a working on click list listener the one above will definitely help you pass the correct id to your new activity. Sorry again for this confusion I had on my side. Thanks again for all other postings!
How to refresh an Android ListView after adding/deleting dynamic data?
Call notifyDataSetChanged() on your Adapter object once you've modified the data in that adapter.
Some additional specifics on how/when to call notifyDataSetChanged() can be viewed in this Google I/O video.
Also you can use this:
myListView.invalidateViews();
Please ignore all the invalidate(), invalidateViews(), requestLayout(), ... answers to this question.
The right thing to do (and luckily also marked as right answer) is to call notifyDataSetChanged() on your Adapter.
Troubleshooting
If calling notifyDataSetChanged() doesn't work all the layout methods won't help either. Believe me the ListView was properly updated. If you fail to find the difference you need to check where the data in your adapter comes from.
If this is just a collection you're keeping in memory check that you actually deleted from or added the item(s) to the collection before calling the notifyDataSetChanged().
If you're working with a database or service backend you'll have to call the method to retrieve the information again (or manipulate the in memory data) before calling the notifyDataSetChanged().
The thing is this notifyDataSetChanged only works if the dataset has changed. So that is the place to look if you don't find changes coming through. Debug if needed.
ArrayAdapter vs BaseAdapter
I did find that working with an adapter that lets you manage the collection, like a BaseAdapter works better. Some adapters like the ArrayAdapter already manage their own collection making it harder to get to the proper collection for updates. It's really just an needless extra layer of difficulty in most cases.
UI Thread
It is true that this has to be called from the UI thread. Other answers have examples on how to achieve this. However this is only required if you're working on this information from outside the UI thread. That is from a service or a non UI thread. In simple cases you'll be updating your data from a button click or another activity/fragment. So still within the UI thread. No need to always pop that runOnUiTrhead in.
Quick Example Project
Can be found at https://github.com/hanscappelle/so-2250770.git. Just clone and open the project in Android Studio (gradle). This project has a MainAcitivity building a ListView with all random data. This list can be refreshed using the action menu.
The adapter implementation I created for this example ModelObject exposes the data collection
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 from the 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();
}
}
More Information
Another nice post about the power of listViews is found here: http://www.vogella.com/articles/AndroidListView/article.html
Call runnable whenever you want:
runOnUiThread(run);
OnCreate(), you set your runnable thread:
run = new Runnable() {
public void run() {
//reload content
arraylist.clear();
arraylist.addAll(db.readAll());
adapter.notifyDataSetChanged();
listview.invalidateViews();
listview.refreshDrawableState();
}
};
i got some problems with dynamic refresh of my listview.
Call notifyDataSetChanged() on your Adapter.
Some additional specifics on how/when to call notifyDataSetChanged() can be viewed in this Google I/O video.
notifyDataSetChanged() did not work properly in my case[ I called the notifyDataSetChanged from another class]. Just in the case i edited the ListView in the running Activity (Thread). That video thanks to Christopher gave the final hint.
In my second class i used
Runnable run = new Runnable(){
public void run(){
contactsActivity.update();
}
};
contactsActivity.runOnUiThread(run);
to acces the update() from my Activity. This update includes
myAdapter.notifyDataSetChanged();
to tell the Adapter to refresh the view.
Worked fine as far as I can say.
If you are using SimpleCursorAdapter try calling requery() on the Cursor object.
if you are not still satisfied with ListView Refreshment, you can look at this snippet,this is for loading the listView from DB, Actually what you have to do is simply reload the ListView,after you perform any CRUD Operation
Its not a best way to code, but it will refresh the ListView as you wish..
It works for Me....if u find better solution,please 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();
The solutions proposed by people in this post works or not mainly depending on the Android version of your device. For Example to use the AddAll method you have to put android:minSdkVersion="10" in your android device.
To solve this questions for all devices I have created my on own method in my adapter and use inside the add and remove method inherits from ArrayAdapter that update you data without problems.
My Code: Using my own data class RaceResult, you use your own data model.
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!!!
For me after changing information in sql database nothing could refresh list view( to be specific expandable list view) so if notifyDataSetChanged() doesn't help, you can try to clear your list first and add it again after that call notifyDataSetChanged(). For example
private List<List<SomeNewArray>> arrayList;
List<SomeNewArray> array1= getArrayList(...);
List<SomeNewArray> array2= getArrayList(...);
arrayList.clear();
arrayList.add(array1);
arrayList.add(array2);
notifyDataSetChanged();
Hope it makes sense for you.
If you want to maintain your scroll position when you refresh, and you can do this:
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();
}
For the detail information, please see Android ListView: Maintain your scroll position when you refresh.
Just use myArrayList.remove(position); inside a listener:
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override public void onItemClick(AdapterView<?> parent, android.view.View view, int position, long id) {
myArrayList.remove(position);
myArrayAdapter.notifyDataSetChanged();
}
});
You need to use a single object of that list whoose data you are inflating on ListView. If reference is change then notifyDataSetChanged() does't work .Whenever You are deleting elements from list view also delete them from the list you are using whether it is a ArrayList<> or Something else then Call
notifyDataSetChanged() on object of Your adapter class.
So here see how i managed it in my adapter see below
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();
}
}
Consider you have passed a list to your adapter.
Use:
list.getAdapter().notifyDataSetChanged()
to update your list.
After deleting data from list view, you have to call refreshDrawableState().
Here is the example:
final DatabaseHelper db = new DatabaseHelper (ActivityName.this);
db.open();
db.deleteContact(arg3);
mListView.refreshDrawableState();
db.close();
and deleteContact method in DatabaseHelper class will be somewhat looks like
public boolean deleteContact(long rowId) {
return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;
}
I was not able to get notifyDataSetChanged() to work on updating my SimpleAdapter, so instead I tried first removing all views that were attached to the parent layout using removeAllViews(), then adding the ListView, and that worked, allowing me to update the UI:
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);
while using SimpleCursorAdapter can call changeCursor(newCursor) on the adapter.
I was the same when, in a fragment, I wanted to populate a ListView (in a single TextView) with the mac address of BLE devices scanned over some time.
What I did was this:
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();
}
}
Then the ListView began to dynamically populate with the mac address of the devices found.
I think it depends on what you mean by refresh. Do you mean that the GUI display should be refreshed, or do you mean that the child views should be refreshed such that you can programatically call getChildAt(int) and get the view corresponding to what is in the Adapter.
If you want the GUI display refreshed, then call notifyDataSetChanged() on the adapter. The GUI will be refreshed when next redrawn.
If you want to be able to call getChildAt(int) and get a view that reflects what is what is in the adapter, then call to layoutChildren(). This will cause the child view to be reconstructed from the adapter data.
I had an ArrayList which I wanted to display in a listview. ArrayList contained elements from mysql.
I overrided onRefresh method and in that method I used tablelayout.removeAllViews(); and then repeated the process for getting data again from the database.
But before that make sure to clear your ArrayList or whatever data structre or else new data will get appended to the old one..
If you want to update the UI listview from a service, then make the adapter static in your Main activity and do this:
#Override
public void onDestroy() {
if (MainActivity.isInFront == true) {
if (MainActivity.adapter != null) {
MainActivity.adapter.notifyDataSetChanged();
}
MainActivity.listView.setAdapter(MainActivity.adapter);
}
}
If you are going by android guide lines and you are using the ContentProviders to get data from Database and you are displaying it in the ListView using the CursorLoader and CursorAdapters ,then you all changes to the related data will automatically be reflected in the ListView.
Your getContext().getContentResolver().notifyChange(uri, null); on the cursor in the ContentProvider will be enough to reflect the changes .No need for the extra work around.
But when you are not using these all then you need to tell the adapter when the dataset is changing. Also you need to re-populate / reload your dataset (say list) and then you need to call notifyDataSetChanged() on the adapter.
notifyDataSetChanged()wont work if there is no the changes in the datset.
Here is the comment above the method in docs-
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
I was only able to get notifyDataSetChanged only by getting new adapter data, then resetting the adapter for the list view, then making the call like so:
expandableAdapter = baseFragmentParent.setupEXLVAdapter();
baseFragmentParent.setAdapter(expandableAdapter);
expandableAdapter.notifyDataSetChanged();
on other option is onWindowFocusChanged method, but sure its sensitive and needs some extra coding for whom is interested
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
}
If I talked about my scenario here, non of above answers will not worked because I had activity that show list of db values along with a delete button and when a delete button is pressed, I wanted to delete that item from the list.
The cool thing was, I did not used recycler view but a simple list view and that list view initialized in the adapter class. So, calling the notifyDataSetChanged() will not do anything inside the adapter class and even in the activity class where adapter object is initialized because delete method was in the adapter class.
So, the solution was to remove the object from the adapter in the adapter class getView method(to only delete that specific object but if you want to delete all, call clear()).
To you to get some idea, what was my code look like,
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: here remove() and getItem() methods are inherit from the Adapter class.
remove() - to remove the specific item that is clicked
getItem(position) - is to get the item(here, thats my Word object
that I have added to the list) from the clicked position.
This is how I set the adapter to the listview in the activity class,
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);
After adding/deleting dynamic data in your "dataArray" do:
if you use an ArrayAdapter
adapter.notifyDataSetChanged();
if you use a customAdapter that extends ArrayAdapter
adapter.clear();
adapter.addAll(dataArray);
adapter.notifyDataSetChanged();
if you use a customAdapter that extends BaseAdapter
adapter.clear();
adapter.getData().addAll(dataArray);
adapter.getData().notifyDataSetChanged();
The easiest is to just make a new Adaper and drop the old one:
myListView.setAdapter(new MyListAdapter(...));