Saltar al contenido principal

Ejemplo de Android RecyclerView con Exoplayer Video

Este es un ejemplo de cómo listar videos en un recyclerview usando el popular Exoplayer VideoPlayer.

Aquí está la demostración:

Android RecyclerView con Exoplayer Video Example

Paso 1: Crear Proyecto

Comience por crear un proyecto vacío de AndroidStudio.

Paso 2: Dependencias

En este ejemplo se utilizan varias dependencias de terceros, incluyendo Glide para cargar las capturas de pantalla de vídeo, y exoplayer para reproducir los vídeos.

Aquí están las dependencias:

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.google.android.exoplayer:exoplayer:2.10.8'
implementation 'org.jsoup:jsoup:1.10.3'

implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha03'

}

También necesitarás habilitar Java8 dentro del mismo archivo app/build.gradle;

    compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

Paso 2: Diseñar diseños

En este proyecto se utilizan dos diseños, el diseño del elemento recyclerview así como el diseño de la MainActivity.

(a). layout_media_list_item.xml

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

<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"
/>

<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>

</androidx.constraintlayout.widget.ConstraintLayout>

**Actividad_principal.xml

Añade aquí el ExoPlayerRecyclerView personalizado:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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.example.exoplayer.utils.ExoPlayerRecyclerView
android:id="@+id/exoPlayerRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#dedede"
android:dividerHeight="8dp"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

Paso 3: Crear el objeto modelo

A continuación, cree un objeto multimedia que represente un solo vídeo. Cada uno de estos objetos ocupará una fila en nuestro ExoplayerRecyclerView personalizado.

MediaObject.java

package com.example.exoplayer.model;

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

Paso 4: Crear una Reyclerview personalizada

Crear un ExoplayerRecyclerView personalizado capaz de listar nuestros vídeos:

ExoplayerRecyclerView.java

package com.example.exoplayer.utils;

import android.content.Context;
import android.graphics.Point;
import android.net.Uri;
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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.RequestManager;
import com.example.exoplayer.R;
import com.example.exoplayer.adapter.PlayerViewHolder;
import com.example.exoplayer.model.MediaObject;
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;

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 View.OnClickListener videoViewClickListener = new View.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) 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);
}
}
}

//public void onRestartPlayer() {
// if (videoPlayer != null) {
// playVideo(true);
// }
//}

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

Paso 5: Crear el adaptador RecyclerView

Comienza creando el ViewHolder de RecyclerView:

(a). PlayerViewHolder.java

package com.example.exoplayer.adapter;

import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.RequestManager;
import com.example.exoplayer.R;
import com.example.exoplayer.model.MediaObject;

/**
* Created on : May 24, 2019
* Author : AndroidWave
*/
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);
}
}

(b). MediaRecyclerAdapter.java

package com.example.exoplayer.adapter;

import android.view.LayoutInflater;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.RequestManager;
import com.example.exoplayer.R;
import com.example.exoplayer.model.MediaObject;

import java.util.ArrayList;

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

Paso 6: Crear MainActivity

Poner todo junto en la clase MainActivity, incluyendo la adición de los vídeos a la recyclerview:

MainActivity.java

package com.example.exoplayer;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;

import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.request.RequestOptions;
import com.example.exoplayer.adapter.MediaRecyclerAdapter;
import com.example.exoplayer.model.MediaObject;
import com.example.exoplayer.utils.ExoPlayerRecyclerView;

import java.util.ArrayList;

import static android.widget.LinearLayout.VERTICAL;

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);
mRecyclerView.smoothScrollToPosition(1);
//mRecyclerView.smoothScrollToPosition(1);
/* 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));
}

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("User 1");
mediaObject.setTitle(
"Item 1");
mediaObject.setCoverUrl(
"https://www.muscleandfitness.com/wp-content/uploads/2019/04/7-Demonized-BodyBuilding-Food-Gallery.jpg?w=940&h=529&crop=1");
mediaObject.setUrl("https://s3.ca-central-1.amazonaws.com/codingwithmitch/media/VideoPlayerRecyclerView/Sending+Data+to+a+New+Activity+with+Intent+Extras.mp4");

MediaObject mediaObject2 = new MediaObject();
mediaObject2.setId(2);
mediaObject2.setUserHandle("user 2");
mediaObject2.setTitle(
"Item 2");
mediaObject2.setCoverUrl(
"https://www.muscleandfitness.com/wp-content/uploads/2019/04/7-Demonized-BodyBuilding-Food-Gallery.jpg?w=940&h=529&crop=1");
mediaObject2.setUrl("https://s3.ca-central-1.amazonaws.com/codingwithmitch/media/VideoPlayerRecyclerView/Sending+Data+to+a+New+Activity+with+Intent+Extras.mp4");

MediaObject mediaObject3 = new MediaObject();
mediaObject3.setId(3);
mediaObject3.setUserHandle("User 3");
mediaObject3.setTitle("Item 3");
mediaObject3.setCoverUrl(
"https://www.muscleandfitness.com/wp-content/uploads/2019/04/7-Demonized-BodyBuilding-Food-Gallery.jpg?w=940&h=529&crop=1");
mediaObject3.setUrl("https://s3.ca-central-1.amazonaws.com/codingwithmitch/media/VideoPlayerRecyclerView/Sending+Data+to+a+New+Activity+with+Intent+Extras.mp4");

MediaObject mediaObject4 = new MediaObject();
mediaObject4.setId(4);
mediaObject4.setUserHandle("User 4");
mediaObject4.setTitle("Item 4");
mediaObject4.setCoverUrl(
"https://www.muscleandfitness.com/wp-content/uploads/2019/04/7-Demonized-BodyBuilding-Food-Gallery.jpg?w=940&h=529&crop=1");
mediaObject4.setUrl("https://s3.ca-central-1.amazonaws.com/codingwithmitch/media/VideoPlayerRecyclerView/Sending+Data+to+a+New+Activity+with+Intent+Extras.mp4");

MediaObject mediaObject5 = new MediaObject();
mediaObject5.setId(5);
mediaObject5.setUserHandle("User 5");
mediaObject5.setTitle("Item 5");
mediaObject5.setCoverUrl(
"https://www.muscleandfitness.com/wp-content/uploads/2019/04/7-Demonized-BodyBuilding-Food-Gallery.jpg?w=940&h=529&crop=1");
mediaObject5.setUrl("https://s3.ca-central-1.amazonaws.com/codingwithmitch/media/VideoPlayerRecyclerView/Sending+Data+to+a+New+Activity+with+Intent+Extras.mp4");

mediaObjectList.add(mediaObject);
mediaObjectList.add(mediaObject2);
mediaObjectList.add(mediaObject3);
mediaObjectList.add(mediaObject4);
mediaObjectList.add(mediaObject5);
}
}

Paso 7: Añadir el permiso de Internet

Añade el permiso de Internet a tu AndroidManifest.xml:\N

    <uses-permission android:name="android.permission.INTERNET" />

Ejecutar

Copia el código o descárgalo en el siguiente enlace, construye y ejecuta.

Referencia

Aquí están los enlaces de referencia:

Descargar Ejemplo