Android音视频开发之VideoView使用指南
作者:JulyYu 发布时间:2022-11-20 11:14:52
VideoView介绍
之前介绍过使用MediaPlayer
+SurfaceView
实现播放视频功能。无意间发现官方封装了VideoView
组件来实现简单视频播放功能,内部同样是使用MediaPlayer
+SurfaceView
的形式控制MediaPlayer
对视频文件进行播放。使用场景比较简单,适用于只是播放视频的场景,其提供能力有限不太适合使用在调节视频亮度等其他功能。
MediaController
除了播放组件VideoView
外还有MediaController
组件为视频播放提供播放操作栏功能,可支持视频播放、暂停、快进、快退等功能。另外还提供进度条功能可以拖拽到指定位置进行播放视频。
使用
VideoView
封装了MediaPlayer
同样也提供了类似于MediaPlayer
的api。例如start
方法同样是播放视频功能,但调用该方法前最好也是通过设置setOnpreparedListener
回调结果来执行,当调用setVideoPath
后会主动执行prepareAsync
方法。在VideoView
内部帮助开发者封装实现了很多功能,其实也能借鉴其内部源码来实现功能更全面功能更完备的自制播放器。
常用Api | 说明 |
---|---|
setVideoPath | 设置视频资源 |
start | 播放 |
pause | 暂停 |
resume | 重播 |
seekTo | 指定位置播放 |
isPlaying | 视频是否播放 |
getCurrentPosition | 获取当前播放位置 |
setMediaController | 设置MediaController |
setOnpreparedListener | 监听视频装载完成事件 |
// 实例化videoView
videoView = new VideoView(this);
Uri uri = Uri.fromFile(new File("sdcard/DCIM","新世纪福音战士24.mp4"));
//加载视频资源
videoView.setVideoURI(uri);
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.addView(videoView);
setContentView(linearLayout);
//设置监听
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//回调成功并播放视频
videoView.start();
}
});
//创建操作栏
MediaController mediaController = new MediaController(this);
videoView.setMediaController(mediaController);
mediaController.setMediaPlayer(videoView);
源码分析
既然封装了VideoView
和MediaController
两者组件,在使用过程中也发现了许多之前尝试实现的一些功能看看他们又是如何实现的。
进度显示
MediaController
显示时调用show
方法内部可以看到一个post(mShowProgress);
方法
public void show(int timeout) {
if (!mShowing && mAnchor != null) {
setProgress();
if (mPauseButton != null) {
mPauseButton.requestFocus();
}
disableUnsupportedButtons();
updateFloatingWindowLayout();
mWindowManager.addView(mDecor, mDecorLayoutParams);
mShowing = true;
}
updatePausePlay();
// cause the progress bar to be updated even if mShowing
// was already true. This happens, for example, if we're
// paused with the progress bar showing the user hits play.
post(mShowProgress);
if (timeout != 0 && !mAccessibilityManager.isTouchExplorationEnabled()) {
removeCallbacks(mFadeOut);
postDelayed(mFadeOut, timeout);
}
}
可以看到mShowProgress
是一个Runnable
,内部会延迟不停调用自己来更新setProgress()
。setProgress()
方法就是读取MediaPlayer
播放进度从而更新播放信息。
private final Runnable mShowProgress = new Runnable() {
@Override
public void run() {
int pos = setProgress();
if (!mDragging && mShowing && mPlayer.isPlaying()) {
postDelayed(mShowProgress, 1000 - (pos % 1000));
}
}
};
播放尺寸适配
之前自定义实现播放尺寸适配,在VideoView
内部直接帮助开发者实现视频播放适配,详细代码可以直接看onMeasure
重写。代码大致算法就是通过比较VideoView
布局宽高和视频的宽高进行比例比较来重写计算VideoView
的宽高。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
// + MeasureSpec.toString(heightMeasureSpec) + ")");
int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
if (mVideoWidth > 0 && mVideoHeight > 0) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
// the size is fixed
width = widthSpecSize;
height = heightSpecSize;
// for compatibility, we adjust size based on aspect ratio
if ( mVideoWidth * height < width * mVideoHeight ) {
//Log.i("@@@", "image too wide, correcting");
width = height * mVideoWidth / mVideoHeight;
} else if ( mVideoWidth * height > width * mVideoHeight ) {
//Log.i("@@@", "image too tall, correcting");
height = width * mVideoHeight / mVideoWidth;
}
} else if (widthSpecMode == MeasureSpec.EXACTLY) {
// only the width is fixed, adjust the height to match aspect ratio if possible
width = widthSpecSize;
height = width * mVideoHeight / mVideoWidth;
if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
// couldn't match aspect ratio within the constraints
height = heightSpecSize;
}
} else if (heightSpecMode == MeasureSpec.EXACTLY) {
// only the height is fixed, adjust the width to match aspect ratio if possible
height = heightSpecSize;
width = height * mVideoWidth / mVideoHeight;
if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
// couldn't match aspect ratio within the constraints
width = widthSpecSize;
}
} else {
// neither the width nor the height are fixed, try to use actual video size
width = mVideoWidth;
height = mVideoHeight;
if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
// too tall, decrease both width and height
height = heightSpecSize;
width = height * mVideoWidth / mVideoHeight;
}
if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
// too wide, decrease both width and height
width = widthSpecSize;
height = width * mVideoHeight / mVideoWidth;
}
}
} else {
// no size yet, just adopt the given spec sizes
}
setMeasuredDimension(width, height);
}
来源:https://juejin.cn/post/7085243234523283464


