6

I am using a recyclerview for displaying and broadcasting videos of users. However, when I scroll through the recycler view, I see my first view, which has my video gets recycled hence why it's not visible to other users. Is there a way I can make sure that the first view is not recycled so I dont have to worry about my video view getting resetrecycled every single time I scroll through my list?

Here's my code : In my fragment... ...

 <android.support.v7.widget.RecyclerView
        android:id="@+id/videoList"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/myButtonContainer"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/myoldContainer">

...

and the corresponding adapter...

public class GroupAdapter extends RecyclerView.Adapter<GroupAdapter.myViewHolder> {

    private CopyOnWriteArrayList<Person> persons;
    private Context mContext;

    public GroupAdapter(@NonNull final CopyOnWriteArrayList<Person> persons , Context context) {
        this.persons = persons;
        this.mContext= context;

        for (Person person : this.persons) {
           //get names
        }
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final View layout = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_person, parent, false);
        final MyViewHolder viewHolder = new MyViewHolder(layout);
        return viewHolder;
    }
     @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {



        final Person person = person.get(position);
                final Participant participant = person.getParticipant();


                if (person.getName() != null) {
                    holder.personName.setText(person.getName());
                }

                if (person.getImage() != null) {
                    holder.personImage.setImageBitmap(person.getImage());
                } else {
                    holder.personImage.setImageResource(R.drawable.default_profile);
                }
                holder.personImage.setVisibility(View.INVISIBLE);
                holder.personImage.setVisibility(View.VISIBLE);

                final VideoView videoView;
                if (participant.isMe) {
                    videoView = participant.videoStreamer.videoView;
                } else {
                    videoView = participant.videoPlayer.videoView;
                }
                if (holder.personVideo.getChildCount() != 0) {
                        holder.personVideo.removeAllViews();

                }
                if (videoView.getParent() != null) {
                    ViewGroup parent = (ViewGroup) videoView.getParent();
                        parent.removeView(videoView);

                }
                holder.personVideo.addView(videoView, myViewHolder.videoLayoutParams);

                if (person.isVideoPaused()) {
                    holder.personVideo.setVisibility(View.INVISIBLE);
                    holder.personImage.setVisibility(View.VISIBLE);
                } else {

                    holder.personVideo.setVisibility(View.VISIBLE);
                    holder.personImage.setVisibility(View.INVISIBLE);
                }


    }
@Override
    public int getItemCount() {
        return persons.size();
    }

    public static final class MyViewHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.personVideo)
        public ViewGroup personVideo;
        @BindView(R.id.personImage)
        public ImageView personImage;
        @BindView(R.id.personName)
        public TextView personName;

        protected static FrameLayout.LayoutParams videoLayoutParams = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );

        public MyViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }

    }
}

Here's how I am setting it in my fragment:

  LinearLayoutManager manager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
            videoAdapter = new VideoAdapter(myHelper.getPeople(), getContext());
            videoList.setLayoutManager(manager);
            videoList.setAdapter(videoAdapter);

item_person:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_marginTop="0dp"
    android:background="@drawable/person_border"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <RelativeLayout
        android:id="@+id/personContainer"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent">

        <FrameLayout
            android:id="@+id/personVideo"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <ImageView
            android:id="@+id/personImage"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/black"
            android:src="@drawable/default_profile" />

        <TextView
            android:id="@+id/personName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#AA555555"
            android:gravity="center_horizontal|bottom"
            android:textColor="@color/green"
            android:textSize="12sp"
            android:lines="1"
            android:ellipsize="end"
            tools:text="androiduser@gmail.com"
            android:layout_alignParentBottom="true" />

    </RelativeLayout>




</android.support.constraint.ConstraintLayout>

fragment with recycle view: xml

<android.support.constraint.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">
<RelativeLayout>
....
</RelativeLayout>
<include containers>...</include>
...
 <android.support.v7.widget.RecyclerView
        android:id="@+id/personList"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:visibility="invisible"
       >

    </android.support.v7.widget.RecyclerView>
 </android.support.constraint.ConstraintLayout>

2 Answers2

3

I don't think that "not recycling" the video view will actually stop it from being destroyed when you scroll. It's not the recycling is the problem but the view unbinding.

I think such complex component such as VideoView should not be inside the RecyclerView at all.. You can try adding it as a static content on top, which most likely will solve the issue. You can use a NestedScrollView for that. Take a look here: Recyclerview inside scrollview- How to scroll whole content?

If you still think you want to keep it in the RecyclerView and disable the recycling, do the following.

Create a separate view type for your video view items. Here is an example: https://stackoverflow.com/a/26573338/3086818. Treat your video view item as a header view from the example.

Once you do this, there is a RecycledViewPool class which manages the recycling of items inside a RecyclerView. You can tell it which views to recycle and which not. By default it recycles all views. To disable recycling for your video view items use your new view type like this:

recyclerView.getRecycledViewPool().setMaxRecycledViews(TYPE_VIDEO_VIEW, 0);

