Libraries

ExoPlayer in RecyclerView in android

Google+ Pinterest LinkedIn Tumblr

In the previous tutorial, we demonstrate video streaming over the Internet using ExoPlayer. Somehow you missed previous articles you must read this ExoPlayer in Android for better understanding. In another article we saw how to play audio, video from SD card (local) as well, So you get an idea about ExoPlayer core functionality, DASH and UI library module.

I hope you read my previous tutorials(I can assume you have basic knowledge about ExoPlayer). In this tutorial, I will describe how to implement video playback in the RecyclerView Android. Just like some popular application eg. Magisto, Facebook, Twitter, Instagram.

This article based on ExoPlayer. In case some things miss in this article download full source and a working sample app is there.

Before starting code, Think about the problem statement. Basically, The following major challenges for us to implementing ExoPlayer in RecyclerView.

  • The first problem is managing ExoPlayer playback
  • Second is we need to know which view on the scrolled list is currently active. So we can initialize and release ExoPlayer intense in RecylerView

Create a new project

Simply go to file menu and create a project and add the ExoPlayer dependency in build.gradle.

implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
implementation 'org.jsoup:jsoup:1.10.3'

The complete project hierarchy seems like image

how to play youtube player in recylerview

Create a custom RecyclerView with ExoPlayerRecyclerView name

Now comes in first problem is

how to manage ExoPlayer playback?

  • Create a class ExoPlayerRecyclerView.java which extend RecyclerView 
  • Get start position and end position of view from LayoutManager after that calculate target position.
  • Here now on target position, we will add ExoPlayerView and set ExoPlayer.
 //play the video in the row
    public void playVideo() {
        int startPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
        int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();

        if (endPosition - startPosition > 1) {
            endPosition = startPosition + 1;
        }

        if (startPosition < 0 || endPosition < 0) {
            return;
        }

        int targetPosition;
        if (startPosition != endPosition) {
            int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
            int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
            targetPosition = startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
        } else {
            targetPosition = startPosition;
        }

        if (targetPosition < 0 || targetPosition == playPosition) {
            return;
        }
        playPosition = targetPosition;
        if (videoSurfaceView == null) {
            return;
        }
        videoSurfaceView.setVisibility(INVISIBLE);
        removeVideoView(videoSurfaceView);

        // get target View targetPosition in RecyclerView
        int at = targetPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();

        View child = getChildAt(at);
        if (child == null) {
            return;
        }

        VideoRecyclerViewAdapter.ViewHolder holder
                = (VideoRecyclerViewAdapter.ViewHolder) child.getTag();
        if (holder == null) {
            playPosition = -1;
            return;
        }
        mCoverImage = holder.mCover;
        FrameLayout frameLayout = holder.itemView.findViewById(R.id.video_layout);
        frameLayout.addView(videoSurfaceView);
        addedVideo = true;
        rowParent = holder.itemView;
        videoSurfaceView.requestFocus();
        // Bind the player to the view.
        videoSurfaceView.setPlayer(player);

        // Measures bandwidth during playback. Can be null if not required.
        DefaultBandwidthMeter defaultBandwidthMeter = new DefaultBandwidthMeter();
        // Produces DataSource instances through which media data is loaded.

        DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(appContext,
                Util.getUserAgent(appContext, "android_wave_list"), defaultBandwidthMeter);
        // This is the MediaSource representing the media to be played.
        String uriString = videoInfoList.get(targetPosition).getUrl();
        if (uriString != null) {
            MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
                    .createMediaSource(Uri.parse(uriString));
            // Prepare the player with the source.
            player.prepare(videoSource);
            player.setPlayWhenReady(true);
        }
    }

Now comes in the second problem get visible Video Surface scrolled list.

 private int getVisibleVideoSurfaceHeight(int playPosition) {
        int at = playPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();

        View child = getChildAt(at);
        if (child == null) {
            return 0;
        }

        int[] location01 = new int[2];
        child.getLocationInWindow(location01);

        if (location01[1] < 0) {
            return location01[1] + videoSurfaceDefaultHeight;
        } else {
            return screenDefaultHeight - location01[1];
        }
    }

