I'm making my first java android app and I'm having some issues with the RecyclerView.
I have a RecyclerView with a custom layout for each element.
I want to make that when I press the toolbar button, the Image Buttons inside EACH layout element inside the RecyclerView turn visible or invisible
This is the toolbar edit button code:
ArrayList<View> dest=new ArrayList<View>();
recView= findViewById(R.id.newListView);
recView.setItemViewCacheSize(0);
recView.findViewsWithText(dest,getString(R.string.onesingleuserlayout),View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
for (View oneUserLayout:dest)
{
oneUserLayout.findViewById(R.id.btnDelete).setVisibility(newVisibility);
oneUserLayout.findViewById(R.id.btnEdit).setVisibility(newVisibility);
}
It hides almost all the elements, but not every one of them. The ones shown behave correctly, and SOMETIMES the ones not showing don't.
I think that it is related to the cached items.
Remove this line, in order to permit caching:
recView.setItemViewCacheSize(0);
Here it's explained how it works (which may also explain, why caching is required).
Try to change your approach and instead of calling all those findView methods from outside the RecyclerView, implement a method on the adapter to change a boolean variable representing the visibility of the buttons and then call notifyDataSetChanged to force the layout manager to redraw all the visible itens and since all items reference the boolean variable, when new items are redrawn they will have the new visibility.
Related
I created a RecyclerView that displays items.
Every X items, I have a native ad and beneath it a TextView with some title..
The user has an option to delete an item and then I use the following to do so:
public void removeAt(int position) {
items.remove( position );
notifyItemRemoved( position );
notifyItemRangeChanged( position, items.size() );
}
It does the job good and deletes the item and shifts the whole items one line up.
However, when I scroll over the position of the deleted item it still seems like the image of it is stuck and it shows it in the background between the lines of the RecyclerView as follows:
Any way I can make sure that the deleted item won't appear like this?
SOLUTION:
Setting the background of the title to white did the job.
The most common cause of this is the RecyclerView being inside a Fragment that's being inserted twice, often due to being unconditionally added in the Activity's onCreate when it's already there. Setting the background to white will cover up the issue, but not fix it.
Add some logging (logging when you set the adapter is a good place to do it) to check if your RecyclerView is actually getting added more times than you think.
I have list/rows of checkboxes inside recycle view. In my case, the user can only select 3 checkboxes out of many. As soon as the user selects the third checkbox, I want to disable rest of the checkboxes in different rows.
I am capturing selecting/deselecting checkboxes inside adapter class.
As far as I know, I can enable/disable checkboxes inside onBindViewHolder class.
But in my case, I want to enable/disable checkboxes, after they have been rendered.
One of the approaches I can think is, to call notifyDataSetChanged(); from Activity class and then rerender all the recycled view.
But I hope there is the better way than doing this, inside the adapter class itself.
ps: I am new to android/java.
notifyDataSetChanged() will defently work in your case.
Please note that you don't have to call this method from the Activity.
You can call it internally within your adapter after the third checkbox was selected.
From the top of my head, One way to do this would be to store the "checked" positions indide an array within the adapter, then inside onBindViewHolder() check to see if the current position should be enabled or disabled.
In your model class you can have a boolean variable that decide whether the item should be enabled or not and another boolean variable to keep track of the checked state, or if the item is checked or not. In onBindViewHolder check the variable to enable or disable the item.Keep track of the selected items and then after every check and uncheck, check if the selected items exceeds the limit. Then loop through your items and then check if the item is checked or not. if the item is not checked set the value to false and after the loop send a notifyDataSetChanged() and in onBindViewHolder() check for the value and enable or disable the item/checkbox
You can use SparseBooleanArray to record all the changes for each item based on their positions.
You can use the following method in RecyclerView adapter:
notifyItemInserted(int)
notifyItemRemoved(int)
notifyItemRangeChanged(int, int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)
Read more at RecyclerView.Adapter.
Only use notifyDataSetChanged() as the last resort. Because calling notifyDataSetChanged() will force the views to rebind and relayout. As in the documentation says:
void notifyDataSetChanged ()
Notify any registered observers that the data set has changed.
There are two different classes of data change events, item changes
and structural changes. Item changes are when a single item has its
data updated but no positional changes have occurred. Structural
changes are when items are inserted, removed or moved within the data
set.
This event does not specify what about the data set has changed,
forcing any observers to assume that all existing items and structure
may no longer be valid. LayoutManagers will be forced to fully rebind
and relayout all visible views.
RecyclerView will attempt to synthesize visible structural change
events for adapters that report that they have stable IDs when this
method is used. This can help for the purposes of animation and visual
object persistence but individual item views will still need to be
rebound and relaid out.
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.
how to update ReyclerView adapter without make OnCreateViewHolder(...) being called?
Because when i do
mRecyclerView.getAdapter().notifiItemChanged(position)
it goes inside OnCreateViewHolder(...) what i don't want to. The reason why i don't want is because I have expanded item view and onCreateViewHolder(...) will reinflate it so make collapsed. I only want to OnBindViewHolder(...) being called
Have anyone faced with it?
UPD:
Just found that OnCreateViewHolder(...) is called only one first time, in all next times it's not being called. What is the reason?
t
You could add a boolean isExpanded to your data objects in your dataset, and then in the onBindViewHolder method check whether isExpanded is true, if so expand the view programmatically. (and ofcourse don't forget to switch the boolean when expanding/collapsing the view)
This is the whole point of RecyclerView : it handle a pool of view holders, which are created once (calling OnCreateViewHolder()) then reused (or recycled, hence the view name) as much as possible via OnBindViewHolder().
OnCreateViewHolder() is used to create the views, while OnBindViewHolder (re)set the views state (like text content, selected state, ...)
It follows that any state associated with an item (like the expanded / collapsed state) should not be stored in the ViewHolder. You can for example use a private List in the adapter.
I am using a ListFragment with a custom adapter, which is used to inflate the views. The data is loaded via an AsyncTaskLoader once the fragment is created.
My problem is now that I want to scroll (not smooth scroll) to a certain position to show the last selected element, i.e. The fragment gets destroyed when I select an element and I want to show the same position in the list once the fragment is displayed again.
So creating the view and everything works fine, but I do not know where to add the getListView().setSelection(x) line to scroll to a certain position. I tried to invoke it after clearing and adding the elements to the adapter in Loader<?>.onLoadFinished() function, but that does not work, I get the following exceptoins:
java.lang.IllegalStateException: Content view not yet created
at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
at fs.pokemon.effect.PokemonFragment.updateData(PokemonFragment.java:239)
at fs.pokemon.effect.PokemonFragment$2.onLoadFinished(PokemonFragment.java:129)
...
My question is: Is there a function/callback so I get notified, when the ListView has finished updating its content? Or how else can I scroll the View to a position after the content has loaded?
Thanks to pskink comment I tried it again and noticed that the exception does not occur in setSelection() but rather in getListView() - I though my loader would reload data after the view was created, but instead it used the cached results, so onLoadFinished() was actually called, before the initialization of the fragments view was finished, thus causing an exception.
The correct way to do it is to determine the position in onLoadFinished() and then override the fragments onViewCreated() and execute getListView().setSelection(curPosition) there.
Probably also the case that the loader cleared its cache and the onLoadFinished() will be called after the onViewCreated() method has to be considered, but so far it works fine.
Set the transcript mode and stackfromBottom for the listview while your listfragment activity is created. Now every time the listview is updated it will be scrolled to bottom to the latest item.
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
getListView().setStackFromBottom(true);
}
I tried to found a method, but no results. I want a ListView like below, and when I click on an element, like "Word", it'll be like this picture :
Is it possible ?
What you described is what an ExpandableListView does.
Basically it's like a listview so you'll still have to create your own adapter, but it lets you click a row to inflate a bigger item that you can then stuff your text into.
I would recommend creating your own class that extends ArrayAdapter or BaseAdapter. Then you can make use of the getView() method that gets called every time the screen gets redrawn for the user. You can then design multiple views for each selection and choose which one to display in the getView() function. So when the user selects an item, you set a flag in your custom class, and then notifyDataSetChanged() and you're good to go!