Android Video
简述
Video的播放实现,至于录制请参见《Android Camera简述》。
一、播放方式
1
)自带播放器播放
入口Action为ACTION_VIEW,Data为Uri,Type为MIME类型。
- // 自带播放器播放
- private void sysPlayer(String filename) {
- Uri uri = Uri.parse(filename);
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setDataAndType(uri, "video/3gp");
- startActivity(intent);
- }
2
)VideoView
播放
VideoView组件进行播放。在《Android Camera简述》的Camera摄像样例中的预览操作即是这种方式。(例子选择VideoView播放,仍是用的同一个类==)
3
)MediaPlayer
播放
MediaPlayer进行播放控制。不再赘述了==,具体看下节例子。
二、视频播放器
1
)视频播放活动
文件浏览方式,找到3gp文件进行播放。
1)直接点击,以MediaPlayer方式播放(简单的自定义了下界面^^)
2)长按时,可选"自带播放器播放"、"VideoView播放"、"MediaPlayer播放"三种方式
2
)文件浏览器接口
定义了文件夹点击、文件点击、文件长按、错误四方法。
3
)文件列表适配器
自定义文件列表适配器。主要就是getView()内对一个Item布局的的赋值,有图片、名称、类型、大小四项。
4
)文件浏览器
自定义的文件浏览器。
1)属性资源的使用。即在attrs.xml定义的FileBrowser的属性。
不过,extName&fileImage以后缀数字不断增加的好像不好定义,仍是自定义空间方式。
2)接口事件,参见OnFileBrowserListener.java。
- public class FileBrowser extends ListView implements
- AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
- public static final int ERROR_ACCESS_DIR = 1; // 访问目录错误
- /*
- * 这是其他属性的命名空间。
- *
- * 而attrs.xml定义的FileBrowser的属性,命名空间需如下定义,否则取不到属性值。
- * xmlns:空间名="http://schemas.android.com/apk/res/自定义组件所在包名"
- */
- private final String namespace = "http://vaero.blog.51cto.com/";
- /* 自定义属性 */
- private String baseDir; // 初始根目录
- private boolean backFinish; // 是否返回退出
- private int folderImageResId; // 文件夹的图片资源id
- private Map<String, Integer> fileImageResIdMap = new HashMap<String, Integer>(); // 各文件类型的图片资源id
- private int otherFileImageResId; // 其他文件类型的图片资源id
- private Stack<String> dirStack = new Stack<String>(); // 栈方式存放当前目录
- private List<File> fileList = new ArrayList<File>();; // 当前目录内File
- private FileListAdapter fileListAdapter; // 自定义文件列表适配器
- private OnFileBrowserListener listener; // 文件浏览器接口
- public FileBrowser(Context context, AttributeSet attrs) {
- super(context, attrs);
- // 获得TypedArray对象
- TypedArray typedArray = context.obtainStyledAttributes(attrs,
- R.styleable.FileBrowser);
- // 获取baseDir
- baseDir = typedArray.getString(R.styleable.FileBrowser_baseDir);
- if (null == baseDir) { // 未定义时为SDCard目录
- baseDir = Environment.getExternalStorageDirectory().toString();
- }
- setCurrentPath(baseDir); // 由dir设置当前栈
- // 获取backFinish,默认true
- backFinish = typedArray.getBoolean(R.styleable.FileBrowser_backFinish,
- false);
- // 获取folderImage,默认0
- folderImageResId = typedArray.getResourceId(
- R.styleable.FileBrowser_folderImage, 0);
- // 获取otherFileImage,默认0
- otherFileImageResId = typedArray.getResourceId(
- R.styleable.FileBrowser_otherFileImage, 0);
- // 获取extName&fileImage(以后缀数字不断增加的方式动态获取)
- String extName;
- int fileImageResId;
- int index = 1;
- while (true) {
- extName = attrs.getAttributeValue(namespace, "extName" + index);
- fileImageResId = attrs.getAttributeResourceValue(namespace,
- "fileImage" + index, 0);
- // 不存在extName&fileImage属性时跳出循环
- if ("".equals(extName) || extName == null || fileImageResId == 0) {
- break;
- }
- fileImageResIdMap.put(extName, fileImageResId);
- index++;
- }
- // 设置Item点击监听接口
- setOnItemClickListener(this);
- setOnItemLongClickListener(this);
- // 设置当前目录的File
- setCurrentDirFiles();
- // 设置适配器
- fileListAdapter = new FileListAdapter(context, fileList,
- folderImageResId, fileImageResIdMap, otherFileImageResId);
- setAdapter(fileListAdapter);
- }
- // 由dir设置当前栈
- private void setCurrentPath(String dir) {
- dirStack.clear();
- String spDir = dir.charAt(0) == '/' ? dir.substring(1) : dir;
- for (String s : spDir.split("/")) {
- dirStack.push(s);
- }
- }
- // 由栈获取当前目录
- private String getCurrentDir() {
- StringBuffer result = new StringBuffer();
- result.append("/");
- for (int i = 0; i < dirStack.size(); i++) {
- result.append(dirStack.get(i));
- result.append("/");
- }
- return result.toString();
- }
- // 设置当前目录的File
- private boolean setCurrentDirFiles() {
- // 获得当前目录中所有的File对象
- File[] files = new File(getCurrentDir()).listFiles();
- if (null == files) { // 一些系统目录可能获不到,为null
- dirStack.pop();
- return false;
- }
- fileList.clear();
- // 多于一级时,增加null以表示返回上一级
- if (!dirStack.isEmpty())
- fileList.add(null);
- for (File file : files) {
- fileList.add(file);
- }
- // 类型顺序:null、文件夹、文件;同类型顺序:字典顺序
- Collections.sort(fileList, new Comparator<File>() {
- @Override
- public int compare(File f1, File f2) {
- if (null == f1) {
- return -1;
- } else if (null == f2) {
- return 1;
- } else if (f1.isDirectory() && !f2.isDirectory()) {
- return -1;
- } else if (!f1.isDirectory() && f2.isDirectory()) {
- return 1;
- } else {
- return f1.toString().compareTo(f2.toString()); // 字典顺序比较
- }
- }
- });
- return true;
- }
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position,
- long id) {
- File file = fileList.get(position);
- if (file == null) { // null时,返回上一级
- dirStack.pop(); // 出栈
- setCurrentDirFiles(); // 设置当前目录的File
- fileListAdapter.notifyDataSetChanged(); // 通知数据改变,刷新列表
- if (listener != null) {
- listener.onDirItemClick(getCurrentDir());
- }
- } else if (file.isDirectory()) { // 目录时
- dirStack.push(file.getName()); // 压栈
- if (setCurrentDirFiles()) { // 设置当前目录的File
- fileListAdapter.notifyDataSetChanged(); // 通知数据改变,刷新列表
- if (listener != null) {
- listener.onDirItemClick(getCurrentDir());
- }
- } else {
- if (listener != null) {
- listener.onError(ERROR_ACCESS_DIR);
- }
- }
- } else { // 文件时
- if (listener != null) {
- listener.onFileItemClick(file.getAbsolutePath());
- }
- }
- }
- @Override
- public boolean onItemLongClick(AdapterView<?> parent, View view,
- int position, long id) {
- File file = fileList.get(position);
- if (file.isFile()) { // 文件时
- if (listener != null) {
- listener.onFileItemLongClick(file.getAbsolutePath());
- return true;
- }
- }
- return false;
- }
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && !backFinish
- && !dirStack.isEmpty()) {
- dirStack.pop(); // 出栈
- setCurrentDirFiles(); // 设置当前目录的File
- fileListAdapter.notifyDataSetChanged(); // 通知数据改变,刷新列表
- if (listener != null) {
- listener.onDirItemClick(getCurrentDir());
- }
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
- // 设置文件浏览器接口
- public void setOnFileBrowserListener(OnFileBrowserListener listener) {
- this.listener = listener;
- }
- }
5
)MediaPlayer
播放视频
MediaPlayer播放视频。控制界面很简单,描述如下:
1)暂停、播放、停止三按钮
2)进度条实时进度显示,及拖动跳转
3)进度条左侧当前时间、右侧总时间显示
ps:播放画面自适应等比例满屏
- public class VideoPlayer extends Activity implements
- MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener,
- SurfaceHolder.Callback, SeekBar.OnSeekBarChangeListener {
- public static final int STATUS_STOPPED = 1;
- public static final int STATUS_PAUSING = 2;
- public static final int STATUS_PLAYING = 3;
- private MediaPlayer mPlayer; // MediaPlayer对象
- private SurfaceHolder mSurfaceHolder; // SurfaceHolder对象
- private SurfaceView surfaceView; // SurfaceView组件
- private SeekBar seekBar; // SeekBar组件
- private TextView nowTime, totleTime; // TextView组件
- private LinearLayout linearLayout; // 播放控制布局
- private String mTimerFormat = "%02d:%02d"; // 时间格式
- /* 时间更新Handler */
- private final Handler mHandler = new Handler();
- private Runnable mUpdateTimer = new Runnable() {
- public void run() {
- updateTimerView();
- }
- };
- private String filename; // 文件名称
- private int mStatus = STATUS_STOPPED; // 当前状态
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.video_player);
- surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
- seekBar = (SeekBar) findViewById(R.id.seekBar);
- seekBar.setOnSeekBarChangeListener(this);
- nowTime = (TextView) findViewById(R.id.nowTime);
- totleTime = (TextView) findViewById(R.id.totleTime);
- linearLayout = (LinearLayout) findViewById(R.id.linearLayout);
- // 获取文件名称
- filename = getIntent().getStringExtra(VideoPlayerActivity.KEY_FILENAME);
- /* 初始化mSurfaceHolder */
- mSurfaceHolder = surfaceView.getHolder();
- mSurfaceHolder.addCallback(this); // 设置回调接口
- mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 设置为Buffer类型(播放视频&Camera预览)
- /* 初始化MediaPlayer */
- mPlayer = new MediaPlayer();
- mPlayer.setOnPreparedListener(this);
- mPlayer.setOnCompletionListener(this);
- try {
- mPlayer.setDataSource(filename);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- // 暂停按钮
- public void pauseBtn(View v) {
- if (mStatus == STATUS_PLAYING) {
- mPlayer.pause();
- mStatus = STATUS_PAUSING;
- }
- }
- // 播放按钮
- public void playBtn(View v) {
- if (mStatus == STATUS_PAUSING) {
- mPlayer.start(); // 继续播放
- mStatus = STATUS_PLAYING;
- updateTimerView(); // 更新时间
- } else {
- /* 重新开始播放 */
- mPlayer.reset();
- try {
- mPlayer.setDataSource(filename);
- } catch (Exception e) {
- e.printStackTrace();
- }
- mPlayer.prepareAsync(); // 异步准备
- }
- }
- // 停止按钮
- public void stopBtn(View v) {
- if (mStatus != STATUS_STOPPED) {
- mPlayer.stop(); // 停止播放
- mPlayer.reset(); // 重置
- mStatus = STATUS_STOPPED;
- updateTimerView(); // 更新时间
- }
- }
- // 当Surface被创建的时被触发
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- mPlayer.setDisplay(holder); // 指定SurfaceHolder
- mPlayer.prepareAsync(); // 异步准备(将回调OnPreparedListener接口)
- }
- // 当Surface尺寸等参数改变时触发
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width,
- int height) {
- }
- // 当surface销毁时触发
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- }
- @Override
- public void onPrepared(MediaPlayer mp) {
- /* 获得窗口宽长 */
- Display display = getWindowManager().getDefaultDisplay();
- int wWidth = display.getWidth();
- int wHeight = display.getHeight();
- /* 获得视频宽长 */
- int vWidth = mPlayer.getVideoWidth();
- int vHeight = mPlayer.getVideoHeight();
- /* 最适屏幕 */
- float wRatio = (float) vWidth / (float) wWidth; // 宽度比
- float hRatio = (float) vHeight / (float) wHeight; // 高度比
- float ratio = Math.max(wRatio, hRatio); // 较大的比
- vWidth = (int) Math.ceil((float) vWidth / ratio); // 新视频宽度
- vHeight = (int) Math.ceil((float) vHeight / ratio); // 新视频高度
- // 改变SurfaceHolder大小
- mSurfaceHolder.setFixedSize(vWidth, vHeight);
- // 设置新布局参数(这在samsung i9088上出现stretch的错误==)
- // surfaceView.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight));
- // 设置总时间显示
- setTimeView(totleTime, mPlayer.getDuration());
- // 启动播放
- mPlayer.start();
- // 设定状态
- mStatus = STATUS_PLAYING;
- // 更新时间
- updateTimerView();
- }
- @Override
- public void onCompletion(MediaPlayer mp) {
- mStatus = STATUS_STOPPED;
- finish(); // 播放完成后退出
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- // 播放控制布局的显示&隐藏
- linearLayout
- .setVisibility(linearLayout.getVisibility() == View.VISIBLE ? View.GONE
- : View.VISIBLE);
- }
- return super.onTouchEvent(event);
- }
- @Override
- public void onBackPressed() {
- super.onBackPressed();
- stopBtn(null); // 停止
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mPlayer.release(); // 释放
- }
- // 更新时间
- private void updateTimerView() {
- if (mStatus == STATUS_PLAYING) {
- int position = mPlayer.getCurrentPosition();
- int duration = mPlayer.getDuration();
- setTimeView(nowTime, position); // 设置时间显示
- long pos = seekBar.getMax() * position / duration;
- seekBar.setProgress((int) pos); // 设置进度条
- mHandler.postDelayed(mUpdateTimer, 1000);
- } else if (mStatus == STATUS_STOPPED) {
- nowTime.setText("00:00");
- seekBar.setProgress(0);
- }
- }
- // 设置时间显示
- private void setTimeView(TextView textView, int msec) {
- if (msec >= 1000 * 60 * 60) { // >=1h
- textView.setText(String.format(mTimerFormat, msec / 1000 / 60 / 60,
- msec / 1000 / 60 % 60));
- } else {
- textView.setText(String.format(mTimerFormat, msec / 1000 / 60,
- msec / 1000 % 60));
- }
- }
- // SeekBar进度改变时
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress,
- boolean fromUser) {
- }
- // SeekBar开始拖动时
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
- // SeekBar结束拖动时
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- if (mStatus == STATUS_PLAYING) {
- mPlayer.seekTo(seekBar.getProgress() * mPlayer.getDuration()
- / seekBar.getMax()); // 跳转
- }
- }
- }
三、后记
1
)扩展内容
1.1)应用资源系列之属性[Attribute]资源
1.2)RMVB软解,有兴趣的可以找找(我稍搜了下,无发现==)
2
)模块概览
2.1)Video播放
3
)运行效果
3.1)Video播放
3.2)文件浏览器
3.3)长按选择方式
3.4)自带播放器播放
3.5)VideoView播放
3.6)MediaPlayer播放
本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/834887
,如需转载请自行联系原作者