The complete class code is below

package com.androidwave.exoplayer.ui;

import android.content.Context;
import android.graphics.Point;
import android.net.Uri;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.androidwave.exoplayer.R;
import com.androidwave.exoplayer.adapter.VideoRecyclerViewAdapter;
import com.androidwave.exoplayer.model.VideoInfo;
import com.androidwave.exoplayer.utils.VideoPlayerConfig;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;

import java.util.ArrayList;
import java.util.List;

public class ExoPlayerRecyclerView extends RecyclerView {

    private List<VideoInfo> videoInfoList = new ArrayList<>();
    private int videoSurfaceDefaultHeight = 0;
    private int screenDefaultHeight = 0;
    SimpleExoPlayer player;
    //surface view for playing video
    private PlayerView videoSurfaceView;
    private ImageView mCoverImage;
    private Context appContext;


    /**
     * the position of playing video
     */
    private int playPosition = -1;

    private boolean addedVideo = false;
    private View rowParent;

    /**
     * {@inheritDoc}
     *
     * @param context
     */
    public ExoPlayerRecyclerView(Context context) {
        super(context);
        initialize(context);
    }

    /**
     * {@inheritDoc}
     *
     * @param context
     * @param attrs
     */
    public ExoPlayerRecyclerView(Context context,
                                 AttributeSet attrs) {
        super(context, attrs);
        initialize(context);
    }

    /**
     * {@inheritDoc}
     *
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public ExoPlayerRecyclerView(Context context,
                                 AttributeSet attrs,
                                 int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize(context);
    }

    public void setVideoInfoList(List<VideoInfo> videoInfoList) {
        this.videoInfoList = videoInfoList;

    }


    /**
     * prepare for video play
     */
    //remove the player from the row
    private void removeVideoView(PlayerView videoView) {

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

        int index = parent.indexOfChild(videoView);
        if (index >= 0) {
            parent.removeViewAt(index);
            addedVideo = false;
        }

    }

    //play the video in the row
    public void playVideo() {
        int startPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
        int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();

        if (endPosition - startPosition > 1) {
            endPosition = startPosition + 1;
        }

        if (startPosition < 0 || endPosition < 0) {
            return;
        }

        int targetPosition;
        if (startPosition != endPosition) {
            int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
            int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
            targetPosition = startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
        } else {
            targetPosition = startPosition;
        }

        if (targetPosition < 0 || targetPosition == playPosition) {
            return;
        }
        playPosition = targetPosition;
        if (videoSurfaceView == null) {
            return;
        }
        videoSurfaceView.setVisibility(INVISIBLE);
        removeVideoView(videoSurfaceView);

        // get target View targetPosition in RecyclerView
        int at = targetPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();

        View child = getChildAt(at);
        if (child == null) {
            return;
        }

        VideoRecyclerViewAdapter.ViewHolder holder
                = (VideoRecyclerViewAdapter.ViewHolder) child.getTag();
        if (holder == null) {
            playPosition = -1;
            return;
        }
        mCoverImage = holder.mCover;
        FrameLayout frameLayout = holder.itemView.findViewById(R.id.video_layout);
        frameLayout.addView(videoSurfaceView);
        addedVideo = true;
        rowParent = holder.itemView;
        videoSurfaceView.requestFocus();
        // Bind the player to the view.
        videoSurfaceView.setPlayer(player);

        // Measures bandwidth during playback. Can be null if not required.
        DefaultBandwidthMeter defaultBandwidthMeter = new DefaultBandwidthMeter();
        // Produces DataSource instances through which media data is loaded.

        DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(appContext,
                Util.getUserAgent(appContext, "android_wave_list"), defaultBandwidthMeter);
        // This is the MediaSource representing the media to be played.
        String uriString = videoInfoList.get(targetPosition).getUrl();
        if (uriString != null) {
            MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
                    .createMediaSource(Uri.parse(uriString));
            // Prepare the player with the source.
            player.prepare(videoSource);
            player.setPlayWhenReady(true);
        }


    }

