最近在手表上(安卓系统)做相机、相册、聊天应用,想着业务逻辑简单,不需要使用功能强大的第三方视频库,Android自带的VideoView正好合适,略遗憾的是搭配VideoView使用的MediaController无法定制,网上有不少文章说拷贝源码重写一个,但是我卡住其中一步:无法通过反射获取到PhoneWindow,惨败收场。。。
![](https://img.haomeiwen.com/i11576532/ae01765f165477d6.png)
我的需求其实是非常简单的:1、轻触屏幕唤起控制器;2、1秒后自动隐藏;3、支持播放、暂停、向前10秒、向后10秒、拖动播放
代码如下:
class JXMediaController : ConstraintLayout {
/**
* 控制哪个VideoView,必须
*/
var videoView: VideoView? =null
set(value) {
field = value
initVideoListener()
}
/**
* 显示时长
*/
var hideMilliseconds =1_000
private var mShowing =false
private var mDragging =false
private val mFormatBuilder = StringBuilder()
private val mFormatter = Formatter(mFormatBuilder, Locale.getDefault())
constructor(context: Context) :this(context,null){}
constructor(context: Context, attrs: AttributeSet?) :super(context, attrs) {
View.inflate(context, R.layout.media_controller,this)
pause_btn.setOnClickListener{
pauseOrPlay()
}
backward_btn.setOnClickListener{
backward()
}
forward_btn.setOnClickListener{
forward()
}
seek_bar.setOnSeekBarChangeListener(mSeekListener)
}
//前进10秒
private fun forward() {
val videoView =videoView ?:return
if (!videoView.canSeekForward())return
val remain = videoView.duration - videoView.currentPosition
if (remain >10_000) {
videoView.seekTo(videoView.currentPosition +10_000)
show()
}
// updateProgress(videoView.currentPosition, videoView.duration)
}
//回退10秒
private fun backward() {
val videoView =videoView ?:return
if (!videoView.canSeekBackward())return
if (videoView.currentPosition >10_000) {
videoView.seekTo(videoView.currentPosition -10_000)
show()
}
// updateProgress(videoView.currentPosition, videoView.duration)
}
private fun pauseOrPlay() {
val videoView =videoView ?:return
if (videoView.isPlaying) {
videoView.pause()
pause_btn.setImageResource(R.mipmap.media_play)
}else {
videoView.start()
pause_btn.setImageResource(R.mipmap.media_pause)
}
show()
}
private fun initVideoListener() {
val videoView =videoView ?:return
videoView.setOnPreparedListener{
if (mShowing) {
updatePausePlay()
}
}
videoView.setOnCompletionListener{
if (mShowing) {
updatePausePlay()
}
}
videoView.setOnErrorListener{ mp, what, extra->
if (mShowing) {
updatePausePlay()
}
true
}
videoView.setOnInfoListener{ mp, what, extra->
LogI("on info ....")
if (mShowing) {
updateProgress(mp.currentPosition, mp.duration)
}
true
}
videoView.setOnClickListener{
if (!mShowing) {
show()
}
}
}
//更新暂停/开始按钮
private fun updatePausePlay() {
val videoView =videoView ?:return
if (videoView.isPlaying) {
pause_btn.setImageResource(R.mipmap.media_pause)
}else {
pause_btn.setImageResource(R.mipmap.media_play)
}
}
private fun updateProgress() {
val videoView =videoView ?:return
updateProgress(videoView.currentPosition, videoView.duration)
}
//更新进度
private fun updateProgress(position: Int, duration: Int) {
if (duration >0) {
val pos: Long =1000L * position / duration
seek_bar.progress = pos.toInt()
}
time_current_tv.text = stringForTime(position)
time_tv.text = stringForTime(duration)
}
//显示
fun show() {
show(hideMilliseconds)
}
fun show(timeout: Int) {
if (!mShowing) {
pause_btn.requestFocus()
visibility = View.VISIBLE
mShowing =true
}
updatePausePlay()
post(mShowProgress)
if (timeout !=0) {
removeCallbacks(mFadeOut)
postDelayed(mFadeOut, timeout.toLong())
}
}
fun hide() {
if (mShowing) {
removeCallbacks(mShowProgress)
visibility = View.GONE
mShowing =false
}
}
private fun stringForTime(timeMs: Int): String? {
val totalSeconds = timeMs /1000
val seconds = totalSeconds %60
val minutes = totalSeconds /60 %60
val hours = totalSeconds /3600
mFormatBuilder.setLength(0)
return if (hours >0) {
mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString()
}else {
mFormatter.format("%02d:%02d", minutes, seconds).toString()
}
}
private val mFadeOut =Runnable { hide()}
private val mShowProgress: Runnable =object : Runnable {
override fun run() {
updateProgress()
if (!mDragging &&mShowing &&videoView!!.isPlaying()) {
postDelayed(this,1000 - (videoView!!.currentPosition %1000).toLong())
}
}
}
private val mSeekListener: OnSeekBarChangeListener =object : OnSeekBarChangeListener {
override fun onStartTrackingTouch(bar: SeekBar) {
mDragging =true
removeCallbacks(mFadeOut)
removeCallbacks(mShowProgress)
}
override fun onProgressChanged(bar: SeekBar, progress: Int, fromuser: Boolean) {
if (!fromuser)return
val videoView =videoView ?:return
val duration: Long = videoView.duration.toLong()
val newPosition = duration * progress /1000L
videoView.seekTo(newPosition.toInt())
time_current_tv.text = stringForTime(newPosition.toInt())
}
override fun onStopTrackingTouch(bar: SeekBar) {
mDragging =false
// updatePausePlay()
show()
// post(mShowProgress)
}
}
}
布局很简单,相信你也能猜出来。感谢阅读
网友评论