我已经实现了使用纹理视图和媒体播放器实现的视频播放器的Recycler视图。
如果我向下滚动列表,我可以点击该项目并播放视频。但是,一旦视图离开屏幕,使用回收器视图,然后将其回收再利用。如果我向后滚动,所有视图现在都是空白的(黑色)。
我希望添加一些功能,当用户在屏幕上滚动视频时它会暂停并保持对该视频的引用,这样如果它们滚动回该视频,它将从该点播放。
我已经检查过this但是我不想下载视频,我只是想要流式传输。我不是在寻找有人为我这样做,我只是在寻找一些指示,并希望有人可以分享他们对此的了解...... 提前致谢
这是我到目前为止所做的:
视频播放器
public class CustomVideoPlayer implements TextureView.SurfaceTextureListener, VideoControllerView.MediaPlayerControl, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnVideoSizeChangedListener {
private Context mContext;
private String mUrl;
private MediaPlayer mMediaPlayer;
private Surface mSurface;
private VideoControllerView mControllerView;
private TextureView mTextureView;
private CardView mCardView;
private ProgressBar mProgress;
private FrameLayout mView;
private RelativeLayout mLayout;
public CustomVideoPlayer(Context ctx, TextureView view, ProgressBar progressDialog, FrameLayout holderView){
this.mContext = ctx;
mTextureView = view;
mTextureView.setSurfaceTextureListener(this);
mProgress = progressDialog;
mControllerView = new VideoControllerView(ctx);
mView = holderView;
mTextureView.setOnTouchListener(new ControlTouchListener());
}
@Override
public boolean canPause() {
return true;
}
@Override
public boolean canSeekBackward() {
return true;
}
@Override
public boolean canSeekForward() {
return true;
}
@Override
public int getBufferPercentage() {
return 0;
}
@Override
public int getCurrentPosition() {
return mMediaPlayer.getCurrentPosition();
}
@Override
public int getDuration() {
return mMediaPlayer.getDuration();
}
@Override
public boolean isPlaying() {
return mMediaPlayer.isPlaying();
}
@Override
public void pause() {
mMediaPlayer.pause();
}
@Override
public void seekTo(int i) {
mMediaPlayer.seekTo(i);
}
@Override
public void start() {
mMediaPlayer.start();
}
@Override
public boolean isFullScreen() {
return false;
}
@Override
public void toggleFullScreen() {
}
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
}
@Override
public void onCompletion(MediaPlayer mp) {
}
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mSurface = new Surface(surface);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
public void changePlayState(){
if(mMediaPlayer.isPlaying()){
mMediaPlayer.pause();
}else{
mMediaPlayer.start();
}
}
public void startVideo(String url){
if(mMediaPlayer!=null){
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = new MediaPlayer();
}else{
mMediaPlayer = new MediaPlayer();
}
if(!mMediaPlayer.isPlaying()){
try {
mMediaPlayer.setSurface(mSurface);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource(url);
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
mMediaPlayer.setOnPreparedListener(this);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onPrepared(MediaPlayer mp) {
Log.i(VersysVideoPlayer.class.getSimpleName(), "ON PREPARED CALLED");
mControllerView.setMediaPlayer(this);
mControllerView.setAnchorView(mView);
mControllerView.show();
mProgress.setVisibility(View.GONE);
mMediaPlayer.start();
}
//Touch listener to display video controls
class ControlTouchListener implements View.OnTouchListener{
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
mControllerView.show();
}
return false;
}
}
}
活性/ ADAPTER
public class VideoViewListActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_view_list);
//Create instance of Recycler view
final RecyclerView videoList = (RecyclerView) findViewById(R.id.feed_list);
LinearLayoutManager llm = new LinearLayoutManager(this);
videoList.setLayoutManager(llm);
videoList.setHasFixedSize(true);
final List<String> list = new ArrayList<>();
list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
list.add("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
final VideoAdapter adapter = new VideoAdapter(list, this);
videoList.setAdapter(adapter);
videoList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
TextureView view = adapter.getVideoPlayer();
Log.i("PERCENTAGE VISIBLE: ", String.valueOf(getVisiblePercent(adapter.getVideoPlayer())));
if(getVisiblePercent(view)==100) {
return;
}
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_video_view_list, menu);
return true;
}
public static int getVisiblePercent(View v) {
if (v.isShown()) {
Rect r = new Rect();
v.getGlobalVisibleRect(r);
double sVisible = r.width() * r.height();
double sTotal = v.getWidth() * v.getHeight();
return (int) (100 * sVisible / sTotal);
} else {
return -1;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* Recycler View Adapter
*/
class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoFeedHolder> {
public TextureView mPreview;
private CardView mCardView;
private List<String> mUrls;
private Context mContext;
private Surface mSurface;
VideoControllerView controller;
private View mAnchor;
private ProgressBar mProgressDialog;
private ImageView mHolder;
private int mPosition;
private VersysVideoPlayer mVideoPlayer;
OnItemClickListener mItemClickListener;
public VideoAdapter(List<String> url, Context ctx) {
mUrls = url;
mContext = ctx;
controller = new VideoControllerView(ctx);
}
@Override
public VideoAdapter.VideoFeedHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.video_item_feed, viewGroup, false);
VideoFeedHolder holder = new VideoFeedHolder(v);
return holder;
}
@Override
public void onBindViewHolder(final VideoFeedHolder videoFeedHolder, final int i) {
final VersysVideoPlayer videoPlayer = new VersysVideoPlayer(mContext, videoFeedHolder.mTexturePreview, mProgressDialog, videoFeedHolder.controlHolder);
videoFeedHolder.placeholder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
videoPlayer.startVideo(mUrls.get(i));
videoFeedHolder.placeholder.setVisibility(View.GONE);
videoFeedHolder.bar.setVisibility(View.VISIBLE);
}
});
mPosition = i;
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
public String getUrl() {
return mUrls.get(mPosition);
}
@Override
public int getItemCount() {
return mUrls.size();
}
public TextureView getVideoPlayer() {
return mPreview;
}
public class VideoFeedHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextureView mTexturePreview;
ProgressBar bar;
ImageView placeholder;
FrameLayout controlHolder;
RelativeLayout touchLayout;
public VideoFeedHolder(View itemView) {
super(itemView);
mTexturePreview = (TextureView) itemView.findViewById(R.id.video_player);
mPreview = mTexturePreview;
mCardView = (CardView) itemView.findViewById(R.id.cv);
bar = (ProgressBar)itemView.findViewById(R.id.buffereing);
placeholder = (ImageView) itemView.findViewById(R.id.holder);
mProgressDialog = bar;
controlHolder = (FrameLayout) itemView.findViewById(R.id.media_controller_anchor);
}
@Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(v, getAdapterPosition());
}
}
}
}
}
视频订阅项XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="400dp"
android:id="@+id/cv">
<RelativeLayout
android:id="@+id/anchor"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/detail_layout"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/profile_pic"
android:background="@drawable/profiler"
android:layout_marginLeft="10dp"
android:layout_width="50dp"
android:layout_height="50dp" />
<TextView
android:id="@+id/user_name"
android:layout_alignTop="@+id/profile_pic"
android:layout_toRightOf="@+id/profile_pic"
android:text="Joe Bloggs"
android:layout_marginLeft="10dp"
android:textColor="#000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/date"
android:layout_below="@+id/user_name"
android:layout_toRightOf="@+id/profile_pic"
android:layout_marginLeft="10dp"
android:text="10 Aug 2015"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/desc"
android:layout_below="@+id/profile_pic"
android:layout_marginLeft="10dp"
android:text="This a sample video of a bird getting hit on the head and a rabbit waking from a nap!!"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<RelativeLayout
android:layout_below="@+id/detail_layout"
android:layout_margin="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextureView
android:id="@+id/video_player"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/media_controller_anchor"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
<ImageView
android:id="@+id/holder"
android:layout_centerInParent="true"
android:background="@drawable/default_video_poster"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/buffereing"
android:visibility="gone"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
答案 0 :(得分:2)
通过在适配器中覆盖onViewAttachedToWindow(VH holder)
和onViewDetachedFromWindow(VH holder)
,您可以在每个项目进入或退出RecyclerView
的可见区域时得到通知。因此可以保存项目状态。例如,您可以在适配器类中创建一个HashMap<Integer, Long>
,该类保存最近一次播放的时间。这样,我们应该暂停视频并将视频播放时间存储在HashMap
中,并将项目位置作为键在onViewDetachedFromWindow
中。然后在onViewAttachedToWindow
中将其从地图中恢复,然后...
基于文档
当此适配器创建的视图已附加到窗口时,将调用
onViewAttachedToWindow
。
onViewDetachedFromWindow
会在此适配器创建的视图与其窗口分离时被调用。
答案 1 :(得分:0)
我会通过将visibilitylistener添加到recyclers viewholder类来解决这个问题。 Facebook fresco库使用类似的技术来实现其#34; ImageView&#34;实现。
Fresco是开源的,所以很容易检查它是如何完成的。
答案 2 :(得分:0)
查看文档here 在方法
onViewRecycled(RecyclerView.ViewHolder holder)
您可以获得视频播放的时间,稍后当您获得相同的视频时,只需使用保存的值设置播放器时间
答案 3 :(得分:0)
不使用String对象,而是按如下方式创建类:
public class MyVideoObject {
String url;
int seek_position;
}
如 @gropapa 所述,您可以设置
seek_position
方法中的onViewRecycled
。
重新创建视图后,您可以从存储在对象中的seek_position
开始播放。
我写的图书馆会在ViewHolder
可见时播放视频,在部分可见时暂停播放。目前,它不会为已回收的视图存储seek_position
,但会恢复未回收的视频(部分可见)。