视频播放功能在APP开发中有的会用到这个功能,实现视频播放四种方式
(1)系统自带的视频播放器
(2)VideoView播放器(自带开始、停止、暂停等功能,进度条自带不能改变),实现起来较简单
(3)SurfaceView+MediaPlayer实现播放器(SurfaceView是视频播放功能,不带进度条等时间显示功能,需要自己按照需求开发),比VIdeoVIew实现要困难。
(4)使用第三方插件
所以在选择的时候,可以根据自己的需求是否需要自己设计进度条等功能。自己设计可以使用SurfaceView+MediaPlayer的封装实现视频播放功能,大家可以直接拿封装好的代码实现即可,也可以自己优化功能,但是自己设计的播放器总会很简陋,只能实现基本功能,如开始黑屏,点击开始反应慢等情况都会出现,那么出现了已经写好的第三方插件,哎,现在感觉开发Android真是越来越简单了,各种好用的封装遍地。
第三方插件JiaoZiVideoPlayer,GitHub地址:https://github.com/lipangit/JiaoZiVideoPlayer 有8000多个star,相当可以了。下面分别讲解一下自己封装和第三方插件的使用
(1)MediaPlayer就是实现播放的进度条和控制进度等的方法
(2)MediaPlayer的相关方法及监听接口:
方法 | 介绍 | 状态 |
---|---|---|
setDataSource | 设置数据源 | Initialized |
prepare | 准备播放,同步 | Preparing —> Prepared |
prepareAsync | 准备播放,异步 | Preparing —> Prepared |
start | 开始或恢复播放 | Started |
pause | 暂停 | Paused |
stop | 停止 | Stopped |
seekTo | 到指定时间点位置 | PrePared/Started |
reset | 重置播放器 | Idle |
setAudioStreamType | 设置音频流类型 | -- |
setDisplay | 设置播放视频的Surface | -- |
setVolume | 设置声音 | -- |
getBufferPercentage | 获取缓冲半分比 | -- |
getCurrentPosition | 获取当前播放位置 | -- |
getDuration | 获取播放文件总时间 | -- |
内部回调接口 | 介绍 | 状态 |
---|---|---|
OnPreparedListener | 准备监听 | Preparing ——>Prepared |
OnVideoSizeChangedListener | 视频尺寸变化监听 | -- |
OnInfoListener | 指示信息和警告信息监听 | -- |
OnCompletionListener | 播放完成监听 | PlaybackCompleted |
OnErrorListener | 播放错误监听 | Error |
OnBufferingUpdateListener | 缓冲更新监听 | -- |
(1) 封装的文件一共有四个:封装的class(MySurfaceView),页面Activity(TestMySurfaceView),接口(SurfaceViewListen),和xml文件
(2)Activity代码,其中继承的BaseActivity可修改为Activity
class TestMySurfaceView : BaseActivity(),View.OnClickListener,SurfaceViewListen {
var surface : MySurfaceView ?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_play_video)
initView()
}
fun initView(){
surface = MySurfaceView(surface_view_id,this)
surface!!.initView()
surface!!.initPlayer()
surface!!.setUri("http://112.253.22.157/17/z/z/y/u/zzyuasjwufnqerzvyxgkuigrkcatxr/hc.yinyuetai.com%20/D046015255134077DDB3ACA0D7E68D45.flv")
surface_view_id!!.setOnClickListener(this)
tv_backward!!.setOnClickListener(this)
tv_forward!!.setOnClickListener(this)
}
override fun onClick(v: View) {
when (v.id) {
R.id.tv_backward -> surface!!.backWard()
R.id.tv_forward -> surface!!.forWard()
R.id.surface_view_id -> surface!!.play()
}
}
override fun updateTime(time : Int) {
tv_start_time!!.setText(TimeUtil.secToTime(time/1000))
tv_progess!!.setProgress(time)
}
override fun preparedPlay(currentPosition: Int, duration: Int) {
tv_start_time!!.setText(TimeUtil.secToTime(currentPosition/1000))
tv_end_time!!.setText(TimeUtil.secToTime(duration/1000))
tv_progess!!.setMax(duration)
tv_progess!!.setProgress(currentPosition)
}
override fun playStatus(status: Int) {
//当为开始状态时,需要取消结束按钮
if(status!! == 0){
playOrPause!!.setVisibility(View.INVISIBLE)
playOrPause!!.setImageResource(android.R.drawable.ic_media_pause)
//当为结束状态时
}else{
playOrPause!!.setVisibility(View.VISIBLE)
playOrPause.setImageResource(android.R.drawable.ic_media_play)
}
}
}
(3)封装的class代码
class MySurfaceView(val surfaceId : SurfaceView,var listen : SurfaceViewListen ) : SurfaceHolder.Callback, SeekBar.OnSeekBarChangeListener,
MediaPlayer.OnPreparedListener,
MediaPlayer.OnCompletionListener,
MediaPlayer.OnErrorListener,
MediaPlayer.OnInfoListener,
MediaPlayer.OnSeekCompleteListener,
MediaPlayer.OnVideoSizeChangedListener {
val UPDATE_TIME = 0
val HIDE_CONTROL = 1
private var isShow = false
var path: String? = null
private var mPlayer: MediaPlayer? = null
private var surfaceHolder: SurfaceHolder? = null
private var possion = 0
//初始化
fun initView() {
surfaceHolder = surfaceId!!.holder
mPlayer =MediaPlayer ()
surfaceHolder!!.setKeepScreenOn(true)
}
//给视频播放框架surfaceView加载网络地址
fun setUri(uri : String){
path=uri
}
fun initPlayer()
{
mPlayer!!.setOnCompletionListener(this)
mPlayer!!.setOnErrorListener(this)
mPlayer!!.setOnInfoListener(this)
mPlayer!!.setOnPreparedListener(this)
mPlayer!!.setOnSeekCompleteListener(this)
mPlayer!!.setOnVideoSizeChangedListener(this)
}
override fun surfaceCreated(holder: SurfaceHolder) {
mPlayer!!.setDisplay(holder)
mPlayer!!.prepare()
play()
mPlayer!!.seekTo(possion)
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
//showToastShort("___"+format.toString())
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
// showToastShort("___1")
}
//当开始时对视频进行的准备工作
override fun onPrepared(mp: MediaPlayer) {
listen!!.preparedPlay(mp.currentPosition,mp.duration)
}
//播放完成执行的方法
override fun onCompletion(mp: MediaPlayer) {
//showToastShort("完成")
}
override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
return false
}
override fun onInfo(mp: MediaPlayer, what: Int, extra: Int): Boolean {
return false
}
//视频的播放与暂停
@Throws(IllegalArgumentException::class, SecurityException::class, IllegalStateException::class, IOException::class)
fun play() {
if (mPlayer!!.isPlaying()) {
possion = mPlayer!!.getCurrentPosition()
mPlayer!!.pause()
mHandler.removeMessages(UPDATE_TIME)
mHandler.removeMessages(HIDE_CONTROL)
listen!!.playStatus(1)
} else {
if(possion==0){
mPlayer!!.setDataSource(path)
// 把视频输出到SurfaceView上
mPlayer!!.setDisplay(surfaceHolder)
//不要用prepareAsync,用了也报错,ERROR(-38,0)
mPlayer!!.prepare()
}
// mPlayer!!.seekTo(possion)
mPlayer!!.start()
mHandler.sendEmptyMessageDelayed(UPDATE_TIME, 500)
mHandler.sendEmptyMessageDelayed(HIDE_CONTROL, 50000)
listen!!.playStatus(0)
}
}
override fun onSeekComplete(mp: MediaPlayer) {
// showToastShort("进度条")
}
override fun onVideoSizeChanged(mp: MediaPlayer, width: Int, height: Int) {
}
/**
* 隐藏进度条
*/
private fun hideControl() {
// isShow = false
//mHandler.removeMessages(UPDATE_TIME)
//control_ll!!.animate().setDuration(300).translationY(control_ll!!.getHeight().toFloat())
}
/**
* 显示进度条
*/
private fun showControl() {
if (isShow) {
play()
}
isShow = true
mHandler.removeMessages(HIDE_CONTROL)
mHandler.sendEmptyMessage(UPDATE_TIME)
mHandler.sendEmptyMessageDelayed(HIDE_CONTROL, 50000)
// control_ll!!.animate().setDuration(300).translationY(0.toFloat())
}
/**
* 设置快进10秒方法
*/
fun forWard() {
if (mPlayer!! != null) {
val position = mPlayer!!.getCurrentPosition()
mPlayer!!.seekTo(position + 10000)
}
}
/**
* 设置快退10秒的方法
*/
fun backWard() {
if (mPlayer!! != null) {
var position = mPlayer!!.getCurrentPosition()
if (position > 10000) {
position -= 10000
} else {
position = 0
}
mPlayer!!.seekTo(position)
}
}
//拖动进度条改变时调用
override fun onProgressChanged(seekBar: SeekBar, progress: Int, b: Boolean) {
if (mPlayer!! != null && b) {
mPlayer!!.seekTo(progress)
}
}
//进度条开始时调用
override fun onStartTrackingTouch(seekBar: SeekBar) {
//showToastShort("移动进度条开始")
}
//进度条结束时调用
override fun onStopTrackingTouch(seekBar: SeekBar) {
// showToastShort("移动进度条结束")
}
//设置定时器,定时更新播放的时间和进度条功能
val mHandler = object : Handler() {
override fun handleMessage(msg: Message) {
when (msg.what) {
UPDATE_TIME -> {
listen!!.updateTime(mPlayer!!.getCurrentPosition())
this.sendEmptyMessageDelayed(UPDATE_TIME, 500)
}
HIDE_CONTROL -> hideControl()
}
}
}
}
(4)回调UI的接口文档
interface SurfaceViewListen {
/**
* 更新播放时间
*/
fun updateTime(time : Int)
//播放成功后返回到UI界面的进度条
fun preparedPlay(currentPosition : Int,duration : Int)
//播放状态,开始或者暂停功能,0:代表开始;1:代表暂停
fun playStatus(status : Int)
}
(5)xml文件(可完全按照自己的要求进行设计)
1、首先在build.xml中加载包
implementation 'cn.jzvd:jiaozivideoplayer:6.4.1'
2、编写xml,只需要一个属性就OK
3、在activity中的使用,大功告成
private void initData(){
JzvdStd jiaozi_videoplayer=findViewById(R.id.jiaozi_videoplayer);
jiaozi_videoplayer.setUp("http://112.253.22.157/17/z/z/y/u/zzyuasjwufnqerzvyxgkuigrkcatxr/hc.yinyuetai.com%20/D046015255134077DDB3ACA0D7E68D45.flv",
"视频播放",Jzvd.SCREEN_WINDOW_NORMAL);
// jiaozi_videoplayer.thumbImageView.setImageResource(R.drawable.default_picture);
}
@Override
public void onBackPressed() {
if (Jzvd.backPress()) {
return;
}
super.onBackPressed();
}
@Override
protected void onPause() {
super.onPause();
Jzvd.releaseAllVideos();
}
其实我自己封装的视频播放功能是我在没发现第三方插件之前封装的,后来对于封装不太满意,才发现了JiaoZiVideoPlayer,一下子啊感觉自己封装的真的很low,建议大家还是用第三方插件封装的吧,功能强大不说,使用起来也特别方便。