7

I have to do something like Instagram does. I have a horizontal RecyclerView for stories, and, below, a vertical RecyclerView for feed. I want to accomplish the same scroll behavior(the stories should go with feed at scroll, not stay on top as fixed). The only solution I found is NestedScrollView but it makes extremely bad performance for RecyclerViews, my screen freezes almost forever. I tried a lot of tricks found here like nestedScrollEnabled, autoLayoutMeasure etc. but nothing worked. Thanks.

Ciobanu Razvan
  • 117
  • 1
  • 10

2 Answers2

8

Sorry if this explanation is too abstract. Let me know if you need me to be more explicit.

public class VerticalAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

private static final int TYPE_HEADER = 0;
private static final int TYPE_POST = 1;

List<Post> posts;
List<FeedItems> feedItems; //this array is going to populate the horizontal recycler view. Notice that is passed it on the adapter constructor

public VerticalAdapter(List<Post> posts,List<FeedItems> feedItems) {
    this.posts = posts;
    this.feedItems = feedItems;
}

public void notifyFeedChanged(List<FeedItems> newFeedItems){
    this.feedItems.clear();
    this.feedItems = newFeedItems; //set the new feed items in the array
    notifyItemChanged(0); //tell the main recycler view "Hey, update your first position". This will cause the onBindViewHolder to be called again an thus, the new items will be set into the horizontal recycler view
}

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

    if (viewType == TYPE_HEADER)
        return new HeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.your_header_layout, false));
    else if (viewType == TYPE_POST)
        return new PostViewHolder (LayoutInflater.from(parent.getContext()).inflate(R.layout.your_post_layout, false));

    throw new RuntimeException("Don't know this type");
}

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


    if (holder instanceof HeaderViewHolder){
        //set adapter for the horizontal recycler view
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(((HeaderViewHolder) holder).recyclerView.getContext(),LinearLayoutManager.HORIZONTAL, false)
        ((HeaderViewHolder) holder).recyclerView.setLayoutManager(linearLayoutManager);

        if (((HeaderViewHolder) holder).recyclerView.getAdapter() == null){ //only create the adapter the first time. the following times update the values
            AnotherAdaterYouNeedToCreateForTheHorizontalRecyclerView adapter = new AnotherAdaterYouNeedToCreateForTheHorizontalRecyclerView(feedItems);
            ((HeaderViewHolder) holder).recyclerView.setAdapter(adapter);
        }else {
            ((HeaderViewHolder) holder).recyclerView.getAdapter().notifyDataSetChanged();
        }

    }else if (holder instanceof PostViewHolder){
        //just do the normal post binding
    }
}

@Override
public int getItemCount() {
    return posts.size() + 1; // +1 because of the header
}

@Override
public int getItemViewType(int position) {
    return position == 0 ? TYPE_HEADER : TYPE_POST;
}

private class HeaderViewHolder extends RecyclerView.ViewHolder{

    RecyclerView recyclerView;

    public HeaderViewHolder(View itemView) {
        super(itemView);
        recyclerView = itemView.findViewById(R.id.the_recycler_view_id_on_the_heaedr_layout_file);
    }
}

private class PostViewHolder extends RecyclerView.ViewHolder{

    ImageView imageView;

    public PostViewHolder(View itemView) {
        super(itemView);
        imageView = itemView.findViewById(R.id.post_image_view_or_whatever);
    }
}}

So, your vertical recyclerview has the Post items (or whatever your post class is) drawn vertically, that's the easy thing to achieve. Now, for the horizontal view, you should implement a recyclerview header (check my Adapter example). The header layout will have another horizontal recyclerview.

Atul Bhardwaj
  • 6,647
  • 5
  • 45
  • 63
Tiago Ornelas
  • 1,109
  • 8
  • 21
  • Ok. Everything it's clear except the way i should define adapter/layout manager for horizontal recyclerview in the vertical adapter. Can you provide me a little sample with this part, please ? – Ciobanu Razvan Jul 25 '18 at 16:09
  • I edited the code on my answers. Please keep in mind that you will have to create another adapter (on my example is AnotherAdaterYouNeedToCreateForTheHorizontalRecyclerView). That adapter will have the horizontal items you want to display. – Tiago Ornelas Jul 25 '18 at 16:18
  • Ok, i got it, it's clear now. One last question. How should i notify the horizontal adapter for changes? Can i notify in a separate way feed changes and story changes ? – Ciobanu Razvan Jul 25 '18 at 16:29
  • 1
    Edited the code again. Note the feedItems array and the notifyFeedChanged method I adeded – Tiago Ornelas Jul 25 '18 at 16:36
  • 1
    And for posts notify updates i should notify the adapter for positions from 1 to posts size, right ? – Ciobanu Razvan Jul 25 '18 at 16:46
  • Yes, correct. Probably just call notifyDataSetChanged on the whole adapter – Tiago Ornelas Jul 26 '18 at 08:34
  • Calling it for the whole adapter will refresh the stories too which is not necesarry – Ciobanu Razvan Jul 26 '18 at 10:17
  • 1
    That's true. Unless you implement DiffResult (https://medium.com/@iammert/using-diffutil-in-android-recyclerview-bdca8e4fbb00) – Tiago Ornelas Jul 26 '18 at 10:30
-1

In your XML Layout try keeping both the "header" recycler view and "Post" recycler view in a nested scroll view. It works for me.

    <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF">

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/tv"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingStart="16dp"
                    android:paddingTop="8dp"
                    android:text="Welcome"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large"
                    android:textColor="#000000"
                    android:textSize="24sp"
                    android:textStyle="bold" />

                <TextView
                    android:id="@+id/date"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingStart="16dp"
                    android:paddingTop="8dp"
                    android:textAppearance="@style/TextAppearance.AppCompat.Body1"
                    android:textColor="@color/black_80" />

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/rv"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:orientation="horizontal" />
            </LinearLayout>

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rvv"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" />

        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

</androidx.constraintlayout.widget.ConstraintLayout>