Android模仿VideoView实现视频播放
VideoView介绍
模仿VideoView实现步骤
问题解决
快捷键
VideoView介绍
VideoView是android自带的原生视频空间,通过它,我们能够实现视频播放,进度控制等,不过也有一个通用的UI界面,有需求的话,那就必须进行自定义。
class VideoView extends SurfaceView implements MediaPlayerControl
videoView是通过继承SurfaceView并实现MediaPlayerControl,可以理解为自定义view,我这里是通过activity来实现的。
activity+surfaceview来实现视频功能
模仿VideoView实现步骤
1、使用surfaceview来承载视频播放界面—即布局文件中包含surfaceView
2、继承SurfaceHolder.Callback
3、在surfaceCreated方法回调中,指定mediaplay的display为surfaceview,当SurfaceView中的Surface被创建的时候被调用, 在这里我们指定MediaPlayer在当前的Surface中进行播放
4、对视频尺寸适配:可以实现MediaPlayer.OnPreparedListener,然后在onPrepared回调中进行尺寸适配
5、视频控制:通过实现MediaController.MediaPlayerControl来实现原生的视频控制,自定义的话,下文有提到
注意:本文只实现进度条是控制,没有添加手势控制。
第一二点基本不用解释,
第三点:在activity中,我们可以在oncreate或onPrepared方法中对mediaplay进行初始化,包括视频资源等(这里指的是本地文件),但是如果使用surfaceview作为视频播放的载体的话,需要在播放的时候,surfaceview实例化出来,所以需要在surfaceCreated中调用mPlayer.setDisplay(mSurface.getHolder());// 设置屏幕,避免出现空指针等问题,同时对于第五点,可以在此处进行attach controlView。
第四点:建议在onPrepared回调中实现,适配原理是视频的宽高和屏幕的宽高进行同比例缩放
@Override
public void onPrepared(MediaPlayer mp) {
//首先取得video的宽和高
vWidth = mPlayer.getVideoWidth();
vHeight = mPlayer.getVideoHeight();
if(vWidth > currDisplay.getWidth() || vHeight > currDisplay.getHeight()){
//如果video的宽或者高超出了当前屏幕的大小,则要进行缩放
float wRatio = (float)vWidth/(float)currDisplay.getWidth();
float hRatio = (float)vHeight/(float)currDisplay.getHeight();
//选择大的一个进行缩放
float ratio = Math.max(wRatio, hRatio);
vWidth = (int)Math.ceil((float)vWidth/ratio);
vHeight = (int)Math.ceil((float)vHeight/ratio);
//设置surfaceView的布局参数
mSurface.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight));
}
mPlayer.start();
//此处根据需求是否显示控制进度条,hide为隐藏
if (mMediaController != null) {
mMediaController.show();
}
}
第五点:视频控制:实现MediaController.MediaPlayerControl可以实现控制,但UI是共用的,不能满足需求,所以我的方法就是重写MediaController
先介绍下接口MediaPlayerControl
//开始
void start();
//暂停
void pause();
//获取视频长度,ms
int getDuration();
//当前播放位置 ms
int getCurrentPosition();
//跳转到某个位置ms,当pos<0,pos=0
void seekTo(int pos);
//是否播放
boolean isPlaying();
//得到缓冲的百分比
int getBufferPercentage();
//是否能暂停
boolean canPause();
//是否可以后退
boolean canSeekBackward();
//时候可以快进
boolean canSeekForward();
在以上这些方法中,我们需要调用对应的mediaplay的方法来实现controlVIew和视频同步。
关于重写MediaController,不是继承关系,而是在sdk中找到对应的MediaController.java文件,放在项目中,或者打成jar包引用。
以上就是集成步骤
问题解决
有三个问题需要注意下:
1.重写MediaController会报错,提示不能生成window
import com.android.internal.policy.PhoneWindow;
mWindow = new PhoneWindow(mContext);
所以我们这里需要使用反射的方法进行获取window对象
String POLICYMANAGER_CLASS_NAME = "com.android.internal.policy.PolicyManager";
try {
Class policyClass;
policyClass = Class.forName(POLICYMANAGER_CLASS_NAME);
Method meths[] = policyClass.getMethods();
Method makenewwindow = null;
// Method makenewwindow = policyClass.getMethod("makeNewWindow");
for (int i = 0; i < meths.length; i++) {
if (meths[i].getName().endsWith("makeNewWindow"))
makenewwindow = meths[i];
}
mWindow = (Window) makenewwindow.invoke(null, mContext);
} catch (Exception e) {
e.printStackTrace();
}
通过反射可以在android6.0以下的版本运行,但是6.0会报空指针,原因是在6.0中找不到com.android.internal.policy.PhoneWindow,所以需要分开处理:
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.M){
//M
try {
Class windowClass = Class.forName("com.android.internal.policy.PhoneWindow");
Constructor<?> localConstructor = windowClass.getConstructor(new Class[]{Context.class});
mWindow = (Window) localConstructor.newInstance(new Object[]{BaseApplication.getContext()});//实例化Window,如果传的context不是Application的Context,就会奔溃
}catch (Exception e) {
e.printStackTrace();
}
}
2.手势控制:需要实现GestureDetector.OnGestureListener,需要注意的是在surfaceview的ontouch事件中使用 GestureDetector的替换
public boolean onTouch(View v, MotionEvent event)
{
// TODO Auto-generated method stub
return mGesture.onTouchEvent(event);
}
以上就是全部内容,有兴趣的话,可以关注我的微信订阅号: