ExoPlayer in RecyclerView in Android App step bye step guide


 This article based on ExoPlayer. In case some things missing 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 in RecyclerView
  • Second is we need to know which view on the scrolled list is currently active. Other words you can say which list item is visible 100%. So we can attach the player with a particular view.

Step to implement ExoPlayer in RecyclerView

  • Create Project and set up dependencies
  • Write a custom RecyclerView that are capable of manage video playback.
  • Write an adapter for holding list item
    • Create a model class with getter setter
    • Prepare an XML layout for media list item
    • Write ViewHolder class for representing media list item
    • Complete the RecyclerView Adapter
  • Do followings operation in MainActivity
    • In MainActivity layout, file adds custom ExoRecyclerView that create earlier.
    • Prepare demo content
    • Set the adapter to RecyclerView
    • Run the project

1. Create Project and set up dependencies

All conceptual part is done, let’s move to AndroidStudio create a new project with the default template. Follows few steps your project will be created, now we need to add some dependencies in app/gradle file.

Following dependencies are in this android app tutorial. I hope you already know the uses of use one.

  • RecyclerView – For using RecyclerView
  • ExoPlayer – Using ExoPlayer
  • Glide – For loading media thumbnails
  • Constraint Layout – Performace is much better to other layouts
1.1 Open the app/build.gradle file and add all above dependencies
// RecyclerView
implementation 'com.android.support:recyclerview-v7:28.0.0'
// ExoPlayer
implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
// Image loading
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
1.2 Enable vector drawable by adding below line inside defaultConfig bracket
vectorDrawables.useSupportLibrary = true

2. Write a custom RecyclerView that is capable manage video playback.

Doing that we have to create a new subclass of RecyclerView named is ExoPlayerRecyclerView. Mean create a new class ExoPlayerRecyclerView.java which extends RecyclerView. In this class, we have to manage two things. First, manage ExoPlayer playback. Second is to find that list item that visibility 100%. So that we will add a player on that particular view.

2.1 Problem – 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 currently visible view position.
  • Here now on target position, we will add ExoPlayerView and set ExoPlayer.
//play the video in the row
public void playVideo(boolean isEndOfList) {
int targetPosition;
if (!isEndOfList) {
int startPosition = ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
// if there is more than 2 list-items on the screen, set the difference to be 1
if (endPosition - startPosition > 1) {
endPosition = startPosition + 1;
}
// something is wrong. return.
if (startPosition < 0 || endPosition < 0) {
return;
}
// if there is more than 1 list-item on the screen
if (startPosition != endPosition) {
int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
targetPosition =
startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
} else {
targetPosition = startPosition;
}
} else {
targetPosition = mediaObjects.size() - 1;
}
Log.d(TAG, "playVideo: target position: " + targetPosition);
// video is already playing so return
if (targetPosition == playPosition) {
return;
}
// set the position of the list-item that is to be played
playPosition = targetPosition;
if (videoSurfaceView == null) {
return;
}
// remove any old surface views from previously playing videos
videoSurfaceView.setVisibility(INVISIBLE);
removeVideoView(videoSurfaceView);
int currentPosition =
targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
View child = getChildAt(currentPosition);
if (child == null) {
return;
}
PlayerViewHolder holder = (PlayerViewHolder) child.getTag();
if (holder == null) {
playPosition = -1;
return;
}
mediaCoverImage = holder.mediaCoverImage;
progressBar = holder.progressBar;
volumeControl = holder.volumeControl;
viewHolderParent = holder.itemView;
requestManager = holder.requestManager;
mediaContainer = holder.mediaContainer;
videoSurfaceView.setPlayer(videoPlayer);
viewHolderParent.setOnClickListener(videoViewClickListener);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(
context, Util.getUserAgent(context, AppName));
String mediaUrl = mediaObjects.get(targetPosition).getUrl();
if (mediaUrl != null) {
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(mediaUrl));
videoPlayer.prepare(videoSource);
videoPlayer.setPlayWhenReady(true);
}
}
andwidthMeter =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);
}
}
2.2 Second Problem – Get visible video surface scrolled list.
/**
* Returns the visible region of the video surface on the screen.
* if some is cut off, it will return less than the @videoSurfaceDefaultHeight
*/
private int getVisibleVideoSurfaceHeight(int playPosition) {
int at = playPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
Log.d(TAG, "getVisibleVideoSurfaceHeight: at: " + at);
View child = getChildAt(at);
if (child == null) {
return 0;
}
int[] location = new int[2];
child.getLocationInWindow(location);
if (location[1] < 0) {
return location[1] + videoSurfaceDefaultHeight;
} else {
return screenDefaultHeight - location[1];
}
}

