RecyclerView inside RecyclerView not smoothly - android

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

Related

Why recyclerView's viewholder is null in Android when the views are hidden?

I know it's a little bit generic. So I have a recyclerView, and inside it there are multiple items which can expand and collapse (each of them has a webview and expand and collapse to decide if the full content needs to be shown). I need to set images for a BiImageView in the viewAdapter like this
ItemViewHolder viewHolder = (ItemViewHolder) recyclerView
.findViewHolderForLayoutPosition(position);
viewHolder.rightIndicator.setImageResources()
However it only works when every item is collapsed, when the first item expand, it will take up the screen and other items will be hidden or say below the bottom of the screen. Then only the first few items can set the image resource. For other hidden items, the viewHolder is null. It looks like recyclerView removed them from layout, I'm wondering if there is any way to make sure I can get the viewHolder? Like some callback? I tried onCreate and onBind, but when I scroll down or collapse the first item, these two methods didn't get called.
I think, I can help you with some pseudo code, but I dont remember how to use it with view model. First of all, you have to change all your views in onBindViewHolder method in depend on your model, then in your model class you must have field, for example
boolean expanded = false; // initial state
And in your onBindViewHolder:
if (modelsList.get(position).isExpanded()){
//show expanded view
} else {
//show collapsed view
}
After it, you can set onClickListener to some view in your collapsed view, to expand:
view.setOnClickListener(new OnClickListener(){
public void onClick(View v){
modelsList.get(position).setIsExpanded(!modelsList.get(position).getIsExpanded());
MyAdapter.this.notifyItemChanged(position);
}
When you will click on this view, your model will change and RecyclerView will recreate view binded to this model with new data.
Hope it will help you

Showing different Footer layouts in RecyclerView

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 :)

Proper way of showing tip on a ListView element

I have a Fragment which contains a ListView. The elements of ListView are RecyclerViews which scroll horizontally. Each element of the RecyclerView is a CardView which contains some elements such as ImageViews and TextViews. I have to display a tip on a TextView inside that CardView.
This tip has to be displayed on the TextView in the first CardView of the first RecyclerView of the ListView. I am using ShowTipsView library to show the tips. What is the correct way of showing this tip?
Should I put some code inside the adapters of the ListView or RecyclerView or could it be done from the Fragment's code itself?
The real problem that I face is, how do I get access to the specific view(TextView) inside my Fragment's code?
I found this from your given link:
ShowTipsView showtips = new ShowTipsBuilder(this)
.setTarget(btn_test)
.setTitle("A magnific button")
.setDescription("This button do nothing so good")
.setDelay(1000)
.build();
showtips.show(this);
I see that setTarget(xxx) accepts a View. So you can do this in your fragment because it has your views.

Resize ListView Element While Viewing in Android?

What I am trying to do is populate a listView with views/elements that could be a textview/imageview. Potentially, these textviews are expandable. I want the listview to resize and wrap the content if the textview is expanded.
There are several solutions out there already:
1. Make the elements of the ListView element invisible so that the view in ListView is already the size you want. In this case, create the text view with the original long string and then collapse it.
2. Resize the element in the adapter then call notifyDataSetChanged();
However I'm looking for a solution where the view in ListView automatically resizes, even when its visible. I have tried using Solution #2 and while it works for changing values inside the view such as a string, it doesn't resize the view altogether unless the view is scrolled off screen then comes back into view.
The relevant part of my code is as follows:
holder.text.setTrim(!holder.text.getTrim());
holder.text.setText();
holder.text.requestFocusFromTouch();
notifyDataSetChanged();
holder.text.invalidate();
vi.invalidate();
vi.requestLayout();
I put this inside an OnClickListener inside an adapter's getView function.
Anyone have any solutions?
PS. Has anyone tried doing a method with multiple adapters? This one doesnt seem to work too well for me because I have potentially hundreds of elements.
Edit:
So I've come up with part of a stupid solution that is absolutely ridiculous. The behavior makes no sense. Below (primaryTextView is the same text as above eg. holder.primaryTextView == holder.text)
holder.primaryTextView.setTrim(!holder.primaryTextView.getTrim());
if(!holder.primaryTextView.getTrim()){
holder.primaryTextView.setVisibility(View.GONE);
}
else{
holder.primaryTextView.setVisibility(View.VISIBLE);
}
notifyDataSetChanged();
vi.invalidate();
This allows you to expand the view while viewing it but does not allow you to shrink the view once the text is collapsed. Furthermore, View.GONE and View.VISIBLE are NOT interchangable. Finally, the textView does not even dissapear in the event that View.GONE is triggered. Perhaps this might be a clue?
it doesn't resize the view altogether unless the view is scrolled off screen then comes back into view.
It is working, but you are not refreshing the listview, when you scroll off & return, listview recreates the updated view.
You should notifyDataSetChanged(); adapter.notifyDataSetChanged();
Nevermind found the problem:
In my layout, I was using height="0dp" instead of "wrap_content"

Only relayout children and not all the tree

I'm trying to recreate ListView (and AbsListView) logic, with view recycling.
I need this but we can say it's only for understand Android logic.
Suppose my children items are the same (same layout), using fixed height RelativeLayout.
During scrolling, I'm reusing ghost children view and set properties for current item.
It's working fine, since I'm using View.offsetTopAndBottom() and invalidate() instead of requesting layout during scroll for optimization.
My problem is updating the children (RelativeLayout).
Depends of item, I want to hide or show ImageView on this item. For that, I'm just using iconImage.setVisibility( GONE ) and iconImage.setVisibility( VISIBLE ).
Since I'm blocking requestLayout, it seems to be setVisibility() does not work properly.
If I use requestLayout, all the tree will measure and layout itself, and it's not a good way for a scrolling user experience.
Is there a way for only request layout on recycle child item ?
You need to notify your listView with changed of view so call notifyDataSetChanged(); from Uithread like this :
final ArrayAdapter adapter = ((ArrayAdapter) getListAdapter());
runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
}
});

Categories

Resources