为何那么决绝?不给我喘气的机会。
突然就想搞一下视频播放,就查了一下资料。网上大牛真多,都讲的很详细。我就纵览细品了一下大牛们的代码。然后就随心写了一个综合的小例子。
其中涉及到:视频播放,文件选择等。用的重点内容:MediaPlayer SurfaceView SurfaceHolder VideoView 除此之外还有最基本的:Activity/Intent/Toast/Dialog/Menu等等。比较适合有一定基础的菜鸟。大鸟可选择性飞过。代码比较多。亲测。
一、视频播放的三种方法
第一种:调用系统的视频播放器
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "video/mp4");
startActivity(intent);
第二种:使用VideoView控件
videoView.setMediaController(new MediaController(MainActivity.this));
videoView.setVideoURI(uri);
videoView.start();
videoView.requestFocus();
第三种:使用MediaPlayer和SurfaceView自定义播放
MediaPlayer player = new MediaPlayer();
二、具体实现
这里我有两个Activity分别为:MainActivity和CustomActivity其中MainActivity用于控件的播放CustomActivity用于自定义的播放
还有一个CallbackBundle接口和自定义的OpenFileDialog类,这两个类用于弹出一个对话框让用户选择视频文件
注:CallbackBundle和OpenFileDialog是从RobinTang大牛(http://blog.csdn.net/trbbadboy/article/details/7899424)哪里拿来用的,写的真是太好了,所以一点没改直接用。太感谢大牛了。
上代码:
MainActivity.java
package com.study.myshipin;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;
public class MainActivity extends Activity {
private VideoView videoView = null;
private Uri uri = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()
+ "/test.mp4");
videoView = (VideoView) findViewById(R.id.videoView1);
Toast.makeText(MainActivity.this, "请在菜单里操作", Toast.LENGTH_LONG).show();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
menu.add(0, 1, 1, "控件播放");
menu.add(0, 2, 2, "调用系统");
menu.add(0, 3, 3, "自己定义");
menu.add(0, 4, 5, "退出系统");
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
if (item.getItemId() == 1) {
videoView
.setMediaController(new MediaController(MainActivity.this));
videoView.setVideoURI(uri);
videoView.start();
videoView.requestFocus();
} else if (item.getItemId() == 2) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "video/mp4");
startActivity(intent);
} else if (item.getItemId() == 3) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, CustomActivity.class);
startActivity(intent);
} else if (item.getItemId() == 4) {
finish();
}
return super.onOptionsItemSelected(item);
}
}
CustomActivity.java
package com.study.myshipin;
import java.util.HashMap;
import java.util.Map;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnErrorListener;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.EditText;
import android.widget.Toast;
public class CustomActivity extends Activity implements SurfaceHolder.Callback {
private SurfaceView surfaceView = null;
private MediaPlayer player = null;
private SurfaceHolder holder = null;
private String path = null;
private static final int OPEN_FILE_DIALOG_ID = 0;
@Override
@Deprecated
protected Dialog onCreateDialog(int id) {
// TODO Auto-generated method stub
if (id == OPEN_FILE_DIALOG_ID) {
Map<String, Integer> images = new HashMap<String, Integer>();
// 下面几句设置各文件类型的图标, 需要你先把图标添加到资源文件夹
images.put(OpenFileDialog.sRoot, R.drawable.filedialog_root); // 根目录图标
images.put(OpenFileDialog.sParent, R.drawable.filedialog_folder_up); // 返回上一层的图标
images.put(OpenFileDialog.sFolder, R.drawable.filedialog_folder); // 文件夹图标
images.put("mp4", R.drawable.filedialog_videofile); // video文件图标
images.put("mp3", R.drawable.filedialog_wavfile); // wav文件图标
images.put(OpenFileDialog.sEmpty, R.drawable.filedialog_root);
Dialog dialog = OpenFileDialog.createDialog(id, this, "打开文件",
new CallbackBundle() {
@Override
public void callback(Bundle bundle) {
String filepath = bundle.getString("path");
path = filepath;
}
}, ".mp4;", images);
return dialog;
}
return null;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
Toast.makeText(CustomActivity.this, "影布已就绪", Toast.LENGTH_SHORT).show();
Toast.makeText(CustomActivity.this, "请在菜单里操作", Toast.LENGTH_LONG)
.show();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
Toast.makeText(CustomActivity.this, "影布已销毁", Toast.LENGTH_SHORT).show();
}
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.custom);
path = Environment.getExternalStorageDirectory().getPath()
+ "/test.mp4";
player = new MediaPlayer();
surfaceView = (SurfaceView) findViewById(R.id.surfaceView1);
holder = surfaceView.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(this);
player.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
// TODO Auto-generated method stub
Toast.makeText(CustomActivity.this, "缓冲中,请等候",
Toast.LENGTH_SHORT).show();
}
});
player.setOnErrorListener(new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// TODO Auto-generated method stub
Toast.makeText(CustomActivity.this, "出错啦", Toast.LENGTH_SHORT)
.show();
return false;
}
});
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
if (player.isPlaying()) {
player.stop();
}
player.release();
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
menu.add(1, 1, 1, "播放");
menu.add(1, 2, 2, "暂停");
menu.add(1, 3, 3, "停止");
menu.add(1, 4, 4, "本地");
menu.add(1, 5, 5, "网络");
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
if (item.getItemId() == 1) {
try {
player.reset();
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setDataSource(path);
player.setDisplay(holder);
player.prepare();
player.start();
} catch (Exception e) {
e.printStackTrace();
}
} else if (item.getItemId() == 2) {
if (player.isPlaying()) {
player.pause();
} else {
player.start();
}
} else if (item.getItemId() == 3) {
if (player.isPlaying()) {
player.stop();
}
} else if (item.getItemId() == 4) {
showDialog(OPEN_FILE_DIALOG_ID);
} else if (item.getItemId() == 5) {
final EditText editText = new EditText(CustomActivity.this);
new AlertDialog.Builder(CustomActivity.this).setTitle("请输入视频地址")
.setIcon(android.R.drawable.ic_dialog_info)
.setView(editText)
.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
path = editText.getText().toString().trim();
Toast.makeText(CustomActivity.this, path,
Toast.LENGTH_SHORT).show();
}
}).setNegativeButton("取消", null).show();
}
return super.onOptionsItemSelected(item);
}
}
CallbackBundle.java
package com.study.myshipin;
import android.os.Bundle;
public interface CallbackBundle {
void callback(Bundle bundle);
}
OpenFileDialog.java
package com.study.myshipin;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
public class OpenFileDialog {
public static String tag = "OpenFileDialog";
static final public String sRoot = "/";
static final public String sParent = "..";
static final public String sFolder = ".";
static final public String sEmpty = "";
static final private String sOnErrorMsg = "No rights to access!";
// 参数说明
// context:上下文
// dialogid:对话框ID
// title:对话框标题
// callback:一个传递Bundle参数的回调接口
// suffix:需要选择的文件后缀,比如需要选择wav、mp3文件的时候设置为".wav;.mp3;",注意最后需要一个分号(;)
// images:用来根据后缀显示的图标资源ID。
// 根目录图标的索引为sRoot;
// 父目录的索引为sParent;
// 文件夹的索引为sFolder;
// 默认图标的索引为sEmpty;
// 其他的直接根据后缀进行索引,比如.wav文件图标的索引为"wav"
public static Dialog createDialog(int id, Context context, String title,
CallbackBundle callback, String suffix, Map<String, Integer> images) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setView(new FileSelectView(context, id, callback, suffix,
images));
Dialog dialog = builder.create();
// dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setTitle(title);
return dialog;
}
static class FileSelectView extends ListView implements OnItemClickListener {
private CallbackBundle callback = null;
private String path = sRoot;
private List<Map<String, Object>> list = null;
private int dialogid = 0;
private String suffix = null;
private Map<String, Integer> imagemap = null;
public FileSelectView(Context context, int dialogid,
CallbackBundle callback, String suffix,
Map<String, Integer> images) {
super(context);
this.imagemap = images;
this.suffix = suffix == null ? "" : suffix.toLowerCase();
this.callback = callback;
this.dialogid = dialogid;
this.setOnItemClickListener(this);
refreshFileList();
}
private String getSuffix(String filename) {
int dix = filename.lastIndexOf('.');
if (dix < 0) {
return "";
} else {
return filename.substring(dix + 1);
}
}
private int getImageId(String s) {
if (imagemap == null) {
return 0;
} else if (imagemap.containsKey(s)) {
return imagemap.get(s);
} else if (imagemap.containsKey(sEmpty)) {
return imagemap.get(sEmpty);
} else {
return 0;
}
}
private int refreshFileList() {
// 刷新文件列表
File[] files = null;
try {
files = new File(path).listFiles();
} catch (Exception e) {
files = null;
}
if (files == null) {
// 访问出错
Toast.makeText(getContext(), sOnErrorMsg, Toast.LENGTH_SHORT)
.show();
return -1;
}
if (list != null) {
list.clear();
} else {
list = new ArrayList<Map<String, Object>>(files.length);
}
// 用来先保存文件夹和文件夹的两个列表
ArrayList<Map<String, Object>> lfolders = new ArrayList<Map<String, Object>>();
ArrayList<Map<String, Object>> lfiles = new ArrayList<Map<String, Object>>();
if (!this.path.equals(sRoot)) {
// 添加根目录 和 上一层目录
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", sRoot);
map.put("path", sRoot);
map.put("img", getImageId(sRoot));
list.add(map);
map = new HashMap<String, Object>();
map.put("name", sParent);
map.put("path", path);
map.put("img", getImageId(sParent));
list.add(map);
}
for (File file : files) {
if (file.isDirectory() && file.listFiles() != null) {
// 添加文件夹
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", file.getName());
map.put("path", file.getPath());
map.put("img", getImageId(sFolder));
lfolders.add(map);
} else if (file.isFile()) {
// 添加文件
String sf = getSuffix(file.getName()).toLowerCase();
if (suffix == null
|| suffix.length() == 0
|| (sf.length() > 0 && suffix.indexOf("." + sf
+ ";") >= 0)) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", file.getName());
map.put("path", file.getPath());
map.put("img", getImageId(sf));
lfiles.add(map);
}
}
}
list.addAll(lfolders); // 先添加文件夹,确保文件夹显示在上面
list.addAll(lfiles); // 再添加文件
SimpleAdapter adapter = new SimpleAdapter(
getContext(),
list,
R.layout.filedialogitem,
new String[] { "img", "name", "path" },
new int[] { R.id.filedialogitem_img,
R.id.filedialogitem_name, R.id.filedialogitem_path });
this.setAdapter(adapter);
return files.length;
}
@Override
public void onItemClick(AdapterView<?> parent, View v, int position,
long id) {
// 条目选择
String pt = (String) list.get(position).get("path");
String fn = (String) list.get(position).get("name");
if (fn.equals(sRoot) || fn.equals(sParent)) {
// 如果是更目录或者上一层
File fl = new File(pt);
String ppt = fl.getParent();
if (ppt != null) {
// 返回上一层
path = ppt;
} else {
// 返回更目录
path = sRoot;
}
} else {
File fl = new File(pt);
if (fl.isFile()) {
// 如果是文件
((Activity) getContext()).dismissDialog(this.dialogid); // 让文件夹对话框消失
// 设置回调的返回值
Bundle bundle = new Bundle();
bundle.putString("path", pt);
bundle.putString("name", fn);
// 调用事先设置的回调函数
this.callback.callback(bundle);
return;
} else if (fl.isDirectory()) {
// 如果是文件夹
// 那么进入选中的文件夹
path = pt;
}
}
this.refreshFileList();
}
}
}
布局文件:
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
tools:context="${relativePackage}.${activityClass}" >
<VideoView
android:id="@+id/videoView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
custom.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#888888"
tools:context="${relativePackage}.${activityClass}" >
<SurfaceView
android:id="@+id/surfaceView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" />
</RelativeLayout>
filedialogitem.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/vw1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#000000"
android:orientation="horizontal"
android:padding="4dp" >
<ImageView
android:id="@+id/filedialogitem_img"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="4dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/filedialogitem_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/filedialogitem_path"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:textColor="#FFFFFF"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
AndroidManifest.xml部分代码
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@android:style/Theme.Black.NoTitleBar" >
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:screenOrientation="landscape" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".CustomActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:screenOrientation="landscape" />
</application>