I have solved both problems, The complete code of ExoPlayerRecyclerView is

package com.androidwave.exoplayer.ui;
import android.content.Context;
import android.graphics.Point;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import com.androidwave.exoplayer.R;
import com.androidwave.exoplayer.adapter.PlayerViewHolder;
import com.androidwave.exoplayer.model.MediaObject;
import com.bumptech.glide.RequestManager;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
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.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
import java.util.Objects;
/**
* Created on : May 24, 2019
* Author : AndroidWave
*/
public class ExoPlayerRecyclerView extends RecyclerView {
private static final String TAG = "ExoPlayerRecyclerView";
private static final String AppName = "Android ExoPlayer";
/**
* PlayerViewHolder UI component
* Watch PlayerViewHolder class
*/
private ImageView mediaCoverImage, volumeControl;
private ProgressBar progressBar;
private View viewHolderParent;
private FrameLayout mediaContainer;
private PlayerView videoSurfaceView;
private SimpleExoPlayer videoPlayer;
/**
* variable declaration
*/
// Media List
private ArrayList<MediaObject> mediaObjects = new ArrayList<>();
private int videoSurfaceDefaultHeight = 0;
private int screenDefaultHeight = 0;
private Context context;
private int playPosition = -1;
private boolean isVideoViewAdded;
private RequestManager requestManager;
// controlling volume state
private VolumeState volumeState;
private OnClickListener videoViewClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
toggleVolume();
}
};
public ExoPlayerRecyclerView(@NonNull Context context) {
super(context);
init(context);
}
public ExoPlayerRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
this.context = context.getApplicationContext();
Display display = ((WindowManager) Objects.requireNonNull(
getContext().getSystemService(Context.WINDOW_SERVICE))).getDefaultDisplay();
Point point = new Point();
display.getSize(point);
videoSurfaceDefaultHeight = point.x;
screenDefaultHeight = point.y;
videoSurfaceView = new PlayerView(this.context);
videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
//Create the player using ExoPlayerFactory
videoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
// Disable Player Control
videoSurfaceView.setUseController(false);
// Bind the player to the view.
videoSurfaceView.setPlayer(videoPlayer);
// Turn on Volume
setVolumeControl(VolumeState.ON);
addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (mediaCoverImage != null) {
// show the old thumbnail
mediaCoverImage.setVisibility(VISIBLE);
}
// There's a special case when the end of the list has been reached.
// Need to handle that with this bit of logic
if (!recyclerView.canScrollVertically(1)) {
playVideo(true);
} else {
playVideo(false);
}
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
addOnChildAttachStateChangeListener(new OnChildAttachStateChangeListener() {
@Override
public void onChildViewAttachedToWindow(@NonNull View view) {
}
@Override
public void onChildViewDetachedFromWindow(@NonNull View view) {
if (viewHolderParent != null && viewHolderParent.equals(view)) {
resetVideoView();
}
}
});
videoPlayer.addListener(new Player.EventListener() {
@Override
public void onTimelineChanged(Timeline timeline, @Nullable 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:
Log.e(TAG, "onPlayerStateChanged: Buffering video.");
if (progressBar != null) {
progressBar.setVisibility(VISIBLE);
}
break;
case Player.STATE_ENDED:
Log.d(TAG, "onPlayerStateChanged: Video ended.");
videoPlayer.seekTo(0);
break;
case Player.STATE_IDLE:
break;
case Player.STATE_READY:
Log.e(TAG, "onPlayerStateChanged: Ready to play.");
if (progressBar != null) {
progressBar.setVisibility(GONE);
}
if (!isVideoViewAdded) {
addVideoView();
}
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 playVideo(boolean isEndOfList) {
int targetPosition;
if (!isEndOfList) {
int startPosition = ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
// if there is more than 2 list-items on the screen, set the difference to be 1
if (endPosition - startPosition > 1) {
endPosition = startPosition + 1;
}
// something is wrong. return.
if (startPosition < 0 || endPosition < 0) {
return;
}
// if there is more than 1 list-item on the screen
if (startPosition != endPosition) {
int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
targetPosition =
startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
} else {
targetPosition = startPosition;
}
} else {
targetPosition = mediaObjects.size() - 1;
}
Log.d(TAG, "playVideo: target position: " + targetPosition);
// video is already playing so return
if (targetPosition == playPosition) {
return;
}
// set the position of the list-item that is to be played
playPosition = targetPosition;
if (videoSurfaceView == null) {
return;
}
// remove any old surface views from previously playing videos
videoSurfaceView.setVisibility(INVISIBLE);
removeVideoView(videoSurfaceView);
int currentPosition =
targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
View child = getChildAt(currentPosition);
if (child == null) {
return;
}
PlayerViewHolder holder = (PlayerViewHolder) child.getTag();
if (holder == null) {
playPosition = -1;
return;
}
mediaCoverImage = holder.mediaCoverImage;
progressBar = holder.progressBar;
volumeControl = holder.volumeControl;
viewHolderParent = holder.itemView;
requestManager = holder.requestManager;
mediaContainer = holder.mediaContainer;
videoSurfaceView.setPlayer(videoPlayer);
viewHolderParent.setOnClickListener(videoViewClickListener);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(
context, Util.getUserAgent(context, AppName));
String mediaUrl = mediaObjects.get(targetPosition).getUrl();
if (mediaUrl != null) {
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(mediaUrl));
videoPlayer.prepare(videoSource);
videoPlayer.setPlayWhenReady(true);
}
}
/**
* Returns the visible region of the video surface on the screen.
* if some is cut off, it will return less than the @videoSurfaceDefaultHeight
*/
private int getVisibleVideoSurfaceHeight(int playPosition) {
int at = playPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
Log.d(TAG, "getVisibleVideoSurfaceHeight: at: " + at);
View child = getChildAt(at);
if (child == null) {
return 0;
}
int[] location = new int[2];
child.getLocationInWindow(location);
if (location[1] < 0) {
return location[1] + videoSurfaceDefaultHeight;
} else {
return screenDefaultHeight - location[1];
}
}
// Remove the old player
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);
isVideoViewAdded = false;
viewHolderParent.setOnClickListener(null);
}
}
private void addVideoView() {
mediaContainer.addView(videoSurfaceView);
isVideoViewAdded = true;
videoSurfaceView.requestFocus();
videoSurfaceView.setVisibility(VISIBLE);
videoSurfaceView.setAlpha(1);
mediaCoverImage.setVisibility(GONE);
}
private void resetVideoView() {
if (isVideoViewAdded) {
removeVideoView(videoSurfaceView);
playPosition = -1;
videoSurfaceView.setVisibility(INVISIBLE);
mediaCoverImage.setVisibility(VISIBLE);
}
}
public void releasePlayer() {
if (videoPlayer != null) {
videoPlayer.release();
videoPlayer = null;
}
viewHolderParent = null;
}
public void onPausePlayer() {
if (videoPlayer != null) {
videoPlayer.stop(true);
}
}
private void toggleVolume() {
if (videoPlayer != null) {
if (volumeState == VolumeState.OFF) {
Log.d(TAG, "togglePlaybackState: enabling volume.");
setVolumeControl(VolumeState.ON);
} else if (volumeState == VolumeState.ON) {
Log.d(TAG, "togglePlaybackState: disabling volume.");
setVolumeControl(VolumeState.OFF);
}
}
}
private void setVolumeControl(VolumeState state) {
volumeState = state;
if (state == VolumeState.OFF) {
videoPlayer.setVolume(0f);
animateVolumeControl();
} else if (state == VolumeState.ON) {
videoPlayer.setVolume(1f);
animateVolumeControl();
}
}
private void animateVolumeControl() {
if (volumeControl != null) {
volumeControl.bringToFront();
if (volumeState == VolumeState.OFF) {
requestManager.load(R.drawable.ic_volume_off)
.into(volumeControl);
} else if (volumeState == VolumeState.ON) {
requestManager.load(R.drawable.ic_volume_on)
.into(volumeControl);
}
volumeControl.animate().cancel();
volumeControl.setAlpha(1f);
volumeControl.animate()
.alpha(0f)
.setDuration(600).setStartDelay(1000);
}
}
public void setMediaObjects(ArrayList<MediaObject> mediaObjects) {
this.mediaObjects = mediaObjects;
}
/**
* Volume ENUM
*/
private enum VolumeState {
ON, OFF
}
}

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

