I'm using a custom CompoundView
which extends LinearLayout
to display items of a RecyclerView
. Each item displays an article which contains multiple paragraphs and images.
The CompoundView
adds TextView
or ImageView
dynamically based on the data attached by CompoundView.setData(List<DataPiece> pieces)
, the number of which is unknown before data is attached.
Each DataPiece
object tells CompoundView
whether it's a piece of text or an image. And here is the code for CompoundView.setData(List<DataPiece> pieces)
:
public void setData(List<DataPiece> pieces) {
removeAllViews();
for (DataPiece dataPiece : pieces) {
switch (dataPiece.getType()) {
case IMAGE:
ImageView imageView = new ImageView(getContext());
...
addView(imageView);
break;
case TEXT:
TextView textView = new TextView(getContext());
...
addView(textView);
break;
}
}
}
In the RecyclerView.Adapter.onBindViewHolder()
, the data is attached to CompoundView
by calling MyViewHolder.compoundView.setData(...)
. And it works fine when the RecyclerView
is created.
However, for a CompoundView
item with multiple ImageView
s and TextView
s, when I scroll away from it and then scroll back, the scroll becomes heavily unsmooth.
I guess it's because removeAllViews()
in setData()
is called, and the CompoundView
creation for-loop is executed again by the recycler. But I don't know how to avoid this.
And I also wonder why the scroll is always smooth when using TextView
(with Images) in a RecyclerView
even it's recycled too.
Thanks in advance!
There are multiple considerations that could go into deciding what the best approach might be.
First, do you have an idea about the maximum number of items in the recycler's list? If it is just a handful, maybe you could ditch the RecyclerView
approach and just add your CompoundView
into a container hosted by a ScrollView
.
Secondly - is the layout of each item fairly complicated (a.k.a. are there many TextViews
, ImageViews
etc. in it)? If yes, maybe you could take an approach that would resemble an ExpandableListView
- show a summary as each list item and expand to the full layout of the item on click.
Thirdly - if none of the above is acceptable and you still want to go the current approach - don't construct/add your view in the binding method. Do it in the onCreateViewHolder
, when the system expects you to construct your view (I don't know for sure but by the time you're called on onBindViewHolder
your view might have been already added to the hierarchy and any hierarchical change to it has a ripple effect on its containers - but don't take my word for it, I don't actually know the view is already added, it is just an assumption). You will have to assign each item a different type, so that in onCreateViewHolder
you could match the view type with the supporting data (for the addition of the corresponding number of child views); create the view from scratch each time - this way you don't need to call on removeAllViews
. Something like(I left out parts of the adapter that are not relevant to the case):
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
ArrayList<DataPiecesList> mItems;
public RecyclerViewAdapter(ArrayList<DataPiecesList> items) {
mItems = items;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
CompoundView compoundView = new CompoundView();
List<DataPiece> dataPieces = mItems.get(viewType);
for (int i = 0; i < dataPieces.size(); i++)
{
// construct TextView or ImageView or whatever
compoundView.add(child);
}
MyViewHolder view = new MyViewHolder(compoundView);
return view;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
CompoundView compoundView = viewHolder.itemView;
DataPiece dataPiece = mItems.get(i);
for (int j = 0; j < compoundView.getChildCount(); j++)
{
compoundView.getChildAt(j) <- dataPiece.get(j);
}
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public int getItemCount() {
return mItems.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
...
public MyViewHolder(View itemView) {
super(itemView);
}
}
}
RecyclerView
is supposed to reuse the views. It will be slow if you throw away the already created TextView
/ ImageView
objects and create new ones every time.
It sounds like you need a RecyclerView
with multiple view types. The idea is to create multiple view holders - some of them with ImageView
, the others with TextView
. You'll have to override the getItemViewType(int position) method of your adapter - it should return different values for the IMAGE items and the TEXT items. The onCreateViewHolder(ViewGroup parent, int viewType) receives a viewType parameter so you know which type of ViewHolder to create there. In the onBindViewHolder(VH holder, int position) you could assume that the holder passed to you is the correct type (i.e. the type with TextView
for TEXT items and the type with ImageView
for IMAGE items), so there is no need to remove its child views and create them again.
There is nice article about RecyclerView's Adapters with multiple view types here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With