Sackurise
Sackurise

Reputation: 2814

Android - Two Vertical RecyclerView inside Vertical Scroll view, I need to perform load more for each recyclerview

I need two vertical RecyclerView inside a ScrollView. I need to add a load more footer for each list in the RecyclerView. How this can be achieved in android?

Upvotes: 3

Views: 2790

Answers (2)

Gustavo Pagani
Gustavo Pagani

Reputation: 6988

Have a look at this library, you can build something like the image below:

enter image description here

You can find the full code of that screen here.

Then when the user clicks on "See more" (or "Load more" in you case), you just load more items, add them to the respective Section and notify the adapter. Something like:

worldSection.addItem("Another world news item here");
sectionAdapter.notifyDataSetChanged();

Upvotes: 1

Reaz Murshed
Reaz Murshed

Reputation: 24251

I think this is what you're looking for. Showing multiple lists in a single RecyclerView. I'm putting the description from wiki to describe how this can be achieved easily.

This is a simple mockup to make it clear about what we're trying to achieve.

List Header (First List)

  • Item 1 of first list
  • Item 2 of first list
  • Item 3 of first list

List Header (Second List)

  • Item 1 of second list
  • Item 2 of second list
  • Item 3 of second list
  • Item 4 of second list

Footer View

Let us take two lists named firstList and secondList. Both are a list of some objects. You need to declare them globally and initialize them globally. So that you don't have to pass any specific list to your Adapter.

Defining your views

So before initializing your Adapter you need to declare some constants so that you can differentiate different types of views you're going to populate in your list. For example, in my case, I have five different views and I've assigned an unique value to each of these items so that I can explicitly tell which view to be shown when getItemViewType is called on Adapter.

private static final int FOOTER_VIEW = 1;
private static final int FIRST_LIST_ITEM_VIEW = 2;
private static final int FIRST_LIST_HEADER_VIEW = 3;
private static final int SECOND_LIST_ITEM_VIEW = 4;
private static final int SECOND_LIST_HEADER_VIEW = 5;

Define ViewHolder class and bind each item properly

Now lets customize your ViewHolder. We'll have the instances of the items which will be shown in each list item from here. We're maintaining two different lists here and let us assume each of these lists has a title and a description as the list item.

So we need to get the instances of those and should bind them with appropriate values. I'm adding an example from my code and you'll see two different function here, bindViewFirstList() and bindViewSecondList to bind the UI fields with appropriate values. There's another function which is bindViewFooter() to customize the footer view of our second list. You might declare some other functions like bindViewHeaderFirstList() and bindViewHeaderSecondList to customize and set values of your headers from here. Here's my code sample of ViewHolder.

public class ViewHolder extends RecyclerView.ViewHolder {
    // List items of first list
    private TextView mTextDescription1;
    private TextView mListItemTitle1;

    // List items of second list
    private TextView mTextDescription2;
    private TextView mListItemTitle2;

    // Element of footer view
    private TextView footerTextView;

    public ViewHolder(final View itemView) {
        super(itemView);

        // Get the view of the elements of first list
        mTextDescription1 = (TextView) itemView.findViewById(R.id.description1);
        mListItemTitle1 = (TextView) itemView.findViewById(R.id.title1);

        // Get the view of the elements of second list
        mTextDescription2 = (TextView) itemView.findViewById(R.id.description2);
        mListItemTitle2 = (TextView) itemView.findViewById(R.id.title2);

        // Get the view of the footer elements
        footerTextView = (TextView) itemView.findViewById(R.id.footer);
    }

    public void bindViewSecondList(int pos) {

        // Have a close look here. We're calculating the appropriate position of the item in the secondList
        // Second list will be populated after the firstList
        // So if the firstList doesn't have any item the secondList will have a header and its items.
        if (firstList == null) pos = pos - 1;
        else {
            // If the firstList is not empty, it'll have its header and its items. See the else part.
            if (firstList.size() == 0) pos = pos - 1;
            else pos = pos - firstList.size() - 2;
        }

        final String description = secondList.get(pos).getDescription();
        final String title = secondList.get(pos).getTitle();

        mTextDescription2.setText(description);
        mListItemTitle2.setText(title);
    }

    public void bindViewFirstList(int pos) {

        // Have a close look here. We're calculating the appropriate position of the item in the firstList
        // Decrease pos by 1 as there is a header view now.
        pos = pos - 1;

        final String description = firstList.get(pos).getDescription();
        final String title = firstList.get(pos).getTitle();

        mTextDescription1.setText(description);
        mListItemTitle1.setText(title);
    }

    public void bindViewFooter(int pos) {
        footerTextView.setText("This is footer");
    }
}

Get your custom ViewHolder for each type of view

Now declare custom ViewHolder for each of your element so that you can differentiate them when onBindViewHolder gets called on Adapter.

public class FooterViewHolder extends ViewHolder {
    public FooterViewHolder(View itemView) {
        super(itemView);
    }
}

private class FirstListHeaderViewHolder extends ViewHolder {
    public FirstListHeaderViewHolder(View itemView) {
        super(itemView);
    }
}

private class FirstListItemViewHolder extends ViewHolder {
    public FirstListItemViewHolder(View itemView) {
        super(itemView);
    }
}

private class SecondListHeaderViewHolder extends ViewHolder {
    public SecondListHeaderViewHolder(View itemView) {
        super(itemView);
    }
}