3. Write an adapter for holding list item

Before moving ahead we need to write some helper class for RecyclerView adapter,

3.1 Create a model class with getter setter

Go to the src folder and create the model folder for separating all model classes. Inside this folder create a POJO class named MediaObject.java. That has getter and setter of title and cover image URL and media URL

package com.androidwave.exoplayer.model;
/**
* Created by Morris on 03,June,2019
*/
public class MediaObject {
private int uId;
private String title;
private String mediaUrl;
private String mediaCoverImgUrl;
private String userHandle;
public String getUserHandle() {
return userHandle;
}
public void setUserHandle(String mUserHandle) {
this.userHandle = mUserHandle;
}
public int getId() {
return uId;
}
public void setId(int uId) {
this.uId = uId;
}
public String getTitle() {
return title;
}
public void setTitle(String mTitle) {
this.title = mTitle;
}
public String getUrl() {
return mediaUrl;
}
public void setUrl(String mUrl) {
this.mediaUrl = mUrl;
}
public String getCoverUrl() {
return mediaCoverImgUrl;
}
public void setCoverUrl(String mCoverUrl) {
this.mediaCoverImgUrl = mCoverUrl;
}
}
3.2 Prepare an XML layout for media list item

Create a layout file inside the res => layout folder named is layout_media_list_item.xml, which holds the view of the list item. So add some view component in this layout file.

