У меня есть задача сделать воспроизведение видео в recyclerview
, использовать exoplayer и использовать эту ссылку :
Я следую учебник от Droid wafe:
и код очень хорошо работает в моем приложении. но. есть небольшая проблема. проблема в том, что я вызываю функцию notifyItemChange
(положение), чтобы обновить элемент в videoViewHolder. видео остановилось, но звук все еще работает в фоновом режиме.
Я думаю, что проблема в том, когда вызывается notifyItemChange
, повторное создание держателя просмотра видео и приостановка видео (по умолчанию), но звук продолжает работать, потому что не вызывается setplaywhenready(false)
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.google.android.exoplayer2.*;
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.*;
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 es.hyrax.zonapets.R;
import es.hyrax.zonapets.data.network.model.post.Post;
import es.hyrax.zonapets.ui.user_private.home.dashboard_my_zona.viewholder_myzona.ViewHolderImages;
import es.hyrax.zonapets.ui.user_private.home.dashboard_my_zona.viewholder_myzona.ViewHolderMultipleImages;
import es.hyrax.zonapets.ui.user_private.home.dashboard_my_zona.viewholder_myzona.ViewHolderVideos;
import es.hyrax.zonapets.utils.listener.OnDoubleClickListener;
import java.util.ArrayList;
import java.util.Objects;
public class ExoPlayerRecyclerViewMyZona extends RecyclerView {
private static final String TAG = "ExoPlayerRecyclerView";
private static final String AppName = "ZonaPets";
/**
* PlayerViewHolder UI component
* Watch PlayerViewHolder class
*/
private ImageView mediaCoverImage, volumeControl;
private FrameLayout container_volume;
private ProgressBar progressBar;
private View viewHolderParent;
private AspectRatioFrameLayout mediaContainer;
private PlayerView videoSurfaceView;
private SimpleExoPlayer videoPlayer;
/**
* variable declaration
*/
// Media List
private ArrayList<Post> 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 int targetPosition;
private OnEventClickVideoViewHolder onEventClick;
public ExoPlayerRecyclerViewMyZona(@NonNull Context context) {
super(context);
init(context);
}
public ExoPlayerRecyclerViewMyZona(@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 containerView.
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);
}
if (volumeControl != null){
volumeControl.setAlpha(1f);
}
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 (volumeControl != null){
volumeControl.setAlpha(1f);
}
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);
//set get target position to this target position
this.targetPosition = targetPosition;
int currentPosition =
targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
View child = getChildAt(currentPosition);
if (child == null) {
return;
}
if (child.getTag() instanceof ViewHolderVideos) {
ViewHolderVideos holder = (ViewHolderVideos) child.getTag();
if (holder == null) {
playPosition = -1;
return;
}
mediaCoverImage = holder.img_post;
progressBar = holder.progressBar;
volumeControl = holder.volumeControl;
viewHolderParent = holder.itemView;
container_volume = holder.container_volume;
requestManager = holder.requestManager;
mediaContainer = holder.mediaContainer;
videoSurfaceView.setPlayer(videoPlayer);
mediaContainer.setOnClickListener(new OnDoubleClickListener() {
@Override
public void onDoubleClick(View v) {
onEventClick.onDoubleClickListener(targetPosition);
}
@Override
public void onSingleClick(View v) {
onEventClick.onSingleClickListener(targetPosition);
}
});
container_volume.setOnClickListener(new OnDoubleClickListener() {
@Override
public void onDoubleClick(View v) {
}
@Override
public void onSingleClick(View v) {
toggleVolume();
}
});
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(
context, Util.getUserAgent(context, AppName));
String mediaUrl = mediaObjects.get(targetPosition).getImagepost().get(0).getVideo();/*"https://androidwave.com/media/androidwave-video-1.mp4";*/
if (mediaUrl != null) {
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(mediaUrl));
videoPlayer.prepare(videoSource);
videoPlayer.setPlayWhenReady(true);
}
}else {
videoPlayer.setPlayWhenReady(false);
}
}
/**
* 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);
volumeControl.setVisibility(VISIBLE);
}
private void resetVideoView() {
if (isVideoViewAdded) {
removeVideoView(videoSurfaceView);
playPosition = -1;
videoSurfaceView.setVisibility(INVISIBLE);
mediaCoverImage.setVisibility(VISIBLE);
volumeControl.setVisibility(VISIBLE);
}
}
private Boolean currentPositionIsVideoType(){
int currentPosition =
targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
View child = getChildAt(currentPosition);
if (child == null) {
return false;
}
if (child.getTag() instanceof ViewHolderVideos) {
return true;
}else {
return false;
}
}
public void releasePlayer() {
if (currentPositionIsVideoType()) {
if (videoPlayer != null) {
videoPlayer.release();
videoPlayer = null;
}
viewHolderParent = null;
}
}
public void onPausePlayer() {
if (currentPositionIsVideoType()){
if (videoPlayer != null) {
//videoPlayer.stop(true);
videoPlayer.setPlayWhenReady(false);
videoPlayer.getPlaybackState();
}
}
}
public void resumePlayer() {
if (currentPositionIsVideoType()){
if (videoPlayer != null) {
videoPlayer.setPlayWhenReady(true);
videoPlayer.getPlaybackState();
}
}
}
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(1f)
.setDuration(600).setStartDelay(1000);
}
}
public void setMediaObjects(ArrayList<Post> mediaObjects) {
this.mediaObjects = mediaObjects;
}
public void setVideoViewHolderClickListener(OnEventClickVideoViewHolder onEventClick){
this.onEventClick = onEventClick;
}
/**
* Volume ENUM
*/
private enum VolumeState {
ON, OFF
}
public interface OnEventClickVideoViewHolder {
void onSingleClickListener(int position);
void onDoubleClickListener(int position);
}
}
и журнал cat не показывает никаких ошибок !!
пожалуйста, посмотрите мой комментарий в отчете о проблеме , чтобы помочь понять