private class SecondListItemViewHolder extends ViewHolder {
    public SecondListItemViewHolder(View itemView) {
        super(itemView);
    }
}

Get proper layout files for each type of list items

In your onCreateViewHolder you need to return the layout associated with the specific list item. Here's mine.

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View v;

    if (viewType == FOOTER_VIEW) {
        v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_footer, parent, false);
        FooterViewHolder vh = new FooterViewHolder(v);
        return vh;

    } else if (viewType == FIRST_LIST_ITEM_VIEW) {
        v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_first_list, parent, false);
        FirstListItemViewHolder vh = new FirstListItemViewHolder(v);
        return vh;

    } else if (viewType == FIRST_LIST_HEADER_VIEW) {
        v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_first_list_header, parent, false);
        FirstListHeaderViewHolder vh = new FirstListHeaderViewHolder(v);
        return vh;

    } else if (viewType == SECOND_LIST_HEADER_VIEW) {
        v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_second_list_header, parent, false);
        SecondListHeaderViewHolder vh = new SecondListHeaderViewHolder(v);
        return vh;

    } else {
        // SECOND_LIST_ITEM_VIEW
        v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_second_list, parent, false);
        SecondListItemViewHolder vh = new SecondListItemViewHolder(v);
        return vh;
    }
}

Populate each type of views with proper elements from onBindViewHolder

Check if the ViewHolder passed to onBindViewHolder is an instance of an item you want to show in that specific position.

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    try {
        if (holder instanceof SecondListItemViewHolder) {
            SecondListItemViewHolder vh = (SecondListItemViewHolder) holder;
            vh.bindViewSecondList(position);

        } else if (holder instanceof FirstListHeaderViewHolder) {
            FirstListHeaderViewHolder vh = (FirstListHeaderViewHolder) holder;

        } else if (holder instanceof FirstListItemViewHolder) {
            FirstListItemViewHolder vh = (FirstListItemViewHolder) holder;
            vh.bindViewFirstList(position);

        } else if (holder instanceof SecondListHeaderViewHolder) {
            SecondListHeaderViewHolder vh = (SecondListHeaderViewHolder) holder;

        } else if (holder instanceof FooterViewHolder) {
            FooterViewHolder vh = (FooterViewHolder) holder;
            vh.bindViewFooter(position);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Get the item count of the RecyclerView

Here's a tricky part of this Adapter. You might have three cases here. Either your first list can be empty or the second one. The third case is, both lists are populated with some data.

So, we need to consider each of these three cases here. In my example, I've a header in my first list. In case of my second list I want to show a header along with a footer. So my getItemCount looks like this.

@Override
public int getItemCount() {

    int firstListSize = 0;
    int secondListSize = 0;

    if (secondList == null && firstList == null) return 0;

    // Get the sizes of the lists 
    if (secondList != null)
        secondListSize = secondList.size();
    if (firstList != null)
        firstListSize = firstList.size();

    // Case 1: When both lists have data
    if (secondListSize > 0 && firstListSize > 0)
        return 1 + firstListSize + 1 + secondListSize + 1;   // first list header, first list size, second list header , second list size, footer
    // Case 2: When only the second list have data
    else if (secondListSize > 0 && firstListSize == 0)
        return 1 + secondListSize + 1;                       // second list header, second list size, footer
    // Case 3: When only the first list have data
    else if (secondListSize == 0 && firstListSize > 0)
        return 1 + firstListSize;                            // first list header , first list size
    else return 0;
}

Return proper item view from getItemViewType

Here's the trickiest part of this Adapter I guess. You need to calculate which item view to be returned when a specific position of the item in your list is given.

The simplest way to get this thing done is to write the logic of returning the views of headers and footer first. When you're done with writing the logic of defining the position of your headers and footers, its easy to write for the others.

Here's how I've managed mine. See, I've defined the position for the headers and footers first which have simplified the other equations for me.

@Override
public int getItemViewType(int position) {

    int firstListSize = 0;
    int secondListSize = 0;

    if (secondList == null && firstList == null)
        return super.getItemViewType(position);

    if (secondList != null)
        secondListSize = secondList.size();
    if (firstList != null)
        firstListSize = firstList.size();

    if (secondListSize > 0 && firstListSize > 0) {
        if (position == 0) return FIRST_LIST_HEADER_VIEW;
        else if (position == firstListSize + 1)
            return SECOND_LIST_HEADER_VIEW;
        else if (position == secondListSize + 1 + firstListSize + 1)
            return FOOTER_VIEW;
        else if (position > firstListSize + 1)
            return SECOND_LIST_ITEM_VIEW;
        else return FIRST_LIST_ITEM_VIEW;

    } else if (secondListSize > 0 && firstListSize == 0) {
        if (position == 0) return SECOND_LIST_HEADER_VIEW;
        else if (position == secondListSize + 1) return FOOTER_VIEW;
        else return SECOND_LIST_ITEM_VIEW;

    } else if (secondListSize == 0 && firstListSize > 0) {
        if (position == 0) return FIRST_LIST_HEADER_VIEW;
        else return FIRST_LIST_ITEM_VIEW;
    }

    return super.getItemViewType(position);
}

You're done with the implementation of your custom Adapter when multiple lists are needed to be shown in a single RecyclerView. I've added a demo project in the code section. You can build and run the project.

Cheers!

Upvotes: 2

Related Questions