It is common knowledge to call notifyDataSetChanged() or notifyItemChanged() when you mutate an array that is being handled by a RecyclerView adapter in order to update the view and reflect the changes made. But I spun up a sample application in which neither are called when I add items to an array immediately after calling .setAdapter() in the same block, and the view updates! This behavior doesn't happen when I try to add items via a button's onClickListener or from a background thread (both require notifyDataSetChanged for the view to update).
What's going on here? Shouldn't adding items to the list immediately after calling .setAdapter() in the same block require something like notifyDataSetChanged()?
i.e.
#Override
protected void onCreate(Bundle savedInstanceState) {
...
myList.add("a");
myList.add("b");
recyclerView.setAdapter(adapter);
myList.add("c"); // the recyclerView is updating and shows "c" here! why???
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
myList.add("e"); // the recyclerView will NOT update here unless you call `notifyDataSetChanged`
}
});
...
If you're passing an ArrayList to your Adapter, and you're adding elements in onCreate, that would be before the app has drawn your first frame.
There is a moment where you're able to add elements, because the adapter hasn't laid out the items.
And within your onClick, that would be after onCreate has finished, which means the adapter is already laid out.
You could try moving add("c"); to onPostCreate or onResume and you may see similar result as your onClick.
Related
I have two fragments (a and b) inside an Activity according to the picture below.
I am able to delete from first one but how to add that item to favorites fragment RecyclerView?
Deleting actress name and adding to favorites
My Viewholder code for RecyclerView Fragment one class:
addToFavoriteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mArrayList.remove(getAdapterPosition());
notifyDataSetChanged();
}
});
How to add this deleted item inside adapter of Favorites Recyclerview?
What I would do is maintain a list of actresses (either locally or on the server) with each one containing an isFavorite boolean attribute.
Then, while you have one global list, each recyclerview is only showing a subset:
On the left, you show all actresses where isFavorite is set to false.
On the right, you show all actresses where isFavorite is set to true.
How you update it could be done a few different ways, but here is what I recommend at a high level:
Have an onClick listener for each one that bubbles up to the activity, so the activity is aware any time an actresses's favorite state changes. Every time the state changes for an actress, tell your adapters in each fragment to update.
If you don't want to refresh the entire list every time, you could integrate a remove and add method like Mauker's Answer.
You can create a method inside your adapter that removes an item from the RecyclerView, and returns the given item.
Then, you can use this item reference to add it to the second RecyclerView.
Pseudocode example
public myItem removeAndGetItem(int position) {
myItem item = mArrayList.get(position);
mArrayList.remove(position);
notifyDataSetChanged();
return item;
}
Then you could call something like (also, pseudocode):
myItem item = adapter1.removeAndGetItem(position);
adapter2.add(item);
Adjust the examples to your code, and it should do the trick.
Edit
I misread the part about the RecyclerViews being on different Fragments.
So, you can still do what I said on the example above, you'll just have to pass that item to the second Fragment, using Fragment callbacks, or Broadcasting the item, or even through an EventBus.
Instead of using notifyDataSetChanged() which can be very costly, try to use notifyItemRemoved(int position) instead. As you can see on the docs:
If you are writing an adapter it will always be more efficient to use the more specific change events if you can. Rely on notifyDataSetChanged() as a last resort.
In Android Studio I have selected a new project based on master / detail flow. The project works as it should be. Now I want to extend it. As of now the content of the items in the detail fragment are only shown when I click an item on the list. I want that the top item is selected automaticaly when the app starts. I thought I put in ItemListFragment just a method call
#Override
public void onStart()
{
mCallbacks.onItemSelected(DummyContent.ITEMS.get(0).id);
}
that a click is simulated in the lifecycle once all objects are initialized. That fails. What is the best way of doing this?
You can select the first element by the following code
yourListView.setSelection(0);
yourListView.getSelectedView().setSelected(true);
Hope this helps. Cheers mate! :)
You can use SharedPreferences. You can save first item of listview as preference when create listview, then you can use it later.
Have you try it in onResume()? I always put it in onResume()
UPDATE #1
You can try my way to implement the selection. Normally i don't use the select of list view.
1.In your data model, create a boolean field called "selection".
2.When you apply data to your adapter, set the first data "selection" to true.
3.In your adapter getView callback, try to handles your data "selection" such as:
if (!dummyData.selection){
do something when not select....
}else{
do something when selected...
}
UPDATE #2
put this code in ItemListFragment
#Override
public void onResume() {
super.onResume();
mCallbacks.onItemSelected(DummyContent.ITEMS.get(0).id);
}
I am trying to synchronize ListViews scroll positions in a ViewPager.
I have a ViewPager with Fragments as children. Each fragment contains a ListView. I created a ListViewScrollListener interface, with which I listen to each ListView scroll activity. When one ListView is scrolled by the user, the active fragments in the ViewPager listen to the scroll event and call the following method on their ListView:
listView.setSelectionFromTop(firstVisibleItem, pixelOffset);
Sadly, the scroll positions of the ListView elements in the Fragments do not update. When I put the same setSelectionFromTop method in the onResume() method, it works like a charm, hence the position is set correctly when I swype the ViewPager and the Fragments are being created.
I also checked out StackOverflow for similar problems and tried the following solution, without positive results:
mListView.post(new Runnable() {
#Override
public void run() {
listView.setSelectionFromTop(firstVisibleItem, pixelOffset);
}
});
I checked if the setSelectionFromTop is called correctly, and apparently it is. Any ideas why it's not working?
Update 1
As hinted by Ne0, I also checked to run the setSelectionFromTop method inside the Fragment on the main ui thread, without any positive results.
getActivity().runOnUiThread( new Runnable() {
#Override
public void run() {
listView.setSelectionFromTop(firstVisibleItem, pixelOffset);
}
});
Any other ideas what this could be?
To make my question more understandable let me start with an image of my view.
I have an xml file named Menu, that has customized list view in it. I have created another xmlview named MenuCell as below.
Now tapping on add button I'm adding Item to the cart. which is working perfectly fine except not updating value of a cart (top right corner) on click event. But If I navigate to different view and come back to this view at this point I'm getting number of items added in the cart reflected properly. So How Can I reload my controllerview when I tap in my arradepter view's ImageButton.
this is my adapter code
holder.imageButton1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
addItem(position);
notifyDataSetChanged();
}
});
void addItem(int position) {
count++;
}
Where count is item added count.
If anyone can tell How am I able to reflect this count of my arrayadpter class to my other controller class that holds actual list view.
Any Help will be appreciated
Thanks in advance.
You need to use callback feature to get notified to your activity , hence add button is part of list component so you can update your list view.
Here are few links for understanding of call back using interface
http://cleancodedevelopment-qualityseal.blogspot.in/2012/10/understanding-callbacks-with-java.html
http://stackoverflow.com/questions/11577695/what-is-a-call-back-interface-in-java
After :
notifyDataSetChanged();
just add:
YourList.invalidateViews();
YourList.scrollBy(0, 0);
Send the reference of your controller to your list adapter class. In the controller I guess you have a method that computes some data and after that call update the view that holds cart data summary. Just make sure that update is on UI thread.
Your List is not refreshing at the instant because you have to refresh data of your adapter and then call notifyDataSetChanged();
Suppose You have the data in an array which is visible in textview.Then In your function addItem add the data into ur array and then call notifyDataSetChanged();
This will immediately tell the list view that watever data it is containing is changed so time to refresh.
If you can paste more of ur adapter code i can be helpful
I really can't make this up so I'd be thankful for any hint. I must make some mistake here (4.1.2).
I have an Activity which, in onCreate(), sets up a subclassed ArrayAdapter for ListView items which render as a Checkable ViewGroup.
The Activity already utilizes a NonConfiguration mechanism to re-build the Adapter upon orientation change. However, it's currently not storing the ListView's getCheckedItemPosition() because I feel it shouldn't be necessary (details below).
Interestingly, what I'm observing is the following.
The Activity is rendered.
The user checks a ListView item.
The ListView item is displayed in a checked state.
The onItemClickListener calls getCheckedItemPosition() for the ListView and gets a correct result.
The user changes the screen orientation.
onCreate() re-builds the 'ListView' and it displays just like before (after onCreate(); see below).
onCreate() calls getCheckedItemPosition() and gets -1 despite the ListView showing the correcly checked item
Upon further examination, the following details emerge.
onCreate():
get ListView resource
build MyAdapter
set MyAdapter as adapter for ListView
getCheckedItemPosition() returns -1
after onCreate():
MyAdapter.getView() is being called
CheckableViewGroup.setChecked() is called with the correct checked value
the last two steps will be repeated for all items
As said before, I'm relying on the Android feature that View objects save their state if they have an ID assigned. I'd say this is the case since there must be some object out there which sets the correct checked status for all the list entries. (By the way, CheckableViewGroup also overrides on{Save,Restore}InstanceState() but that won't be called regardless whether or not it has an ID assigned (presumably because it never gets attached to the layout root?).
So it looks as if the ListView at the same time knows and does not know its getCheckedItemPosition()? Or am I on the wrong track?
public final class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
itemList = (ListView)findViewById(R.id.ma_list);
listAdapter = new MyAdapter();
itemList.setAdapter(listAdapter);
itemList.setOnItemClickListener(
new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> av, View v, int pos, long id) { checkForm(); }
}
);
listAdapterSetup();
// this is where itemList.getCheckedItemPosition() returns -1
// when the listView has been re-built from NonConfiguration data:
checkForm();
}
}
I was making another test after I posted my question, because I was getting an idea while describing my observations.
And indeed, my suspicion was confirmed.
Timing is key here.
The ListView will report the correct getCheckedItemPosition() value in onResume() (but not before).
So the solution is easy: Perform any evaluation logic in onResume().