1.概述

本人经常刷今日头条,里面的视频模块我觉得做的很好,做Android的嘛,看到好的东西就想怎么实现的。头条的那个视频播放器,只能同时播放同一个视频,点击下一个上一个就停止,觉得这功能不错,决定自己实现一下。

先看一下最后的效果图吧:

2.实现思路

通过mediaPlayer + TextureView实现视频播放,播放,暂停,进度条分别对应MediaPlayer 的start,pause,seekTo方法,这些很简单。为了实现今日头条那样只允许一个视频播放的效果。想的是让MediaPlayer实例只有一个,当点击下一个视频的时候先将MediaPlayer的实例release掉,再置为null,然后重新进行初始化,从而实现单一播放效果。

头条还有一个功能是当前播放的视频滑动出屏幕的时候自动停止播放的功能,这个功能实现的思路是,通过recyclerView的addOnChildAttachStateChangeListener监听,监听接口中会实现onChildViewAttachedToWindow(View view)方法跟onChildViewDetachedFromWindow(View view)这两个方法。第一个监听的是recyclerView的item出现在屏幕中的回调,第二个是item移出屏幕时的回调,我们在第二个方法中,通过传过来的参数view拿到当前item的videoPlayer,调用player.release();方法即可实现视频滑动出屏幕自动停止播放的功能。

3.核心代码

播放器的代码就不贴了,太多,你懂的。。可以去下载完整版的代码

1.管理器代码