    private int getVisibleVideoSurfaceHeight(int playPosition) {
        int at = playPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();

        View child = getChildAt(at);
        if (child == null) {
            return 0;
        }

        int[] location01 = new int[2];
        child.getLocationInWindow(location01);

        if (location01[1] < 0) {
            return location01[1] + videoSurfaceDefaultHeight;
        } else {
            return screenDefaultHeight - location01[1];
        }
    }


    private void initialize(Context context) {

        appContext = context.getApplicationContext();
        Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        Point point = new Point();
        display.getSize(point);
        videoSurfaceDefaultHeight = point.x;

        screenDefaultHeight = point.y;
        videoSurfaceView = new PlayerView(appContext);
        videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);

        BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        TrackSelection.Factory videoTrackSelectionFactory =
                new AdaptiveTrackSelection.Factory(bandwidthMeter);
        TrackSelector trackSelector =
                new DefaultTrackSelector(videoTrackSelectionFactory);
        LoadControl loadControl = new DefaultLoadControl(
                new DefaultAllocator(true, 16),
                VideoPlayerConfig.MIN_BUFFER_DURATION,
                VideoPlayerConfig.MAX_BUFFER_DURATION,
                VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER,
                VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER, -1, true);

        // 2. Create the player
        player = ExoPlayerFactory.newSimpleInstance(appContext, trackSelector, loadControl);
        // Bind the player to the view.
        videoSurfaceView.setUseController(false);
        videoSurfaceView.setPlayer(player);

        addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                    playVideo();
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
            }
        });

        addOnChildAttachStateChangeListener(new OnChildAttachStateChangeListener() {
            @Override
            public void onChildViewAttachedToWindow(View view) {

            }

            @Override
            public void onChildViewDetachedFromWindow(View view) {
                if (addedVideo &amp;&amp; rowParent != null &amp;&amp; rowParent.equals(view)) {
                    removeVideoView(videoSurfaceView);
                    playPosition = -1;
                    videoSurfaceView.setVisibility(INVISIBLE);
                }

            }
        });
        player.addListener(new Player.EventListener() {
            @Override
            public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {

            }

            @Override
            public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {

            }

            @Override
            public void onLoadingChanged(boolean isLoading) {

            }

            @Override
            public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
                switch (playbackState) {

                    case Player.STATE_BUFFERING:
                        //   videoSurfaceView.setAlpha(0.5f);
                        break;
                    case Player.STATE_ENDED:
                        player.seekTo(0);
                        break;
                    case Player.STATE_IDLE:

                        break;
                    case Player.STATE_READY:
                        videoSurfaceView.setVisibility(VISIBLE);
                        videoSurfaceView.setAlpha(1);
                        mCoverImage.setVisibility(GONE);

                        break;
                    default:
                        break;
                }
            }

            @Override
            public void onRepeatModeChanged(int repeatMode) {

            }

            @Override
            public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {

            }

            @Override
            public void onPlayerError(ExoPlaybackException error) {

            }

            @Override
            public void onPositionDiscontinuity(int reason) {

            }

            @Override
            public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {

            }

            @Override
            public void onSeekProcessed() {

            }
        });
    }

    public void onPausePlayer() {
        if (videoSurfaceView != null) {
            removeVideoView(videoSurfaceView);
            player.release();
            videoSurfaceView = null;
        }
    }

    public void onRestartPlayer() {
        if (videoSurfaceView == null) {
            playPosition = -1;
            playVideo();
        }
    }

    /**
     * release memory
     */
    public void onRelease() {

        if (player != null) {
            player.release();
            player = null;
        }

        rowParent = null;
    }


}

Now ExoPlayerRecyclerView.java component is ready to use. Now I will explain how to use this component.

Create a class name is VideoInfo.java

Go to the src folder and create the model folder for separating all model class. Inside create a new class with name VideoInfo.java. this is a POJO class that hold getter and setter of title and cover image URL . and media URL

