1、VideoView类介绍
Android的VideoView组件可以从不同的来源(例如资源文件或内容提供器)读取图像,计算和维护视频的画面尺寸以使其适用于任何布局管理器,并提供一些诸如缩放、着色之类的显示选项,包含在widget下面:android.widget.VideoView。Android中视屏播放框架如下图:
从图中可以看出,VideoView组件进行视频播放的过程可以分为三步:
(1)JAVA Framework层,应用程序进来之后到VideoView,再经过Surface;
(2)Native Framework层,先到SurfaceFlinger,然后到OverlayHal,同时借助了PVPlayer;
(3)Driver层,利用Main framebuffer和Video Plane进行播放;
VideoView组件的类继承与接口实现情况为:
继承:public class VideoView extends SurfaceView
接口:implements MediaController.MediaPlayerControl
而从Object开始的继承层次如下:
java.lang.Object
android.view.View
android.view.SurfaceView
android.widget.VideoView
VideoView类的构造函数有三个:
(1)public VideoView(Context context),创建一个默认属性的VideoView实例。参数context为视图运行的应用程序上下文,通过它可以访问当前主题、资源等等。注:以下描述中重复参数的解释就不给出了。
(2)public VideoView(Context context, AttributeSet attrs),创建一个带有attrs属性的VideoView实例。attrs用于视图的 XML 标签属性集合。
(3)public VideoView(Context context, AttributeSet attrs, int defStyle),创建一个带有attrs属性,并且指定其默认样式的VideoView实例。参数defStyle为应用到视图的默认风格,如果为 0 则不应用(包括当前主题中的)风格,该值可以是当前主题中的属性资源,也可以是明确的风格资源ID。
比较常用的共有方法有播放start()、暂停pause()等,具体描述与用法见后面测试部分。
2、视频播放代码
先给出一个简单的测试案例,程序中以File和Uri两种形式来给VideoView组件加载手机SD卡中的视频资源。资源类型为MP4,使用手机自带摄像机录制,完整路径名为“/mnt/sdcard/Pictures/video.mp4”。在代码采用的是读取系统路径的方式:Environment.getExternalStorageDirectory() + "/Pictures/video.mp4"。
另外说明一点,利用Uri对网络资源进行读取的方式和本地资源类似,只是资源名称的形式不同。如:
Uri uri = Uri.parse("rtsp://v2.cache2.c.youtube.com/CjgLENy73wIaLwm3JbT_%ED%AF%80%ED%B0%819HqWohMYESARFEIJbXYtZ29vZ2xlSARSB3Jlc3VsdHNg_vSmsbeSyd5JDA==/0/0/0/video.3gp");
文件格式3gp也是VideoView组件支持格式中的一种。
由于要对手机中的文件进行读取,所以必须在AndroidManifest.xml文件中添加用户权限:
1
2
3
程序中用到的String变量封装在了string.xml中:
1
2 VideoView0803
3
4 Hello world!
5 Settings
6
7 PlayCard
8 StopCard
9 PlayUri
10 StopUri
11
界面上的组件由一个VideoView、两个Button,一个TextView组成,布局文件代码如下:
1
7
8
16
17
25
26
34
35
46
47
其中,VideoView组件videoView用于视频内容的播放,Button组件startFile和startUri分别以File和Uri形式打开视频文件,TextView组件fileName用于显示完整路径名(标注资源的来历,对于该例子没这个必要)。运行前的界面图:
主文件MainActivity完整代码如下:
1 package com.dylan_wang.videoview0803;
2
3 import android.app.Activity;
4 import android.net.Uri;
5 import android.os.Bundle;
6 import android.os.Environment;
7 import android.view.Menu;
8 import android.view.MenuItem;
9 import android.view.View;
10 import android.widget.Button;
11 import android.widget.MediaController;
12 import android.widget.TextView;
13 import android.widget.VideoView;
14
15 import java.io.File;
16
17
18 public class MainActivity extends Activity {
19
20 private String filename = null;
21 private Button startCard = null;
22 private Button startUri = null;
23 private TextView fileName = null;
24 private VideoView video = null;
25 private MediaController media = null;
26
27 @Override
28 protected void onCreate(Bundle savedInstanceState) {
29 super.onCreate(savedInstanceState);
30 setContentView(R.layout.activity_main);
31
32 filename = Environment.getExternalStorageDirectory() + "/Pictures/video.mp4";
33
34 startCard = (Button)findViewById(R.id.startCard);
35 startUri = (Button)findViewById(R.id.startUri);
36 fileName = (TextView)findViewById(R.id.fileName);
37 video = (VideoView)findViewById(R.id.videoView);
38 media = new MediaController(MainActivity.this);
39
40 startCard.setOnClickListener(new View.OnClickListener() {
41 @Override
42 public void onClick(View v) {
43 playVideoFromFile();
44 }
45 });
46
47 startUri.setOnClickListener(new View.OnClickListener() {
48 @Override
49 public void onClick(View v) {
50 openVideoFromUri();
51 }
52 });
53 }
54
55 private void playVideoFromFile(){
56 if(startCard.getText().toString().equals("PlayCard")) {
57 File file = new File(filename);
58 if (file.exists()) {
59 //将VideoView与MediaController进行关联
60 video.setVideoPath(file.getAbsolutePath());
61 video.setMediaController(media);
62 media.setMediaPlayer(video);
63 //让VideoView获取焦点
64 video.requestFocus();
65 video.start();
66 startCard.setText(R.string.pauseCard);
67 fileName.setText(filename);
68 }
69 }
70 else {
71 video.pause();
72 startCard.setText(R.string.startCard);
73 }
74 }
75
76 private void openVideoFromUri(){
77 if(startUri.getText().toString().equals("PlayUri")) {
78 Uri uri = Uri.parse(filename);
79 video.setVideoURI(uri);
80 video.setMediaController(media);
81 media.setMediaPlayer(video);
82 //同上
83 video.requestFocus();
84 video.start();
85 startUri.setText(R.string.pauseUri);
86 fileName.setText(filename);
87 }
88 else {
89 video.pause();
90 startUri.setText(R.string.startUri);
91 }
92 }
93
94 @Override
95 public boolean onCreateOptionsMenu(Menu menu) {
96 // Inflate the menu; this adds items to the action bar if it is present.
97 getMenuInflater().inflate(R.menu.menu_main, menu);
98 return true;
99 }
100
101 @Override
102 public boolean onOptionsItemSelected(MenuItem item) {
103 // Handle action bar item clicks here. The action bar will
104 // automatically handle clicks on the Home/Up button, so long
105 // as you specify a parent activity in AndroidManifest.xml.
106 int id = item.getItemId();
107
108 //noinspection SimplifiableIfStatement
109 if (id == R.id.action_settings) {
110 return true;
111 }
112
113 return super.onOptionsItemSelected(item);
114 }
115 }
3、运行结果分析
OK,现在点击界面上的任何一个按钮就可以播放视频文件了。由于两个按钮的响应只是读取资源的方式不同,播放效果是一样的,所以下面只给出点击左边按钮PLAYCARD后的播放图:
左边这张为程序运行后,没有任何操作的图片,右边是正在播放视频的图片。
可以发现两个有趣的现象:
1、在布局文件activity_main.xml中已将videoView组件在父容器中的设置为水平居中(android:layout_centerHorizontal="true"),而且运行初始界面也是正常的,但是视频播放后,该组件就偏向了左边;
2、播放过程中会有系统的控制栏出现在下方,可以点击进行视屏的播放控制,但是在测试很不稳定,出现一两秒就会消失;
针对第一个问题,查了一些资料,有人说是因为默认情况下,如果视频分辨率小于设备的屏幕分辨率,VideoView在播放视频时都是在左上角显示的,将VideoView组件的gravity属性设置为center即可。但是在xml文件及UI编辑属性栏均没有找到VideoView组件有该属性,难道和版本有关?也有人说和底层的Player有关,虽然没有进行替换测试,但感觉这个原因比较靠谱。至于第二个问题,暂时没有找到合理的解释,以后继续找吧。
有知道真实原因的大神,欢迎一起讨论啊!