public class NiceVideoPlayerManager {private NiceVideoPlayer mVideoPlayer;private NiceVideoPlayerManager() {}private static NiceVideoPlayerManager sInstance;public static synchronized NiceVideoPlayerManager instance() {if (sInstance == null) {sInstance = new NiceVideoPlayerManager();}return sInstance;}public void setCurrentNiceVideoPlayer(NiceVideoPlayer videoPlayer) {mVideoPlayer = videoPlayer;}public void releaseNiceVideoPlayer() {if (mVideoPlayer != null) {mVideoPlayer.release();mVideoPlayer = null;}}public boolean onBackPressd() {if (mVideoPlayer != null) {if (mVideoPlayer.isFullScreen()) {return mVideoPlayer.exitFullScreen();} else if (mVideoPlayer.isTinyWindow()) {return mVideoPlayer.exitTinyWindow();} else {mVideoPlayer.release();return false;}}return false;}

2.播放控制器代码

public class NiceVideoPlayerController extends FrameLayoutimplements View.OnClickListener,SeekBar.OnSeekBarChangeListener {private Context mContext;private NiceVideoPlayerControl mNiceVideoPlayer;private ImageView mImage;private ImageView mCenterStart;private LinearLayout mTop;private ImageView mBack;private TextView mTitle;private LinearLayout mBottom;private ImageView mRestartPause;private TextView mPosition;private TextView mDuration;private SeekBar mSeek;private ImageView mFullScreen;private LinearLayout mLoading;private TextView mLoadText;private LinearLayout mError;private TextView mRetry;private LinearLayout mCompleted;private TextView mReplay;private TextView mShare;private Timer mUpdateProgressTimer;private TimerTask mUpdateProgressTimerTask;private boolean topBottomVisible;private CountDownTimer mDismissTopBottomCountDownTimer;public NiceVideoPlayerController(Context context) {super(context);mContext = context;init();}private void init() {LayoutInflater.from(mContext).inflate(R.layout.nice_video_palyer_controller, this, true);mCenterStart = (ImageView) findViewById(R.id.center_start);mImage = (ImageView) findViewById(R.id.image);mTop = (LinearLayout) findViewById(R.id.top);mBack = (ImageView) findViewById(R.id.back);mTitle = (TextView) findViewById(R.id.title);mBottom = (LinearLayout) findViewById(R.id.bottom);mRestartPause = (ImageView) findViewById(R.id.restart_or_pause);mPosition = (TextView) findViewById(R.id.position);mDuration = (TextView) findViewById(R.id.duration);mSeek = (SeekBar) findViewById(R.id.seek);mFullScreen = (ImageView) findViewById(R.id.full_screen);mLoading = (LinearLayout) findViewById(R.id.loading);mLoadText = (TextView) findViewById(R.id.load_text);mError = (LinearLayout) findViewById(R.id.error);mRetry = (TextView) findViewById(R.id.retry);mCompleted = (LinearLayout) findViewById(R.id.completed);mReplay = (TextView) findViewById(R.id.replay);mShare = (TextView) findViewById(R.id.share);mCenterStart.setOnClickListener(this);mBack.setOnClickListener(this);mRestartPause.setOnClickListener(this);mFullScreen.setOnClickListener(this);mRetry.setOnClickListener(this);mReplay.setOnClickListener(this);mShare.setOnClickListener(this);mSeek.setOnSeekBarChangeListener(this);this.setOnClickListener(this);}public void setTitle(String title) {mTitle.setText(title);}public void setImage(String imageUrl) {Glide.with(mContext).load(imageUrl).placeholder(R.drawable.img_default).crossFade().into(mImage);}public void setImage(@DrawableRes int resId) {mImage.setImageResource(resId);}public void setNiceVideoPlayer(NiceVideoPlayerControl niceVideoPlayer) {mNiceVideoPlayer = niceVideoPlayer;if (mNiceVideoPlayer.isIdle()) {mBack.setVisibility(View.GONE);mTop.setVisibility(View.VISIBLE);mBottom.setVisibility(View.GONE);}}@Overridepublic void onClick(View v) {if (v == mCenterStart) {if (mNiceVideoPlayer.isIdle()) {mNiceVideoPlayer.start();}} else if (v == mBack) {if (mNiceVideoPlayer.isFullScreen()) {mNiceVideoPlayer.exitFullScreen();} else if (mNiceVideoPlayer.isTinyWindow()) {mNiceVideoPlayer.exitTinyWindow();}} else if (v == mRestartPause) {if (mNiceVideoPlayer.isPlaying() || mNiceVideoPlayer.isBufferingPlaying()) {mNiceVideoPlayer.pause();} else if (mNiceVideoPlayer.isPaused() || mNiceVideoPlayer.isBufferingPaused()) {mNiceVideoPlayer.restart();}} else if (v == mFullScreen) {if (mNiceVideoPlayer.isNormal()) {mNiceVideoPlayer.enterFullScreen();} else if (mNiceVideoPlayer.isFullScreen()) {mNiceVideoPlayer.exitFullScreen();}} else if (v == mRetry) {mNiceVideoPlayer.release();mNiceVideoPlayer.start();} else if (v == mReplay) {mRetry.performClick();} else if (v == mShare) {Toast.makeText(mContext, "分享", Toast.LENGTH_SHORT).show();} else if (v == this) {if (mNiceVideoPlayer.isPlaying()|| mNiceVideoPlayer.isPaused()|| mNiceVideoPlayer.isBufferingPlaying()|| mNiceVideoPlayer.isBufferingPaused()) {setTopBottomVisible(!topBottomVisible);}}}private void setTopBottomVisible(boolean visible) {mTop.setVisibility(visible ? View.VISIBLE : View.GONE);mBottom.setVisibility(visible ? View.VISIBLE : View.GONE);topBottomVisible = visible;if (visible) {if (!mNiceVideoPlayer.isPaused() && !mNiceVideoPlayer.isBufferingPaused()) {startDismissTopBottomTimer();}} else {cancelDismissTopBottomTimer();}}public void setControllerState(int playerState, int playState) {switch (playerState) {case NiceVideoPlayer.PLAYER_NORMAL:mBack.setVisibility(View.GONE);mFullScreen.setVisibility(View.VISIBLE);mFullScreen.setImageResource(R.drawable.ic_player_enlarge);break;case NiceVideoPlayer.PLAYER_FULL_SCREEN:mBack.setVisibility(View.VISIBLE);mFullScreen.setVisibility(View.VISIBLE);mFullScreen.setImageResource(R.drawable.ic_player_shrink);break;case NiceVideoPlayer.PLAYER_TINY_WINDOW:mFullScreen.setVisibility(View.GONE);break;}switch (playState) {case NiceVideoPlayer.STATE_IDLE:break;case NiceVideoPlayer.STATE_PREPARING:// 只显示准备中动画,其他不显示mImage.setVisibility(View.GONE);mLoading.setVisibility(View.VISIBLE);mLoadText.setText("正在准备...");mError.setVisibility(View.GONE);mCompleted.setVisibility(View.GONE);mTop.setVisibility(View.GONE);mCenterStart.setVisibility(View.GONE);break;case NiceVideoPlayer.STATE_PREPARED:startUpdateProgressTimer();break;case NiceVideoPlayer.STATE_PLAYING:mLoading.setVisibility(View.GONE);mRestartPause.setImageResource(R.drawable.ic_player_pause);startDismissTopBottomTimer();break;case NiceVideoPlayer.STATE_PAUSED:mLoading.setVisibility(View.GONE);mRestartPause.setImageResource(R.drawable.ic_player_start);cancelDismissTopBottomTimer();break;case NiceVideoPlayer.STATE_BUFFERING_PLAYING:mLoading.setVisibility(View.VISIBLE);mRestartPause.setImageResource(R.drawable.ic_player_pause);mLoadText.setText("正在缓冲...");startDismissTopBottomTimer();break;case NiceVideoPlayer.STATE_BUFFERING_PAUSED:mLoading.setVisibility(View.VISIBLE);mRestartPause.setImageResource(R.drawable.ic_player_start);mLoadText.setText("正在缓冲...");cancelDismissTopBottomTimer();case NiceVideoPlayer.STATE_COMPLETED:cancelUpdateProgressTimer();setTopBottomVisible(false);mImage.setVisibility(View.VISIBLE);mCompleted.setVisibility(View.VISIBLE);if (mNiceVideoPlayer.isFullScreen()) {mNiceVideoPlayer.exitFullScreen();}if (mNiceVideoPlayer.isTinyWindow()) {mNiceVideoPlayer.exitTinyWindow();}break;case NiceVideoPlayer.STATE_ERROR:cancelUpdateProgressTimer();setTopBottomVisible(false);mTop.setVisibility(View.VISIBLE);mError.setVisibility(View.VISIBLE);break;}}private void startUpdateProgressTimer() {cancelUpdateProgressTimer();if (mUpdateProgressTimer == null) {mUpdateProgressTimer = new Timer();}if (mUpdateProgressTimerTask == null) {mUpdateProgressTimerTask = new TimerTask() {@Overridepublic void run() {NiceVideoPlayerController.this.post(new Runnable() {@Overridepublic void run() {updateProgress();}});}};}mUpdateProgressTimer.schedule(mUpdateProgressTimerTask, 0, 300);}private void updateProgress() {int position = mNiceVideoPlayer.getCurrentPosition();int duration = mNiceVideoPlayer.getDuration();int bufferPercentage = mNiceVideoPlayer.getBufferPercentage();mSeek.setSecondaryProgress(bufferPercentage);int progress = (int) (100f * position / duration);mSeek.setProgress(progress);mPosition.setText(NiceUtil.formatTime(position));mDuration.setText(NiceUtil.formatTime(duration));}private void cancelUpdateProgressTimer() {if (mUpdateProgressTimer != null) {mUpdateProgressTimer.cancel();mUpdateProgressTimer = null;}if (mUpdateProgressTimerTask != null) {mUpdateProgressTimerTask.cancel();mUpdateProgressTimerTask = null;}}private void startDismissTopBottomTimer() {cancelDismissTopBottomTimer();if (mDismissTopBottomCountDownTimer == null) {mDismissTopBottomCountDownTimer = new CountDownTimer(8000, 8000) {@Overridepublic void onTick(long millisUntilFinished) {}@Overridepublic void onFinish() {setTopBottomVisible(false);}};}mDismissTopBottomCountDownTimer.start();}private void cancelDismissTopBottomTimer() {if (mDismissTopBottomCountDownTimer != null) {mDismissTopBottomCountDownTimer.cancel();}}@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {cancelDismissTopBottomTimer();}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {if (mNiceVideoPlayer.isBufferingPaused() || mNiceVideoPlayer.isPaused()) {mNiceVideoPlayer.restart();}int position = (int) (mNiceVideoPlayer.getDuration() * seekBar.getProgress() / 100f);mNiceVideoPlayer.seekTo(position);startDismissTopBottomTimer();}/*** 控制器恢复到初始状态*/public void reset() {topBottomVisible = false;cancelUpdateProgressTimer();cancelDismissTopBottomTimer();mSeek.setProgress(0);mSeek.setSecondaryProgress(0);mCenterStart.setVisibility(View.VISIBLE);mImage.setVisibility(View.VISIBLE);mBottom.setVisibility(View.GONE);mFullScreen.setImageResource(R.drawable.ic_player_enlarge);mTop.setVisibility(View.VISIBLE);mBack.setVisibility(View.GONE);mLoading.setVisibility(View.GONE);mError.setVisibility(View.GONE);mCompleted.setVisibility(View.GONE);}

以上代码为最为核心代码。其实还有很多功能可以实现,比如加入重力感应进行全屏,退出全屏,这个需要监听重力感应器的变化。也可以重写onTouch事件监听手势实现滑动快进,音量,屏幕亮度的调节,我会在以后的代码中完善这些功能。希望大家有好的建议多多指教,大家在Android的世界里一起进步。

最后附上demo源码:https://gitee.com/fireqiang/MyVideoPlayer

Android 仿今日头条视频播放器实现(不使用节操哟)相关推荐