猜你喜欢
- 本文实例通过Java的Zip输入输出流实现压缩和解压文件,前一部分代码实现获取文件路径,压缩文件名的更改等,具体如下:package com
- 官方 JSON.NET 地址 http://james.newtonking.com/pages/json-net.aspxXML TO J
- 好久没有做web了,JSON目前比较流行,闲得没事,所以动手试试将对象序列化为JSON字符(尽管DotNet Framework
- 前言在引入 fl_chart 绘制图表的时候,看到插件有下面这样的动效,随机散乱的圆点最后组合成了 Flutter 的 Logo,挺酷炫的。
- 首先创建一个工具类import android.annotation.TargetApi;import android.app.Activi
- 一、HandlerThread的介绍及使用举例  
- 用法1 为原始类型扩展方法先说一下,this 后面跟的类型,就是要拓展方法的类型。注意要写在静态类中的静态方法,不然有些情况下访问/// &
- 项目效果实现代码using System;namespace 飞行棋项目{ class Program  
- 本文实例讲述了Android实现长按back键退出应用程序的方法。分享给大家供大家参考。具体分析如下:最近在做一个Android上的应用,碰
- 一、Elasticseach介绍1.简单介绍官网:开源搜索:Elasticsearch、ELK Stack 和 Kibana 的
- 获取网络信息需要在AndroidManifest.xml文件中加入相应的权限。 <uses-permission android:na
- 定义:定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。类型:行为类模式类图: 
- 本文实例分析了C#中登录窗体和欢迎窗体关闭方法。分享给大家供大家参考。具体分析如下:在c#的winform编程中,我们经常会做登录窗体或欢迎
- 概要笔者近期做到对天气预报JSON数据解析,在此小记。天气预报接口:http://wthrcdn.etouch.cn/weather_min
- java 算法之归并排序详解一、思想 归并排序:将一个数组排序,可以先(递归地)将它分成两半部份分别排序,然后将结果归并起来; &
- 本文介绍了java 读写Parquet格式的数据,分享给大家,具体如下:import java.io.BufferedReader;impo
- MyBatis @MapKey的妙用背景在实际开发中,有一些场景需要我们返回主键或者唯一键为Key、Entity为Value的Map集合,如
- 一 技术发展技术的创新和发展都是为了解决一类问题二 框架设计Spring Framework 6大模块三 Spring AOP详解循环依赖问
- Android RecycleView添加head配置封装的实例这个是把RecycleView的适配器给封装了,直接调用就可以了,还添加了可
- 在IntelliJ IDEA中一不小心将你本地代码给覆盖了,这个时候,你 ctrl + z