<?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="wrap_content"
android:layout_margin="8dp"
android:background="@color/white"
>
<TextView
android:id="@+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:letterSpacing="-0.02"
android:lineSpacingExtra="5sp"
android:textColor="@color/navy"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Was india better then south africa in current serise?"
/>
<ImageView
android:id="@+id/imageView"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toTopOf="@+id/mediaContainer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvTitle"
app:srcCompat="@drawable/ic_user"
/>
<TextView
android:id="@+id/tvUserHandle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:letterSpacing="-0.02"
android:textColor="@color/windows_blue"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="@+id/imageView"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="@+id/imageView"
tools:text="by @morris12"
/>
<FrameLayout
android:id="@+id/mediaContainer"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:adjustViewBounds="true"
android:background="@android:color/black"
android:gravity="center"
android:scaleType="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
>
<ImageView
android:id="@+id/ivMediaCoverImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:scaleType="centerCrop"
android:src="@drawable/cover"
/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
style="?android:attr/progressBarStyle"
/>
<ImageView
android:id="@+id/ivVolumeControl"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="end|bottom"
android:layout_marginBottom="15dp"
android:layout_marginEnd="15dp"
android:alpha="0"
android:animateLayoutChanges="true"
android:scaleType="centerCrop"
android:src="@drawable/ic_volume_on"
/>
</FrameLayout>
</android.support.constraint.ConstraintLayout>
3.4 Write ViewHolder class for representing media list item

Create a new class inside the adapter package named is PlayerViewHolder. This class will extends RecyclerView.ViewHolder. Initialize the all list item inside this view.

package com.androidwave.exoplayer.adapter;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.androidwave.exoplayer.R;
import com.androidwave.exoplayer.model.MediaObject;
import com.bumptech.glide.RequestManager;
/**
* Created by Morris on 03,June,2019
*/
public class PlayerViewHolder extends RecyclerView.ViewHolder {
/**
* below view have public modifier because
* we have access PlayerViewHolder inside the ExoPlayerRecyclerView
*/
public FrameLayout mediaContainer;
public ImageView mediaCoverImage, volumeControl;
public ProgressBar progressBar;
public RequestManager requestManager;
private TextView title, userHandle;
private View parent;
public PlayerViewHolder(@NonNull View itemView) {
super(itemView);
parent = itemView;
mediaContainer = itemView.findViewById(R.id.mediaContainer);
mediaCoverImage = itemView.findViewById(R.id.ivMediaCoverImage);
title = itemView.findViewById(R.id.tvTitle);
userHandle = itemView.findViewById(R.id.tvUserHandle);
progressBar = itemView.findViewById(R.id.progressBar);
volumeControl = itemView.findViewById(R.id.ivVolumeControl);
}
void onBind(MediaObject mediaObject, RequestManager requestManager) {
this.requestManager = requestManager;
parent.setTag(this);
title.setText(mediaObject.getTitle());
userHandle.setText(mediaObject.getUserHandle());
this.requestManager
.load(mediaObject.getCoverUrl())
.into(mediaCoverImage);
}
}
3.5 Complete the RecyclerView Adapter

Doing that create an adapter class named is MediaRecyclerAdapter and paste below code

package com.androidwave.exoplayer.adapter;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.androidwave.exoplayer.R;
import com.androidwave.exoplayer.model.MediaObject;
import com.bumptech.glide.RequestManager;
import java.util.ArrayList;
/**
* Created by Morris on 03,June,2019
*/
public class MediaRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<MediaObject> mediaObjects;
private RequestManager requestManager;
public MediaRecyclerAdapter(ArrayList<MediaObject> mediaObjects,
RequestManager requestManager) {
this.mediaObjects = mediaObjects;
this.requestManager = requestManager;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return new PlayerViewHolder(
LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.layout_media_list_item, viewGroup, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
((PlayerViewHolder) viewHolder).onBind(mediaObjects.get(i), requestManager);
}
@Override
public int getItemCount() {
return mediaObjects.size();
}
}