  1. Android 仿今日头条频道管理(下)(GridView之间Item的移动和拖拽)

    前言 上篇博客我们说到了今日头条频道管理的操作交互体验,我也介绍了2个GridView之间Item的相互移动.详情请參考:Android 仿今日头条频道管理(上)(GridView之间Item的移动和 ...

  2. android仿今日头条个人中心页面

    android仿今日头条个人中心页面 效果图 实现步骤: 自定义ScrollView,添加一个反弹的动画 代码: package com.example.administrator.gerenzhon ...

  3. 转载 Android仿今日头条详情页实现

    转载自@ice_Anson Android仿今日头条详情页实现 源码地址: Android仿今日头条详情页实现 github源码地址 动态图 最近项目有个需求,需要实现一个和今日头条新闻详情页一样的体 ...

  4. Android 仿今日头条首页标题栏效果

    今天带来的是仿今日头条首页的联动滑动效果,废话不多说,先上效果图: 思路: 做这个我们需要实现的效果有 1.滑动内容区域,标题栏会有变化来显示当前所处的位置. 2.点击标题栏,内容区域也会随着滑动并跳 ...

  5. android 今日头条加载动画,Android 仿今日头条简单的刷新效果实例代码

    点击按钮,先自动进行下拉刷新,也可以手动刷新,刷新完后,最后就多一行数据.有四个选项卡. 前两天导师要求做一个给本科学生预定机房座位的app,出发点来自这里.做着做着遇到很多问题,都解决了.这个效果感 ...

  6. Android 仿今日头条评论时键盘自动弹出的效果

    Android 仿今日头条评论时键盘自动弹出的效果:当点击评论时,弹出对话框,同时弹出软键盘,当点击返回键时,将对话框关闭,不只是关闭软键盘. 效果图: 对这个对话框设置一个style效果: < ...

  7. Android仿今日头条首页的顶部标签栏和底部导航栏

    Android仿今日头条首页的顶部标签栏和底部导航栏 先是底部导航栏TextView+ImageView+Fragment: 效果图: activity_main.xml布局: <?xml ve ...

  8. Android 仿今日头条的视频播放控件(几行代码快速实现)

    前段时间由于项目需要用到类似于今日头条的视频播放器,实现在线播放,边缓存边播放,当然也可以播放本地文件,如下图: 这里我推荐大家使用的是jiecaovideoplayer开源库,这个库的播放引擎是ij ...

  9. 安卓视频播放器 仿今日头条视频播放控件----JiaoZiVideoPlayer

    前段时间由于项目需要用到类似于今日头条的视频播放器,实现在线播放,边缓存边播放,当然也可以播放本地文件,如下图: 这里写图片描述 这里我推荐大家使用的是jiecaovideoplayer开源库,这个库 ...

最新文章

  1. VMWARE HOST-ONLY方式共享上网
  2. 人脸识别算法初次了解
  3. 【Java Calendar日历类】可视化日历程序(控制台输出)
  4. 【转载】 Searching过程粗略梳理
  5. asp.net环境下的静态类以及静态变量
  6. hdu 3480 斜率dp
  7. 前端学习(1741):前端调试值之元素状态改变的监听方法
  8. 重庆邮电计算机科学分数线,2020重庆邮电大学录取分数线已公布
  9. java商城_java开源商城系统的优势是什么?
  10. Windows与Linux(服务器)之间大文件传输
  11. 前端:CSS/08/框架
  12. Bootstrap媒体对象列表
  13. HDU 5015 233 Matrix 矩阵快速幂
  14. 2021年10月国产数据库大事记-墨天轮
  15. Fastadmin中的语言加载机制
  16. 不用写采集规则也可以轻松采集网站文章,揭秘一款明泽文章采集软件的工作原理
  17. 高数 下总复习 完结
  18. 转载:js和as间的交互
  19. 如何解决“自动装包”过程中oppo、vivo等手机需要输入密码的问题
  20. Pepper使用心得

热门文章

  1. DirectX End-User Runtimes2010 dl
  2. 对项目经理而言,PMP认证是否对职业生涯的发展有帮助?
  3. 这样的牛皮凉席清水席要慎买!!!
  4. 如何启用Ubuntu 18.04的鼠标键
  5. 人物志 | 美团技术委员会前端通道主席洪磊:爱折腾的斜杠青年
  6. structure-from-motion revisited论文笔记
  7. 如何读取环境变量值?Go 每日一库之 godotenv
  8. Google 文档 地址
  9. vps搭建代理ip服务
  10. 什么蓝牙耳机的延迟最低?2022延迟最低的蓝牙耳机推荐