package com.androidwave.exoplayer.model;

public class VideoInfo {
    private int mId;
    private String mTitle;
    private String mUrl;
    private String mCoverUrl;
    private String mUserHandle;

    public String getUserHandle() {
        return mUserHandle;
    }

    public void setUserHandle(String mUserHandle) {
        this.mUserHandle = mUserHandle;
    }

    public int getId() {
        return mId;
    }

    public void setId(int mId) {
        this.mId = mId;
    }

    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String mTitle) {
        this.mTitle = mTitle;
    }

    public String getUrl() {
        return mUrl;
    }

    public void setUrl(String mUrl) {
        this.mUrl = mUrl;
    }

    public String getCoverUrl() {
        return mCoverUrl;
    }

    public void setCoverUrl(String mCoverUrl) {
        this.mCoverUrl = mCoverUrl;
    }
}

Create a RecyclerView adapter and bind views with holder

Create adapter class names with VideoRecyclerViewAdapter.java.

package com.androidwave.exoplayer.adapter;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import com.androidwave.exoplayer.R;
import com.androidwave.exoplayer.model.VideoInfo;
import com.androidwave.exoplayer.ui.BaseViewHolder;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;

import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class VideoRecyclerViewAdapter extends RecyclerView.Adapter<BaseViewHolder> {

    public static final int VIEW_TYPE_EMPTY = 0;
    public static final int VIEW_TYPE_NORMAL = 1;

    private List<VideoInfo> mInfoList;

    public VideoRecyclerViewAdapter(List<VideoInfo> infoList) {
        mInfoList = infoList;
    }

    @NonNull
    @Override
    public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        switch (viewType) {
            case VIEW_TYPE_NORMAL:
                return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false));
            case VIEW_TYPE_EMPTY:
                return new EmptyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_empty_view, parent, false));
            default:
                return null;
        }
    }

    @Override
    public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
        holder.onBind(position);
    }


    @Override
    public int getItemViewType(int position) {
        if (mInfoList != null &amp;&amp; mInfoList.size() > 0) {
            return VIEW_TYPE_NORMAL;
        } else {
            return VIEW_TYPE_EMPTY;
        }
    }

    @Override
    public int getItemCount() {
        if (mInfoList != null &amp;&amp; mInfoList.size() > 0) {
            return mInfoList.size();
        } else {
            return 1;
        }
    }



    public void onRelease() {
        if (mInfoList != null) {
            mInfoList.clear();
            mInfoList = null;
        }
    }

    public class ViewHolder extends BaseViewHolder {
        @BindView(R.id.textViewTitle)
        TextView textViewTitle;
        @BindView(R.id.userHandle)
        TextView userHandle;
        @BindView(R.id.video_layout)
        public FrameLayout videoLayout;
        @BindView(R.id.cover)
        public ImageView mCover;
        public final View parent;


        public ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
            parent = itemView;
        }

        protected void clear() {

        }

        public void onBind(int position) {
            super.onBind(position);
            parent.setTag(this);
            VideoInfo videoInfo = mInfoList.get(position);
            textViewTitle.setText(videoInfo.getTitle());
            userHandle.setText(videoInfo.getUserHandle());
            Glide.with(itemView.getContext())
                    .load(videoInfo.getCoverUrl()).apply(new RequestOptions().optionalCenterCrop())
                    .into(mCover);
        }
    }

    public class EmptyViewHolder extends BaseViewHolder {

        @BindView(R.id.btn_retry)
        Button retryButton;

        @BindView(R.id.tv_message)
        TextView messageTextView;

        public EmptyViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
            itemView.setVisibility(View.GONE);
        }

        @Override
        protected void clear() {

        }

        @OnClick(R.id.btn_retry)
        void onRetryClick() {

        }
    }
}

Lets add RecyclerView inside XML file

If you selected some default template while creating a project, MainActivity.java and activity_main.xml class will automatically be created.

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.androidwave.exoplayer.ui.ExoPlayerRecyclerView
        android:id="@+id/recyclerViewFeed"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#dedede"
        android:dividerHeight="8dp" />

