简体   繁体   中英

Recyclerview with nested items of any depth

I need to display a Recyclerview that can have any amount of subheadings. Sadly I only found solutions that support a depth of one. This isn't enough for my case.

I could have something like this:

Heading 1
  Subheading 1
    Subsubheading 1
      Subsubsubheading 1
  Subheading 2
Heading 2
...

You get the idea. Futhermore, it would also be quite useful if the user can expand these headings and their content like in MS Word. How does one achieve this behaviour (if possible without external libraries)? Thank your for you support!

For this problem one RecyclerView should be completely enough. Your Adapter items should have a tree structure containing a node depth and children. Then, based on each item's depth padding of its corresponding view could be changed.

Let's consider a simple example - there's a data class called File as below:

public class File {
    ...
    public final List<File> children = new ArrayList();
    public boolean isExpanded;
    public boolean isFolder;
    // THIS MUST BE NON-NEGATIVE
    public int depth; 
    ...
}

Then RecyclerView.Adapter (initially you should pass it a list of root files):

...
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    File file = files.get(position);

    holder.itemView.setPadding(Math.min(depth, <MAX_PADDING>) * <leftPadding>, 0, <rightPadding>, 0);

    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (file.isFolder) {
                if (!file.isExpanded) {
                    expand(holder, file);
                } else {
                    // if you wanna collapse the folder then I'll leave it up to you :)
                } 
            }
        }
    });
}

private void expand(ViewHolder holder, File file) {
    file.isExpanded = true;
    int start = holder.getAdapterPosition();
    files.addAll(start + 1, file.children);
    notifyItemRangeInserted(start + 1, file.children.size());
}
...

If you wanna implement collapse then it's a bit trickier (you'll have to calculate the correct number of items that should be removed from the list) but I'll leave it up to you.

I ended up using a combination of the approach from user3170251 and my own. Because my headers are from different models I used an interface and all models implement that interface.

The recyclerview only gets a list of objects that implement the interface. By checking the type of the current element it knows whether it is a read-only header or a normal element with special on-click functions. Now, to have some sort of hierarchy you need to store the depth in the model itself. So the data hierarchy that the recyclerview sees is still a flat list, but by saving the depth in the model itself I can give headers with a deeper depth a smaller font size.

The answer from Anshul Aggarwal on Is there an addHeaderView equivalent for RecyclerView? really helped me out.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM