I would like to know what is the right way to implement a footer in RecyclerView which can be used to display different views. Few examples are as below
View for network error with a button to reload.
View when data is loading from server with a progress bar.
View when there is no more data available.
And so on.
I came across https://stackoverflow.com/a/29168617/371557 which mentions that I have to Override getItemViewType and inflate different views in onCreateViewHolder
What I do not understand is how will RecyclerView know which ViewType it has to request for? How can I force the RecyclerView to show the ViewType?
RecyclerView is the new view more powerfull than listview, you can make a footer in this but it's for me not the right way.
I recommend to create a layout include your RecyclerView and add a dedicated view like Snackbar or a custom footer.
For your question, the RecyclerView knows what to display with the getItemViewType(int positionItem) method.
For example, if the item in position 6 must be shown in the list, getItemViewType return the id of view in onCreateViewHolder(ViewGroup viewGroup, int id) method and recycle the view which could already be used in list for saving memory.
(Sorry for my poor English :)
Related
I use a RecyclerView to display a list of items. The RecyclerView shows a single item at the time. By clicking a button I would like to change ViewHolders for all the items, including the one displayed. The data stays the same, only the list item layout changes.
I supposed I need to clear the cache pool, but it did not help. There are still views in the recycler pool.
recyclerView.recycledViewPool.clear()
RecyclerView keeps using the cached views.
Moreover, how to re-create the view with a new ViewHolder of the item displayed?
add type in your model class
var viewType : ViewType
make an enum of viewType
ViewType { VIEW_ONE, VIEW_TWO }
override ItemViewType function in your RecyclerViewAdapter. Make separate Layout files for each view type and create/inflate in onCreateViewHolder of RecyclerViewAdapter.
When button is being pressed. Change the ViewType in your model class and call notifyDatasetChanged()
Is there a way to scroll to the end of recyclerview using Espresso?
There is an item with text let's say 'Text XYZ' and the recyclerview has an id recycler_view. This item happens to be the last item of the recycler view.
I tried
onView(withId(R.id.recycler_view)).check(matches(isDisplayed())).perform(RecyclerViewActions.scrollTo(withText("Text XYZ")),click());
But this doesn't seem to work. Any ideas?
RecyclerViewActions.scrollTo() matches against the ItemView of the ViewHolder, which is inflated in onCreateViewHolder() of the adapter. And in order for the scrollTo() to work, you need to provide a matcher that uniquely identifies that ItemView.
Your current matcher tells espresso to scroll to a ViewHolder that was inflated with a TextView as an itemView. Which can happen, but usually you have some ViewGroup action going on there to style the view holders in the way you want them to look.
If you change your scrollTo() Matcher, to hasDescendant(withText("Text XYZ")) to account for all nested layouts (if there more than one).
Also keep in mind since you are also trying to click the item - you can't do it in the same perform() because it will send the click to the current ViewInteraction, which is in this case a RecyclerView with id R.id.recycler_view. Doing so in the same perform just clicks the middle on the RecyclerView, not the item that you scrolled to.
To solve this you need either need another onView() with the matcher that you used to scroll to an item or use RecyclerView.actionOnItem().
In case of another onView() statement, the hasDescendant(withText("Text XYZ")) will fail you, because it will find all parents of that TextView (viewholder, recyclerview, the viewgroup that holds the recyclerview and so on) as they all have this particular descendant. This will force you to make the ItemView matcher to be more precise and account for all nested layouts. My usual go to matcher is withChild() in these situations, but it might be different for you.
If you know the last position of the RecyclerView, then you can scroll to it
static final int LAST_POSITION = 100;
// First method
onView(withId(R.id.recyclerview))
.perform(RecyclerViewActions.scrollToPosition((LAST_POSITION)));
// Second method
onView(withId(R.id.recyclerview))
.perform(actionOnItemAtPosition(LAST_POSITION, scrollTo()));
Even if this approach doesn't show the scrolling up but allows you to select the position of the recycler.
Thanks to this guy (Espresso-testing-recyclerviews I used this approach:
onView(allOf(withId(R.id.recyclerUserCustomListsView)))
.perform(RecyclerViewActions.actionOnItemAtPosition(7, click()));
I have a ListView. Each view item in that Listview has a progress bar that will be updated by some threads. As The listview reuse the views (Has no unique view), the threads update unexpected views when new item has been added and when the listview has been scrolled.
I would like to know how can I create some unique views that each thread will update only the given view in the listview?
Listview doesn't re-use the views by default like recyclerview does, because listview relies on implementers using the ViewHolder pattern to achieve the re-use. Simply don't use the viewholder pattern and each list item will receive its own view. Just be careful not to do this in a list with too many concurrent items. Viewholder became a defacto standard for a reason.
I had just to take a look at that class. Put the view that will be updated in the header list. When done, put it back to the adapter.
There are some topics about RecyclerView inside RecyclerView but I see most of them do not fit my case. My case is I have a RecyclerView (verticle linear layout management) displays a list of CardView, each Cardview contains a inner RecyclerView (horizontal linear layout management). The problem is all about performance when scrolling, it is not smooth at all. I notice if I comment the setAdapter for the Inner Recyclerview, the scrooling become smooth, but I make the CardView not updated the new list. The code is something similar to this:
onBindViewHolder...{
holder.innerRecycler.setAdapter(new InnerAdapter((data));
// comment that line make the outer recyclerview smoothly but the CardView data not updated thanks to the view recycling.
}
I know a scrollable view inside a scrollable view is not a good idea but I dont see any other choices. Anyone face to this kind of layout before?. Thanks.
UPDATE (add more code).
// init outer recyclerview
mOuterRecyclerView = (RecyclerView) findViewById(...);
mOuterRecyclerView.setLayoutManagement(new LinearLayoutManagement(this));
mOuterRecyclerView.setHasFixedSize(true);
mOuterRecyclerView.setAdapter(new OuterAdapter(dataList));
// The adapter class for the outer one
onBindViewHolder(...){
final dataItem = mItems.get(position);
holder.innerRecycler.setAdapter(new InnerAdapter(dataItem.getList()));
}
// the holder for the outer
class MyHolder extends ViewHolder{
RecyclerView innerRecycler;
public MyHolder(View view){
super(..);
innerRecycler = findViewById(...);
}
}
// the adapter for the inner
onBindViewHolder(...){
final dataItem = mItems.get(pos);
// async loading
holder.tvTitle.setText(dataItem.getTitle);
}
The layout is pretty simple so I dont post the fully code here. :)
#Paul i had the same requirement and the below trick worked.
Put the parent recyclerview inside NestedScrollView and in your onCreate method,
set.
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setNestedScrollingEnabled(false);
This prevents scroll of parent recyclerview and scroll becomes absolutely smooth.
There is no issue in such an approach as long as they scroll along different axes.You could enable RecyclerView.startNestedScroll(int) and also handle situations like overscroll.This delay would be because you are reinitiating an adpater everytime. You could try something different like maintaining a map of adpaters and calling RecyclerView.swapAdapter(args...) in bindVH.
Another good step could also be using a common pool for recycled views using RecyclerView.setRecycledViewPool(args...) .
I have created and used a list of 100+ plus items with a nested (different axis) recycler and have not faced issues .
If you would provide more code( where you have written async loading) I could help you more.But I suggest you go through the API and the design patterns and that should solve your issue.
Since there are some user ask me how to archive, I'm going to post what I do.
The sugession that #Droidekas seems a good point but I dont follow that.
What I did is:
Each horizontal recycler view is a item of a vertical recycler view
When I need to set the data for the particular vertical item (onBindViewHolder), I use the horizontal recycler adapter setNotifyDataSetChanged instead of setting a completely new adapter or swapping it. This way, I got the vertical recyler run very smoothly.
The vertical recyler adapter structure could be:
Vertical Item view holder -> Vertical Item[Name, List<Horizontal item>,...]
Vertical Item view holder -> Vertical Item[Name, List<Horizontal item>,...]
In OnBindViewHolder
holder.setData(VerticalItem)
In the Holder, I have a setData method looks like:
setData(VerticalItem item){
mItems = item.getHorizontalItems();
mHorizontalAdapter.notifyDataSetChanged();
}
Hope it helps. :D
i have create custom list view using base adapter to dynamic row
content.row content are created programmatically (check box,text view) they are include in layout.
problem to scrolling time they are very slow because not use
view holder. how can i use view holder this type of custom list view?
any solution or suggestion?
following this list..
ViewHolder is use in a list view when same view is repeated. Say there are total 6 items visible at a time in your activity. Then using viewholder pattern 6+2=8 views will be inflated at a time. one extra at the top and one extra at the bottom to give smooth scrolling effect. Now suppose scroll up operation is performed, and item at 8th position is visible, item at 0th position will be recycled and appended at the end of the list as the 9th item. if the views are not same this recycling can not be performed. check https://www.youtube.com/watch?v=wDBM6wVEO70
For your problem you can assume there is 5 maximum value possible then you can create adapter view using 10 dynamic views inside and set visibility as required.
Another optionis use LinearLayout and add each row dynamically but this won't give much optimization.