</android.support.constraint.ConstraintLayout>

Now prepare video array list

For displaying data on RecyclerView We have to prepare a demo content.

    private void prepareVideoList() {
        VideoInfo videoInfo = new VideoInfo();
        videoInfo.setId(1);
        videoInfo.setUserHandle("@h.pandya");
        videoInfo.setTitle("Do you think the concept of marriage will no longer exist in the future?");
        videoInfo.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-1.png");
        videoInfo.setUrl("https://androidwave.com/media/androidwave-video-1.mp4");

        VideoInfo videoInfo2 = new VideoInfo();
        videoInfo2.setId(2);
        videoInfo2.setUserHandle("@hardik.patel");
        videoInfo2.setTitle("If my future husband doesn't cook food as good as my mother should I scold him?");
        videoInfo2.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-2.png");
        videoInfo2.setUrl("https://androidwave.com/media/androidwave-video-2.mp4");

        VideoInfo videoInfo3 = new VideoInfo();
        videoInfo3.setId(3);
        videoInfo3.setUserHandle("@arun.gandhi");
        videoInfo3.setTitle("Give your opinion about the Ayodhya temple controversy.");
        videoInfo3.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-3.png");
        videoInfo3.setUrl("https://androidwave.com/media/androidwave-video-3.mp4");

        VideoInfo videoInfo4 = new VideoInfo();
        videoInfo4.setId(4);
        videoInfo4.setUserHandle("@sachin.patel");
        videoInfo4.setTitle("When did kama founders find sex offensive to Indian traditions");
        videoInfo4.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-4.png");
        videoInfo4.setUrl("https://androidwave.com/media/androidwave-video-6.mp4");

        VideoInfo videoInfo5 = new VideoInfo();
        videoInfo5.setId(5);
        videoInfo5.setUserHandle("@monika.sharma");
        videoInfo5.setTitle("When did you last cry in front of someone?");
        videoInfo5.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-5.png");
        videoInfo5.setUrl("https://androidwave.com/media/androidwave-video-5.mp4");

        videoInfoList.add(videoInfo);
        videoInfoList.add(videoInfo2);
        videoInfoList.add(videoInfo3);
        videoInfoList.add(videoInfo4);
        videoInfoList.add(videoInfo5);
        videoInfoList.add(videoInfo);
        videoInfoList.add(videoInfo2);
        videoInfoList.add(videoInfo3);
        videoInfoList.add(videoInfo4);
        videoInfoList.add(videoInfo5);

    }

Finally main actvity looks like

package com.androidwave.exoplayer;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;

import com.androidwave.exoplayer.adapter.VideoRecyclerViewAdapter;
import com.androidwave.exoplayer.model.VideoInfo;
import com.androidwave.exoplayer.ui.ExoPlayerRecyclerView;
import com.androidwave.exoplayer.utils.DividerItemDecoration;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