4. Do followings operation in MainActivity

All part is done, Now come to activity part.

4.1 Lets add ExoPlayerRecyclerView inside the XML file

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

<?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="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
>
<com.androidwave.exoplayer.ui.ExoPlayerRecyclerView
android:id="@+id/exoPlayerRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#dedede"
android:dividerHeight="8dp"
/>
</android.support.constraint.ConstraintLayout>
4.2 Prepare demo content

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);
}

4.3 Finally MainActivity source code 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.MediaRecyclerAdapter;
import com.androidwave.exoplayer.model.MediaObject;
import com.androidwave.exoplayer.ui.ExoPlayerRecyclerView;
import com.androidwave.exoplayer.utils.DividerItemDecoration;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.request.RequestOptions;
import java.util.ArrayList;
import static android.widget.LinearLayout.VERTICAL;
/**
* Created by Morris on 03,June,2019
*/
public class MainActivity extends AppCompatActivity {
ExoPlayerRecyclerView mRecyclerView;
private ArrayList<MediaObject> mediaObjectList = new ArrayList<>();
private MediaRecyclerAdapter mAdapter;
private boolean firstTime = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
// Prepare demo content
prepareVideoList();
//set data object
mRecyclerView.setMediaObjects(mediaObjectList);
mAdapter = new MediaRecyclerAdapter(mediaObjectList, initGlide());
//Set Adapter
mRecyclerView.setAdapter(mAdapter);
if (firstTime) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
mRecyclerView.playVideo(false);
}
});
firstTime = false;
}
}
private void initView() {
mRecyclerView = findViewById(R.id.exoPlayerRecyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, VERTICAL, false));
Drawable dividerDrawable = ContextCompat.getDrawable(this, R.drawable.divider_drawable);
mRecyclerView.addItemDecoration(new DividerItemDecoration(dividerDrawable));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
}
private RequestManager initGlide() {
RequestOptions options = new RequestOptions();
return Glide.with(this)
.setDefaultRequestOptions(options);
}
@Override
protected void onDestroy() {
if (mRecyclerView != null) {
mRecyclerView.releasePlayer();
}
super.onDestroy();
}
private void prepareVideoList() {
MediaObject mediaObject = new MediaObject();
mediaObject.setId(1);
mediaObject.setUserHandle("@h.pandya");
mediaObject.setTitle(
"Do you think the concept of marriage will no longer exist in the future?");
mediaObject.setCoverUrl(
"https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-1.png");
mediaObject.setUrl("https://androidwave.com/media/androidwave-video-1.mp4");
MediaObject mediaObject2 = new MediaObject();
mediaObject2.setId(2);
mediaObject2.setUserHandle("@hardik.patel");
mediaObject2.setTitle(
"If my future husband doesn't cook food as good as my mother should I scold him?");
mediaObject2.setCoverUrl(
"https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-2.png");
mediaObject2.setUrl("https://androidwave.com/media/androidwave-video-2.mp4");
MediaObject mediaObject3 = new MediaObject();
mediaObject3.setId(3);
mediaObject3.setUserHandle("@arun.gandhi");
mediaObject3.setTitle("Give your opinion about the Ayodhya temple controversy.");
mediaObject3.setCoverUrl(
"https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-3.png");
mediaObject3.setUrl("https://androidwave.com/media/androidwave-video-3.mp4");
MediaObject mediaObject4 = new MediaObject();
mediaObject4.setId(4);
mediaObject4.setUserHandle("@sachin.patel");
mediaObject4.setTitle("When did kama founders find sex offensive to Indian traditions");
mediaObject4.setCoverUrl(
"https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-4.png");
mediaObject4.setUrl("https://androidwave.com/media/androidwave-video-6.mp4");
MediaObject mediaObject5 = new MediaObject();
mediaObject5.setId(5);
mediaObject5.setUserHandle("@monika.sharma");
mediaObject5.setTitle("When did you last cry in front of someone?");
mediaObject5.setCoverUrl(
"https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-5.png");
mediaObject5.setUrl("https://androidwave.com/media/androidwave-video-5.mp4");
mediaObjectList.add(mediaObject);
mediaObjectList.add(mediaObject2);
mediaObjectList.add(mediaObject3);
mediaObjectList.add(mediaObject4);
mediaObjectList.add(mediaObject5);
mediaObjectList.add(mediaObject);
mediaObjectList.add(mediaObject2);
mediaObjectList.add(mediaObject3);
mediaObjectList.add(mediaObject4);
mediaObjectList.add(mediaObject5);
}
}








Post a Comment

0 Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.