where TYPE_VIDEO_VIEW is the new type that you created using the previous example. The number 0 tells the RecyclerView how many items to recycle - in this case it's 0, meaning "do not recycle". More info about this here: https://stackoverflow.com/a/36313437/3086818.

I hope this helps.. Good luck!

Gennadii Saprykin
  • 4,505
  • 8
  • 31
  • 41
  • Thanks for your response @Gennadii However, the code I have isn't very sraightforward to change in either of those ways. I have posted the entire code snippets now. I am actually inflating layout "item_person" , from my adapter in createviewholder and my recyclerview is a part of a view in the xml fragment which populates each of these "item_persons" individually as people join the room. I tried the nested scroll view but it was difficult to get it to work in my case and same for the other part which was a little complex.Can you actually explain with respect to me code,will be really helpful –  May 16 '18 at 10:23
  • Also, the only issue I am facing with the second approach is setting the viewtype in oncreateviewholder. Not sure if I need to inflate 2 different layouts? or use the same one. pretty confused on that part –  May 16 '18 at 10:32
  • I'm kind of curios, what went wrong with the first approach? In the second approach you may inflate 2 different layouts if you want to, but it's not an obligation. The important part is making sure the video item matches type 1 and other items match type 2. Then you can disable the recycling for type 1, which will turn it off for video items only. – Gennadii Saprykin May 21 '18 at 15:58
  • Maybe I did something wrong but adding recyclerview in NestedScroll view caused crashes and issues. As you can see from my code, it wasn't straightforward,unless I miscontrued?Also,on second thought is it possible to just not recycle the first item at position 0 since it's the only one that broadcasts/streams video across, rest of them just display the video.When I tried setrecyclable(false) for position 0 in bindviewholder it didn't work as expected and still recycled when the view was not visible. Can you refer to my code and suggest something that can be changed easily to fix the issue? –  May 21 '18 at 16:36
  • Just to clarify, I did *not* think disabling recycling would fix the issue in the first place, that's why I suggested `NestedScrollView`. Recycling = reusing the view when you scroll. Even if you disable it, the view will still be destroyed when it's not visible because this is how `RecyclerView` works. On the other hand `NestedScrollView` simply scrolls its content, it's not going to destroy anything and that's why it should fix the issue. So I think you should still go with `NestedScrollView` and investigate the reason of the crash instead. – Gennadii Saprykin May 22 '18 at 08:54
  • It was designed specifically for the usecase like yours - putting a `RecyclerView` and other content inside one scrollable area which is exactly what you have here. Try to figure out why it's crashing and you will solve the problem. If you can't figure it out on your own, edit the question, add a stacktrace and we will try to help you out.. – Gennadii Saprykin May 22 '18 at 08:56
  • Thanks @Gennadii. Just to clarify I need to put the nested scrollview inside my fragment layout? in the constraintlayout? or make it a parent containing constraintlayout? I initially replaced constraintlayout and made nestedscrollview the root. is that right? –  May 22 '18 at 11:07
  • Inside your fragment layout. You will have a `ConstraintLayout` which will have all content and a `NestedScrollView` with everything that should scroll. In the `NestedScrollView` you will have your broadcast video and a `RecyclerView`. – Gennadii Saprykin May 22 '18 at 11:42
  • I haven't tried it yet, but it might be the solution I am looking for, will cross check with you if I come across any issues. However, thanks for the detailed answer and since my bounty is expiring soon, I think you deserve the +50 for your answer :) –  May 24 '18 at 10:56
  • once everything works as expected I'll mark it as the correct answer –  May 24 '18 at 10:56
  • Thank you! Good luck with your solution and let me know how it goes :) – Gennadii Saprykin May 24 '18 at 12:02
  • back to this issue. I need to use recyclerview for this, but just dont want to recycle the item at position 0. Isnt there a quick and easy way to do so? I have a video in position 0 which is expected to stream non stop even when the item is not visible. any idea how to go about it with recycle view but with a simpler approach? –  Jun 26 '18 at 01:05
  • I already replied to this :) Even if you don't recycle the item at position 0, this is NOT going to solve the issue because the RecyclerView always destroys items that are no longer visible, this is how it works. It is not expected that some items can live in memory forever. To make the view stream non stop you need to keep it **outside** the RecyclerView and make it scroll together with it, so that it "looks" like it's part of it. – Gennadii Saprykin Jun 26 '18 at 07:36
  • It might be a little complex to do that way, based on how everythign is wired up. I have posted my entire code here. Can you give me an idea about how to go about that in the best possible way? –  Jun 26 '18 at 14:11
  • The best possible way I have already described, I don't see what is actually "wired up" here. The code looks very simple. Just move the FrameLayout with video out of RecyclerView and make them scroll together. This link might help: https://stackoverflow.com/a/36540701/3086818. The code will actually become much simpler because you won't need to keep the video in your ViewHolder anymore. – Gennadii Saprykin Jun 27 '18 at 06:54
0

The answer is yes, you can actually do that. Since you said "The first item" so you simply add a check.

if(position == 0)
{
holder.setIsRecyclable(false); //This line prevents the row from recycling
}
Abdul Ahad
  • 435
  • 4
  • 14