import static android.widget.LinearLayout.VERTICAL;

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.recyclerViewFeed)
    ExoPlayerRecyclerView recyclerViewFeed;

    private List<VideoInfo> videoInfoList = new ArrayList<>();
    private VideoRecyclerViewAdapter mAdapter;
    private boolean firstTime = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        prepareVideoList();
        recyclerViewFeed.setVideoInfoList(videoInfoList);
        mAdapter = new VideoRecyclerViewAdapter(videoInfoList);
        recyclerViewFeed.setLayoutManager(new LinearLayoutManager(this, VERTICAL, false));
        Drawable dividerDrawable = ContextCompat.getDrawable(this, R.drawable.divider_drawable);
        recyclerViewFeed.addItemDecoration(new DividerItemDecoration(dividerDrawable));
        recyclerViewFeed.setItemAnimator(new DefaultItemAnimator());
        recyclerViewFeed.setAdapter(mAdapter);

        if (firstTime) {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    recyclerViewFeed.playVideo();
                }
            });
            firstTime = false;
        }

    }

    private void prepareVideoList() {
        VideoInfo videoInfo = new VideoInfo();
        videoInfo.setId(1);
        videoInfo.setUserHandle("@h.pandya");
        videoInfo.setTitle("Do you think the concept of marriage will no longer exist in the future?");
        videoInfo.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-1.png");
        videoInfo.setUrl("https://androidwave.com/media/androidwave-video-1.mp4");

        VideoInfo videoInfo2 = new VideoInfo();
        videoInfo2.setId(2);
        videoInfo2.setUserHandle("@hardik.patel");
        videoInfo2.setTitle("If my future husband doesn't cook food as good as my mother should I scold him?");
        videoInfo2.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-2.png");
        videoInfo2.setUrl("https://androidwave.com/media/androidwave-video-2.mp4");

        VideoInfo videoInfo3 = new VideoInfo();
        videoInfo3.setId(3);
        videoInfo3.setUserHandle("@arun.gandhi");
        videoInfo3.setTitle("Give your opinion about the Ayodhya temple controversy.");
        videoInfo3.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-3.png");
        videoInfo3.setUrl("https://androidwave.com/media/androidwave-video-3.mp4");

        VideoInfo videoInfo4 = new VideoInfo();
        videoInfo4.setId(4);
        videoInfo4.setUserHandle("@sachin.patel");
        videoInfo4.setTitle("When did kama founders find sex offensive to Indian traditions");
        videoInfo4.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-4.png");
        videoInfo4.setUrl("https://androidwave.com/media/androidwave-video-6.mp4");

        VideoInfo videoInfo5 = new VideoInfo();
        videoInfo5.setId(5);
        videoInfo5.setUserHandle("@monika.sharma");
        videoInfo5.setTitle("When did you last cry in front of someone?");
        videoInfo5.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-5.png");
        videoInfo5.setUrl("https://androidwave.com/media/androidwave-video-5.mp4");

        videoInfoList.add(videoInfo);
        videoInfoList.add(videoInfo2);
        videoInfoList.add(videoInfo3);
        videoInfoList.add(videoInfo4);
        videoInfoList.add(videoInfo5);
        videoInfoList.add(videoInfo);
        videoInfoList.add(videoInfo2);
        videoInfoList.add(videoInfo3);
        videoInfoList.add(videoInfo4);
        videoInfoList.add(videoInfo5);

    }

    @Override
    protected void onPause() {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                recyclerViewFeed.onPausePlayer();
            }
        });
        super.onPause();
    }

    @Override
    protected void onResume() {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                recyclerViewFeed.onRestartPlayer();
            }
        });
        super.onResume();
    }

    @Override
    protected void onDestroy() {
        if(recyclerViewFeed!=null)
        recyclerViewFeed.onRelease();
        super.onDestroy();
    }
}

After following all above just RUN the project and use app, If you have any queries, feel free to ask them in the comment section below. Happy Coding 🙂

Download Sample Project- ExoPlayer in RecyclerView in Android
Author

30
Leave a Reply

avatar
17 Comment threads
13 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
16 Comment authors
leewannadminWaqasMurthyRahul Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Hitesh
Guest
Hitesh

Hello Sir,
I want to full source code exoplayer in Recylerview and Autoplayback.

Thanks!!
Hitesh

Darshan
Guest
Darshan

What if all the items in my RecyclerView are not videos? How can I modify the ExoPlayerRecyclerView to identify the rows which have videos and play them accordingly? Any help would be great 🙂

Darshan Pania
Guest
Darshan Pania

How can I use this when I am using multiple viewholders in my recycle view? Any suggestion would help

nidhi
Guest
nidhi

Hello Sir,
i m new to android your tutorial is good. I want to full source code exoplayer in Recylerview and Autoplayback.
if i pause at some point for example if i pause the video at 20sec if i go back and come again same activity it should be played(continued) from 20sec. how can i do this…..

Ravi
Guest
Ravi

Hello api 25 it is not work in api 25 please help me

Mujahid
Guest
Mujahid

Aoa sir how are u?

