问题
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
ImageViewto itself.
Later, when particular ViewHolder is re-used or recycled it will:
- Detach all
ImageViewsfrom itself. - Return those
ImageViewsto 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