问题
I am trying to re-create this:
I have a list of data:
List<Block> blocks;
And the Block.java
class has a method getNumBlocks()
which returns the number of blocks that item should show. The number of blocks can range from something as small as 1 block to something as large as 20 blocks.
I created a normal RecyclerView adapter, along with the ImageView
layout for the block:
public class BlockAdapter extends RecyclerView.Adapter<BlockAdapter.ViewHolder> {
private Context context;
private List<Block> blocks;
public BlockAdapter(Context context, List<Block> blocks) {
this.context = context;
this.blocks = blocks;
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView block;
public ViewHolder(View v) {
super(v);
block = (ImageView) v.findViewById(R.id.block);
}
}
@Override
public BlockAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.block_layout, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
Block block = blocks.get(position);
// How to use block.getNumBlocks() to show the correct number of blocks?
}
@Override
public int getItemCount() {
return blocks.size();
}
}
Here is block_layout.xml
(it's just an empty LinearLayout, the blocks should be added inside of this LinearLayout):
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/card_bg"
android:padding="16dp">
</LinearLayout>
And here is the block.xml
layout:
<ImageView
android:id="@+id/block"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp" />
So my question is, how can I implement this so that it shows the correct number of blocks for each RecyclerView item?
What is the best way to do this performance wise?
回答1:
It's good that you care about performance. Creating new ImageViews
every time you want to display a row is against the idea of recycling views. Basically when scrolling you would re-use only the top level views, but still creating new ImageViews
for any item every time it appears on screen.
To deal with that you can create your own pool of views like that:
public class BlockAdapter extends RecyclerView.Adapter<BlockAdapter.ViewHolder> {
private final List<Block> blocks;
private final List<ImageView> imageViewPool = new LinkedList<>();
public BlockAdapter(List<Block> blocks) {
this.blocks = blocks;
}
@Override
public int getItemCount() {
return blocks.size();
}
@Override
public BlockAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.block_layout, parent, false));
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.bind(blocks.get(position));
}
public class ViewHolder extends RecyclerView.ViewHolder {
private final List<ImageView> imageViews = new ArrayList<>();
private final ViewGroup container;
public ViewHolder(View v) {
super(v);
container = (ViewGroup) itemView;
}
public void bind(Block block) {
recycleImageViews();
for (int i = 0; i < block.getNumBlocks(); ++i) {
final ImageView imageView = getRecycledImageViewOrCreate();
imageViews.add(imageView);
container.addView(imageView);
}
}
private ImageView getRecycledImageViewOrCreate() {
if (imageViewPool.isEmpty()) {
return (ImageView)LayoutInflater.from(container.getContext()).inflate(R.layout.block, container, false);
}
return imageViewPool.remove(0);
}
public void recycleImageViews() {
imageViewPool.addAll(imageViews);
imageViews.clear();
container.removeAllViews();
}
}
@Override
public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
holder.recycleImageViews();
}
}
The idea behind this solution is very similar to what RecyclerView
does.
Every time ViewHolder
wants to present sub items it will:
- Take ImageView from the
imageViewPool
. If there is no spare one in the pool, a new one will be inflated. - Attach this
ImageView
to itself.
Later, when particular ViewHolder
is re-used or recycled it will:
- Detach all
ImageViews
from itself. - Return those
ImageViews
to re-usable pool so next item that will appear on the screen can take those.
回答2:
First change your viewholader and get the reference of your root layout -
public class ViewHolder extends RecyclerView.ViewHolder {
LinearLayout root;
public ViewHolder(View v) {
super(v);
root = (LinearLayout) v.findViewById(R.id.root);
}
}
After that inside bindview holder iterate over a loop with size of blocks
for(int i=0;i<block.size;i++) {
ImageView blockImg = new ImageView(context);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(left, top, right, bottom);
blockImg.setLayoutParams(lp);
holder.root.addView(blockImg);
}
Using LayoutParams you can give desired margin as well and apply it over imageview
来源:https://stackoverflow.com/questions/43101231/showing-a-dynamic-number-of-views-in-recyclerview-item