Play Video in RecyclerView - Play/Pause video when scrolled off screen

24,475

Solution 1

By overridding onViewAttachedToWindow(VH holder) and onViewDetachedFromWindow(VH holder) in your adapter, you can get notified when each item enters or exits from the visible area of RecyclerView. So it is possible to save the item state. For example you can create a HashMap<Integer, Long> in adapter class which holds the last time of playing. In this way, we should pause the video and store the video playing time in the HashMap with item position as key, in onViewDetachedFromWindow. Then in onViewAttachedToWindow restore it from the map and ...

Based on documentation:

onViewAttachedToWindow is called when a view created by this adapter has been attached to a window.

onViewDetachedFromWindow is called when a view created by this adapter has been detached from its window.


Visual Result:

enter image description here

Solution 2

I would approach this by adding visibilitylistener to recyclers viewholder class. Facebook fresco library uses similar technique for its "ImageView" implementation.

Fresco is open source so its simple to checkout how its done.

Share:
24,475
DJ-DOO
Author by

DJ-DOO

Updated on July 17, 2022

Comments

  • DJ-DOO
    DJ-DOO almost 2 years

    I've implemented a Recycler view of video players implemented using a texture view and media player.

    If I scroll down through the list I can click on the item and the video plays. However, with the recycler view once the view goes off screen it is then recycled for reuse. If I scroll back up all the views are now blank (black).

    I am looking to add functionality that when the user scrolls the video off screen that it will pause and keep reference to that video so that if they scroll back to that video it will play from that point.

    I have checked this out however I don't want to download the video, I just want to stream. I am not looking for someone to do this for me, I'm just looking for some pointers and hoping someone could share their knowledge on this... Thanks in advance

    This is what I have done so far:

    VIDEO PLAYER

    public class CustomVideoPlayer implements TextureView.SurfaceTextureListener, VideoControllerView.MediaPlayerControl, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnVideoSizeChangedListener {
    
        private Context mContext;
        private String mUrl;
        private MediaPlayer mMediaPlayer;
        private Surface mSurface;
        private VideoControllerView mControllerView;
    
        private TextureView mTextureView;
        private CardView mCardView;
        private ProgressBar mProgress;
        private FrameLayout mView;
        private RelativeLayout mLayout;
    
    
        public CustomVideoPlayer(Context ctx, TextureView view, ProgressBar progressDialog, FrameLayout holderView){
    
            this.mContext = ctx;
            mTextureView = view;
            mTextureView.setSurfaceTextureListener(this);
            mProgress = progressDialog;
            mControllerView = new VideoControllerView(ctx);
            mView = holderView;
    
            mTextureView.setOnTouchListener(new ControlTouchListener());
        }
    
    
        @Override
        public boolean canPause() {
            return true;
        }
    
        @Override
        public boolean canSeekBackward() {
            return true;
        }
    
        @Override
        public boolean canSeekForward() {
            return true;
        }
    
        @Override
        public int getBufferPercentage() {
            return 0;
        }
    
        @Override
        public int getCurrentPosition() {
            return mMediaPlayer.getCurrentPosition();
        }
    
        @Override
        public int getDuration() {
            return mMediaPlayer.getDuration();
        }
    
        @Override
        public boolean isPlaying() {
            return mMediaPlayer.isPlaying();
        }
    
        @Override
        public void pause() {
            mMediaPlayer.pause();
        }
    
        @Override
        public void seekTo(int i) {
            mMediaPlayer.seekTo(i);
        }
    
        @Override
        public void start() {
            mMediaPlayer.start();
        }
    
        @Override
        public boolean isFullScreen() {
            return false;
        }
    
        @Override
        public void toggleFullScreen() {
    
        }
    
        @Override
        public void onBufferingUpdate(MediaPlayer mp, int percent) {
    
        }
    
        @Override
        public void onCompletion(MediaPlayer mp) {
    
        }
    
    
    
        @Override
        public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
    
        }
    
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            mSurface = new Surface(surface);
        }
    
        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    
        }
    
        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            return false;
        }
    
        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    
        }
    
        public void changePlayState(){
            if(mMediaPlayer.isPlaying()){
                mMediaPlayer.pause();
            }else{
                mMediaPlayer.start();
            }
        }
    
        public void startVideo(String url){
            if(mMediaPlayer!=null){
                mMediaPlayer.reset();
                mMediaPlayer.release();
                mMediaPlayer = new MediaPlayer();
            }else{
                mMediaPlayer = new MediaPlayer();
            }
            if(!mMediaPlayer.isPlaying()){
                try {
                    mMediaPlayer.setSurface(mSurface);
                    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                    mMediaPlayer.setDataSource(url);
                    mMediaPlayer.prepareAsync();
                    mMediaPlayer.setOnCompletionListener(this);
                    mMediaPlayer.setOnBufferingUpdateListener(this);
                    mMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
                    mMediaPlayer.setOnPreparedListener(this);
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        @Override
        public void onPrepared(MediaPlayer mp) {
            Log.i(VersysVideoPlayer.class.getSimpleName(), "ON PREPARED CALLED");
            mControllerView.setMediaPlayer(this);
            mControllerView.setAnchorView(mView);
            mControllerView.show();
            mProgress.setVisibility(View.GONE);
            mMediaPlayer.start();
        }
    
        //Touch listener to display video controls
        class ControlTouchListener implements View.OnTouchListener{
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(event.getAction() == MotionEvent.ACTION_DOWN){
                    mControllerView.show();
                }
                return false;
            }
        }
    }
    

    ACTIVITY/ADAPTER

    public class VideoViewListActivity extends AppCompatActivity   {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_video_view_list);
    
            //Create instance of Recycler view
            final RecyclerView videoList = (RecyclerView) findViewById(R.id.feed_list);
            LinearLayoutManager llm = new LinearLayoutManager(this);
            videoList.setLayoutManager(llm);
            videoList.setHasFixedSize(true);
    
            final List<String> list = new ArrayList<>();
            list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
            list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
            list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
            list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
            list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
            list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
            final VideoAdapter adapter = new VideoAdapter(list, this);
            videoList.setAdapter(adapter);
    
    
            videoList.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    TextureView view = adapter.getVideoPlayer();
                    Log.i("PERCENTAGE VISIBLE: ", String.valueOf(getVisiblePercent(adapter.getVideoPlayer())));
                    if(getVisiblePercent(view)==100) {
                        return;
                    }
                  }
                }
            });
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.menu_video_view_list, menu);
            return true;
        }
    
    
        public static int getVisiblePercent(View v) {
            if (v.isShown()) {
                Rect r = new Rect();
                v.getGlobalVisibleRect(r);
                double sVisible = r.width() * r.height();
                double sTotal = v.getWidth() * v.getHeight();
                return (int) (100 * sVisible / sTotal);
            } else {
                return -1;
            }
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            int id = item.getItemId();
    
            //noinspection SimplifiableIfStatement
            if (id == R.id.action_settings) {
                return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    
    
        /**
         * Recycler View Adapter
         */
        class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoFeedHolder> {
    
            public TextureView mPreview;
            private CardView mCardView;
            private List<String> mUrls;
            private Context mContext;
            private Surface mSurface;
            VideoControllerView controller;
            private View mAnchor;
            private ProgressBar mProgressDialog;
            private ImageView mHolder;
            private int mPosition;
            private VersysVideoPlayer mVideoPlayer;
            OnItemClickListener mItemClickListener;
    
            public VideoAdapter(List<String> url, Context ctx) {
                mUrls = url;
                mContext = ctx;
                controller = new VideoControllerView(ctx);
            }
    
            @Override
            public VideoAdapter.VideoFeedHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
                View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.video_item_feed, viewGroup, false);
                VideoFeedHolder holder = new VideoFeedHolder(v);
                return holder;
            }
    
            @Override
            public void onBindViewHolder(final VideoFeedHolder videoFeedHolder, final int i) {
    
                final VersysVideoPlayer videoPlayer = new VersysVideoPlayer(mContext, videoFeedHolder.mTexturePreview, mProgressDialog, videoFeedHolder.controlHolder);
                videoFeedHolder.placeholder.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        videoPlayer.startVideo(mUrls.get(i));
                        videoFeedHolder.placeholder.setVisibility(View.GONE);
                        videoFeedHolder.bar.setVisibility(View.VISIBLE);
                    }
                });
    
                mPosition = i;
            }
    
            @Override
            public void onAttachedToRecyclerView(RecyclerView recyclerView) {
                super.onAttachedToRecyclerView(recyclerView);
            }
    
    
            public String getUrl() {
                return mUrls.get(mPosition);
            }
    
    
            @Override
            public int getItemCount() {
    
                return mUrls.size();
            }
    
            public TextureView getVideoPlayer() {
                return mPreview;
            }
    
    
            public class VideoFeedHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    
                TextureView mTexturePreview;
                ProgressBar bar;
                ImageView placeholder;
                FrameLayout controlHolder;
                RelativeLayout touchLayout;
                public VideoFeedHolder(View itemView) {
                    super(itemView);
    
                    mTexturePreview = (TextureView) itemView.findViewById(R.id.video_player);
                    mPreview = mTexturePreview;
                    mCardView = (CardView) itemView.findViewById(R.id.cv);
                    bar = (ProgressBar)itemView.findViewById(R.id.buffereing);
                    placeholder = (ImageView) itemView.findViewById(R.id.holder);
                    mProgressDialog = bar;
    
                    controlHolder = (FrameLayout) itemView.findViewById(R.id.media_controller_anchor);
    
                }
    
                @Override
                public void onClick(View v) {
                    if (mItemClickListener != null) {
                        mItemClickListener.onItemClick(v, getAdapterPosition());
                    }
                }
            }
    
         }
    }
    

    VIDEO FEED ITEM XML

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.v7.widget.CardView
            android:layout_margin="10dp"
            android:layout_width="match_parent"
            android:layout_height="400dp"
            android:id="@+id/cv">
            <RelativeLayout
                android:id="@+id/anchor"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">
                <RelativeLayout
                    android:id="@+id/detail_layout"
                    android:layout_marginTop="10dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">
                    <ImageView
                        android:id="@+id/profile_pic"
                        android:background="@drawable/profiler"
                        android:layout_marginLeft="10dp"
                        android:layout_width="50dp"
                        android:layout_height="50dp" />
    
                    <TextView
                        android:id="@+id/user_name"
                        android:layout_alignTop="@+id/profile_pic"
                        android:layout_toRightOf="@+id/profile_pic"
                        android:text="Joe Bloggs"
                        android:layout_marginLeft="10dp"
                        android:textColor="#000000"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content" />
    
                    <TextView
                        android:id="@+id/date"
                        android:layout_below="@+id/user_name"
                        android:layout_toRightOf="@+id/profile_pic"
                        android:layout_marginLeft="10dp"
                        android:text="10 Aug 2015"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content" />
    
                    <TextView
                        android:id="@+id/desc"
                        android:layout_below="@+id/profile_pic"
                        android:layout_marginLeft="10dp"
                        android:text="This a sample video of a bird getting hit on the head and a rabbit waking from a nap!!"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content" />
                </RelativeLayout>
                <RelativeLayout
                    android:layout_below="@+id/detail_layout"
                    android:layout_margin="10dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">
                        <TextureView
                            android:id="@+id/video_player"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent" />
                       <FrameLayout
                           android:id="@+id/media_controller_anchor"
                           android:layout_alignParentBottom="true"
                           android:layout_width="match_parent"
                           android:layout_height="wrap_content">
                       </FrameLayout>
    
                        <ImageView
                            android:id="@+id/holder"
                            android:layout_centerInParent="true"
                            android:background="@drawable/default_video_poster"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content" />
    
                        <ProgressBar
                            android:id="@+id/buffereing"
                            android:visibility="gone"
                            android:layout_centerInParent="true"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content" />
                </RelativeLayout>
            </RelativeLayout>
        </android.support.v7.widget.CardView>
    </RelativeLayout>