简书链接:androidrecyclerview列表播放视频视频全屏翻页自动播放高仿微视之路 文章字数:1154,阅读全文大约需要4分钟 我这文章暂时是打广告的,要使用列表播放的朋友
http://github.com/qssq/videoplayer 视频效果http://www.iqiyi.com/w_19rxx2xtg5.html
要做2件事情,第一 要监听recyclerview的滚动状态变为空闲的时候寻找videoview 进行播放
第二 ,要再recyclerview的布局改变的时候也寻找videoview进行播放
第三 让视频无缝无白屏闪屏播放
所以我就不用国内的这些第三方了,感觉摸不着头脑,y因为搞不到为何findview之后调用播放按钮竟然毛线反应没有,
于是找到了一个国外的,但是有很多坑 我修改了好几天,解决了白闪问题,增加了缓冲的显示,增加了点击播放暂停的控制
使用方法
1 compile 'cn.qssq666:video-player-manager:0.5'
如果多个界面都有列表视频播放 mVideoPlayerManager
那么在界面不可见的时候各位要进行暂停或者销毁 创建单例管理器
fragment或activity的处理 构建管理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 mVideoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() { @Override public void onPlayerItemChanged(MetaData metaData) { } } ) { @Override public void onVideoPlayTimeChanged(int positionInMilliseconds) { float duration = mVideoPlayerManager.getCurrentPlayer().getDuration(); int percent = (int) (positionInMilliseconds / duration * 100f); // int shouldSetWidth = (int) ((getWidth() / (float) mMaxProgress) * progress); if (BuildConfig.DEBUG) { if (binding != null) { binding.progressBar.setProgress(percent); } Log.w(TAG, "percent:" + percent); } } @Override public void onPrepare() { binding.progressBar.setProgress(0); } };
监听recyclerview的滚动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 binding.viewpager.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { int actualCurrentPosition = 0; if (newState == RecyclerView.SCROLL_STATE_IDLE) { //第一次没法解决 autoPlayVideo(binding.viewpager);//每次滚动一页就自动播放 } } }); //解决第一次打开界面不播放问题 binding.viewpager.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { autoPlayVideo(binding.viewpager); Log.w(TAG,"V"+left+",top:"+top+",right:"+right+",bottom:"+bottom+",oldLeft:"+oldLeft+",oldTop:"+oldTop+",oldRight:"+oldRight); } });
自动播放的具体逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 private void autoPlayVideo(RecyclerView view) { RecyclerView.LayoutManager layoutManager = view.getLayoutManager(); int actualCurrentPosition = binding.viewpager.getCurrentPosition(); MetaData metaData = new MetaData() { }; GenericDataBindViewHolder<ViewItemSmallVideoBinding> holder = (GenericDataBindViewHolder<ViewItemSmallVideoBinding>) binding.viewpager.findViewHolderForLayoutPosition(actualCurrentPosition); if (holder == null) { Log.e(TAG,"找不到viewholder"); return; } SmallVideoModel model1 = getAdapter().getData().get(actualCurrentPosition); if (mVideoPlayerManager.isCurrent(model1.getVideo())) { if(BuildConfig.DEBUG){ Log.w(TAG,"是当前视频,忽略"); } return; } holder.getBinding().progressbar.setProgress(0);// holder.getBinding().progressWrap.setVisibility(View.VISIBLE); holder.getBinding().btnStart.setVisibility(View.GONE); SmallVideoModel model = getAdapter().getData().get(actualCurrentPosition); mVideoPlayerManager.playNewVideo(metaData, holder.getBinding().videoPlayer2, model.getVideo()); }
页面声明周期控制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Override public void onDestroy() { super.onDestroy(); if (mVideoPlayerManager != null) { mVideoPlayerManager.stopAnyPlayback(); } } @Override public void onResume() { super.onResume(); if (mFromActivity) { return; } Log.w(TAG, "PLAYSTEATE:" + mVideoPlayerManager.getCurrentPlayerState()); mVideoPlayerManager.continuePlay(); } @Override public void onPause() { super.onPause(); if (mFromActivity) { return; } Log.w(TAG, "PLAYSTEATE:" + mVideoPlayerManager.getCurrentPlayerState()); mVideoPlayerManager.pause(); }
列表适配器的处理 视频列表view布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="model" type="cn.qssq666.bean.SmallVideoModel" /> </data> <FrameLayout android:id="@+id/video_player_root" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- <cn.jzvd.JZVideoPlayerStandard android:id="@+id/videoplayer" android:layout_width="match_parent" android:layout_height="20dp" />--> <cn.qssq666.videomanager.ui.VideoPlayerView android:id="@+id/video_player_2" android:layout_width="match_parent" android:layout_height="match_parent" /> <ImageView android:id="@+id/iv" android:layout_width="match_parent" android:layout_height="match_parent" android:focusable="false" android:focusableInTouchMode="false" android:scaleType="centerCrop" app:image="@{model.image}" /> <View android:id="@+id/video_player_mask" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> </LinearLayout> <LinearLayout android:id="@+id/progress_wrap" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:orientation="vertical" android:visibility="gone"> <!-- 缓冲进度 --> <ProgressBar android:id="@+id/progressbar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:indeterminateTint="@color/theme_color_red" android:progress="50" /> <TextView android:id="@+id/progress_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:text="0%" android:textColor="@color/theme_color_red" android:textSize="15sp" /> </LinearLayout> <ImageView android:id="@+id/btn_start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/video_big_btn_paly" /> </FrameLayout> </layout>
监听视频透明封面的点击 请注意,这里播放视频是用管理器去操作播放,而不是通过布局的videoPlayer2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 View.OnClickListener l = new View.OnClickListener() { @Override public void onClick(View v) { if (mVideoPlayerManager.isCurrent(model.getVideo())) { if (mVideoPlayerManager.isPause()) { mVideoPlayerManager.continuePlay(); } else if (mVideoPlayerManager.isPlay()) { mVideoPlayerManager.pause(); } else { mVideoPlayerManager.playNewVideo(model, binding.videoPlayer2, model.getVideo()); } } else { binding.btnStart.setVisibility(View.GONE); binding.progressWrap.setVisibility(View.VISIBLE);binding.progressText.setText("0%"); mVideoPlayerManager.playNewVideo(model, binding.videoPlayer2, model.getVideo()); } } }; binding.videoPlayerMask.setOnClickListener(l);
添加监听 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 binding.videoPlayer2.addMediaPlayerListener(new MediaPlayerWrapper.MainThreadMediaPlayerListener() { @Override public void onVideoSizeChangedMainThread(int width, int height) { } @Override public void onVideoPreparedMainThread() { } //作废 @Override public void onProgressUpdate(int percent) { // binding.seekbar.setProgress(); } @Override public void onVideoCompletionMainThread() { binding.videoPlayer2.restart();//播放完毕重新开始 } @Override public void onErrorMainThread(int what, int extra) { binding.progressWrap.setVisibility(View.GONE); binding.btnStart.setVisibility(View.VISIBLE);//这里少写了一个,应该让封面也显示出来 } @Override public void onBufferingUpdateMainThread(int percent) { if (percent == 100) { if (binding.progressWrap.getVisibility() == View.VISIBLE) { binding.progressWrap.setVisibility(View.GONE); } } else { if (binding.progressWrap.getVisibility() == View.GONE) { binding.progressWrap.setVisibility(View.VISIBLE); } binding.progressbar.setProgress(percent); binding.progressText.setText("" + percent + "%"); } } @Override public void onVideoStoppedMainThread() { // Show the cover when video stopped binding.iv.setVisibility(View.VISIBLE); binding.btnStart.setVisibility(View.VISIBLE); binding.progressWrap.setVisibility(View.GONE); } @Override public void onPrepared(MediaPlayer mp) { // binding.ivStart.setVisibility(View.g); } @Override public void onPrepare() { binding.progressWrap.setVisibility(View.VISIBLE); binding.btnStart.setVisibility(View.GONE);//即将要请求网络,说明有人调用了播放了.这一般是耗时的,第一次播放一个数据源才回调 } @Override public void onVideoPausedMainThread() { binding.iv.setVisibility(View.VISIBLE); binding.progressWrap.setVisibility(View.GONE); binding.btnStart.setVisibility(View.VISIBLE);//即将要请求网络,说明有人调用了播放了.这一般是耗时的,第一次播放一个数据源才回调 } @Override public void onVideoStartedMainThread() { binding.btnStart.setVisibility(View.GONE);//即将要请求网络,说明有人调用了播放了.这一般是耗时的,第一次播放一个数据源才回调 } @Override public void onInfo(MediaPlayer mp, int what, int extra) { if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {//解决白屏问题 binding.iv.setVisibility(View.INVISIBLE); binding.progressWrap.setVisibility(View.GONE); } } });
这里不会泄露,因为每次滑动到下一个视频,那么上一个视频的所有监听都会被移除、销毁。
ok列表播放的就到这里了,但是可能还会有一些体验需要优化,特别是处理继续播放的时候。 我这是高仿微视,他那个整个播放进度是在一个tab选项卡的上面,当缓冲的时候是进度从中间往两边散开,循环模式,所以播放进度这里不需要单独再写,而是在上面的管理器代码里面控制fragmet进度横条的显示,要知道整个进度特效如何实现的朋友可以看看我的github
的allproject 目录导航
2018-4-30 17:41:34 其实这个项目还有很多不足,毕竟只是实现了列表播放,为了做优化,我的demo做了一个可显示进度显示时间,可暂停播放继续播放的 demo.可以去github下载看看。和大部分播放器一样,在界面销毁的时候调用销毁方法就可以解决泄露问题了