Showing a dynamic number of views in RecyclerView item?

倾然丶 夕夏残阳落幕 提交于 2019-12-12 09:06:27

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!