you are doing good job to help other developers thanks i need your help i am working with view pager fragment and i wants when i go to video view pager player should start and when i back from that page player should stop actually i am having issue with stop the player and restart the player please help?

trackback

[…] In this tutorials, I’m going to explain how to use ExoPlayer. In this tutorial, we will demonstrate how to stream audio and video local URI and server as well. Means we are going to create an audio-video player, that player will capable to play audio and video file local or remote server as well. If you want to implement ExoPlayer in RecylerView read this article. […]

mujeeb
Guest
mujeeb

Dear Sir, it is not handled in Paused State , On Resume state

Rahul
Guest
Rahul

Hi, have you got, how to handled in Paused State , On Resume state

Rohan Luthra
Guest
Rohan Luthra

Hey, Can you tell me how to implement this in Fragments using View Pager?
Thank you,

Rohan Luthra
Guest
Rohan Luthra

Sir, I am not able to add this to fragments.
The onCreateView() in VideoAdapter is not being called.
Also i’m getting a Error log – “RecyclerView: No adapter attached; skipping layout”
The code is working correctly when implemented in an Activity. Please help.

Hubert
Guest
Hubert

Can you please help me use exoplayer in recyclerview (videos from firebase)

Akash Garg
Guest
Akash Garg

How to ensure the exoplayer prebuffer for instant seamless playing in HLS while scrolling.

Jose Reis
Guest
Jose Reis

Hi, I really enjoyed your article … But I would like you to help me with the following:
I want this to be implemented within a fragment … Could you help me ???

Murthy
Guest
Murthy

my recyclerview items has videos, images, simple text. how can i use exoplayer that play only at video items. I had tried but video playing on one position greater that video

Waqas
Guest
Waqas

Hi,

Brilliant article. Kindly, can you guide me how can I play YouTube videos. I can pass video url of youtube video to play.

Can you help on this?

thanks

Waqas

leewann
Guest
leewann

Hi, Thanks for good information. I’d like to use your post. When I executed, ExoPlayerRecyclerView.java videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM); <- here I met the below error. If I remark the line, then the app is executed, but no vide plays. ————————————————————————– Caused by: java.lang.reflect.InvocationTargetException at java.lang.reflect.Constructor.newInstance0(Native Method) at java.lang.reflect.Constructor.newInstance(Constructor.java:334) at android.view.LayoutInflater.createView(LayoutInflater.java:647) at com.tsengvn.typekit.TypekitLayoutInflater.createCustomViewInternal(TypekitLayoutInflater.java:289) at com.tsengvn.typekit.TypekitLayoutInflater.access$000(TypekitLayoutInflater.java:21) at com.tsengvn.typekit.TypekitLayoutInflater$PrivateWrapperFactory2.onCreateView(TypekitLayoutInflater.java:245) at android.view.LayoutInflater$FactoryMerger.onCreateView(LayoutInflater.java:186) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:780) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730) at android.view.LayoutInflater.rInflate(LayoutInflater.java:863) at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824) at android.view.LayoutInflater.inflate(LayoutInflater.java:515) at com.tsengvn.typekit.TypekitLayoutInflater.inflate(TypekitLayoutInflater.java:75) at android.view.LayoutInflater.inflate(LayoutInflater.java:423) at com.tsengvn.typekit.TypekitLayoutInflater.inflate(TypekitLayoutInflater.java:69) at android.view.LayoutInflater.inflate(LayoutInflater.java:374) at android.support.v7.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:469) at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:140) at com.korea7st.emochat.activity.StoryPlay3Acti.onCreate(StoryPlay3Acti.java:52) at android.app.Activity.performCreate(Activity.java:6980) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2806) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2928) at android.app.ActivityThread.-wrap11(Unknown Source:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1609) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6709) at… Read more »

leewann
Guest
leewann

ExoPlayerRecyclerView.java
videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
<- here I met the above post error.
so, I remarked the line, then the app is executed,
Now I can hear the audio, but the playing video is showed only full-black.
Could you please help me?