Android三种播放视频的方式(以下内容大多使用真机测试,所以没有运行图片,大家可以自己实战看看)
1、使用其自带的播放器。指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。
2、使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。
3、使用MediaPlayer类和SurfaceView来实现,这种方式很灵活。
使用前先添加权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
1、调用其自带的播放器:
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Movie.mp4");
//调用系统自带的播放器
Intent intent = new Intent(Intent.ACTION_VIEW);
Log.v("URI:::::::::", uri.toString());
intent.setDataAndType(uri, "video/mp4");
startActivity(intent);
2、使用VideoView来播放)
使VideoView主要有以下方法:
方法名 | 作用 |
---|---|
setVideoPath() | 设置要播放的视频的文件路径 |
start() | 开始或继续播放 |
pause() | 暂定播放 |
resume() | 重新从头开始播放 |
seekTo() | 从指定位置开始播放 |
isPlaying() | 判断当前是否正在播放视频 |
getDuration() | 获取载入视频的播放时长 |
- 做个小Demo练练手吧:
布局文件里放了三个Button,分别为播放,暂停,重新播放,在下面是一个VideoView控件,上代码:
<LinearLayout 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:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_play"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Play"
android:textAllCaps="false" />
<Button
android:id="@+id/btn_pause"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Pause"
android:textAllCaps="false" />
<Button
android:id="@+id/btn_replay"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Replay"
android:textAllCaps="false" />
</LinearLayout>
<VideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
再看完整java代码实例:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private VideoView videoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在 onCreate 里,对界面的按钮和显示位置实例化,并检查权限
videoView = (VideoView)findViewById(R.id.video_view);
Button btn_play = (Button)findViewById(R.id.btn_play);
Button btn_pause = (Button)findViewById(R.id.btn_pause);
Button btn_replay = (Button)findViewById(R.id.btn_replay);
btn_play.setOnClickListener(this);
btn_pause.setOnClickListener(this);
btn_replay.setOnClickListener(this);
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}else {
initVideoPath();//初始化MediaPlayer
}
}
单独写一个方法做视频播放的初始化
private void initVideoPath() {
File file = new File(Environment.getExternalStorageDirectory(), "movie.mp4");
videoView.setVideoPath(file.getPath());//指定视频文件路径
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.setLooping(true);//让电影循环播放
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
initVideoPath();
}else{
Toast.makeText(this, "拒绝权限,无法使用程序。", Toast.LENGTH_LONG).show();
finish();
}
break;
default:
break;
}
}
在一个 onClick 方法中,统一处理 Play(播放)、Pause(暂停)、Replay(重新播放)的逻辑。
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_play:
if(!videoView.isPlaying()){
videoView.start();//播放
}
break;
case R.id.btn_pause:
if(videoView.isPlaying()){
videoView.pause();//暂停
}
break;
case R.id.btn_replay:
if(videoView.isPlaying()){
videoView.resume();//重新播放
}
break;
}
}
由于视频播放属于比较占用资源,所以程序最后要释放资源
//执行完毕,释放所有资源。
@Override
protected void onDestroy() {
super.onDestroy();
if(videoView != null){
videoView.suspend();
}
}
}
3、使用MediaPlayer,MediaPlayer优点多,灵活性强,但是难度较大。一般我们都使用surfaceview+mediaplayer的方式来播放视频,让我们来好好看看如何使用它:
- 步骤:
1)获得MediaPlayer实例:
可以使用直接new的方式:
MediaPlayer mp = new MediaPlayer();
也可以使用create的方式,如:
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//这时就不用调用setDataSource了
调用player.setDataSource()方法设置要播放的资源,可以是文件、文件路径、或者URL。
MediaPlayer的setDataSource一共四个方法:
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)
- 如何设置要播放的文件:
MediaPlayer要播放的文件主要包括3个来源:
a. 用户在应用中事先自带的resource资源
例如:MediaPlayer.create(this, R.raw.test);
b. 存储在SD卡或其他文件路径下的媒体文件
例如:mp.setDataSource("/sdcard/test.mp3");
c. 网络上的媒体文件
例如:mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");(该网址可能已经失效)
3)调用MediaPlayer.setDisplay(holder)设置surfaceHolder,surfaceHolder可以通过surfaceview的getHolder()方法获得。
4)调用MediaPlayer.prepare()来准备。
5)调用MediaPlayer.start()来播放视频。
这是大致步骤,但只有这些是不够的
在第二步之前需要确保surfaceHolder已经准备好了。因此需要给surfaceHolder设置一个callback,
调用addCallback()方法。Callback 有三个回调函数,如下:
SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
surfaceCreated()会在SurfaceHolder被创建的时候回调,在这里可以做一些初始化的操作,surfaceDestroyed()会在SurfaceHolder被销毁的时候回调,在这里可以做一些释放资源的操作,防止内存泄漏。
一般,会在surfaceCreated中给MediaPlayer设置surfaceHolder。
@Override
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
}
那么具体如何操作呢,看代码:
public class MainActivity extends AppCompatActivity {
private SurfaceView surfaceView;
private MediaPlayer player;
private SurfaceHolder holder;
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
progressBar= (ProgressBar) findViewById(R.id.progressBar);
player=new MediaPlayer();
try {
File file = new File(Environment.getExternalStorageDirectory(),
"2d1c41ae2482271297c2b6b4e6abf2cf.mp4");//播放手机相册里的视频
player.setDataSource(file.getPath());
holder=surfaceView.getHolder();
holder.addCallback(new MyCallBack());
//player.prepare();
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
progressBar.setVisibility(View.INVISIBLE);
player.start();
player.setLooping(true);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
private class MyCallBack implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
}
MediaPlayer的具体方法介绍:
方法名 | 功能描述 |
---|---|
void setDataSource(String path) | 通过一个具体的路径来设置MediaPlayer的数据源,path可以是本地的一个路径,也可以是一个网络路径 |
void setDataSource(Context context, Uri uri) | 通过给定的Uri来设置MediaPlayer的数据源,这里的Uri可以是网络路径或是一个ContentProvider的Uri。 |
void setDataSource(MediaDataSource dataSource) | 通过提供的MediaDataSource来设置数据源 |
void setDataSource(FileDescriptor fd) | 通过文件描述符FileDescriptor来设置数据源 |
int getCurrentPosition() | 获取当前播放的位置 |
int getAudioSessionId() | 返回音频的session ID |
int getDuration() | 得到文件的时间 |
TrackInfo[] getTrackInfo() | 返回一个track信息的数组 |
boolean isLooping () | 是否循环播放 |
boolean isPlaying() | 是否正在播放 |
void pause() | 暂停 |
void start() | 开始 |
void stop() | 停止 |
void prepare() | 同步的方式装载流媒体文件。 |
void prepareAsync() | 异步的方式装载流媒体文件。 |
void reset() | 重置MediaPlayer至未初始化状态。 |
void release () | 回收流媒体资源。 |
void seekTo(int msec) | 指定播放的位置(以毫秒为单位的时间) |
void setAudioStreamType(int streamtype) | 指定流媒体类型 |
void setLooping(boolean looping) | 设置是否单曲循环 |
void setNextMediaPlayer(MediaPlayer next) | 当 当前这个MediaPlayer播放完毕后,MediaPlayer next开始播放 |
void setWakeMode(Context context, int mode) | 设置CPU唤醒的状态。 |
setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) | 网络流媒体的缓冲变化时回调 |
setOnCompletionListener(MediaPlayer.OnCompletionListener listener) | 网络流媒体播放结束时回调 |
setOnErrorListener(MediaPlayer.OnErrorListener listener) | 发生错误时回调 |
setOnPreparedListener(MediaPlayer.OnPreparedListener listener) | 当装载流媒体完毕的时候回调。 |
用MediaPlayer播放音频
音频一般也是用MediaPlayer播放的,MediaPlayer的使用上面已经讲过了,在音频的使用和视频大同小异,所以我们不再赘述,实战感受一下吧。
我们来写个播放音频的项目试试
- 布局文件很简单,就三个Button,分别为播放,暂停,停止。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_play"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击播放"/>
<Button
android:id="@+id/btn_pause"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击暂停"/>
<Button
android:id="@+id/btn_stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击停止播放"/>
</LinearLayout>
- 再到java文件里实现代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button playBtn,resumeBtn,pauseBtn;
private MediaPlayer mediaPlayer ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pauseBtn = findViewById(R.id.btn_pause);
stopBtn = findViewById(R.id.btn_stop);
playBtn = findViewById(R.id.btn_play);
playBtn.setOnClickListener(this);
pauseBtn.setOnClickListener(this);
stopBtn.setOnClickListener(this);
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE
},1);
}else {
init();
}
}
private void init(){
try{
File file = new File(Envioronment.getExternalStorageDirectory(),"2d1c41ae2482271297c2b6b4e6abf2cf.mp4");
mediaPlayer.setDataSource(file.getPath());
mediaPlayer.setprepare();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull
int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length >0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED){
init();
}else{
Toast.makeText(this,"拒绝权限将无法使用程序",
Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_play:
if (!mediaPlayer.isPlaying()){
mediaPlayer.start();//开始播放
}
break;
case R.id.btn_pause:
if (mediaPlayer.isPlaying()){
mediaPlayer.pause();//暂停播放
}
break;
case R.id.btn_stop:
if (mediaPlayer.isPlaying()){
mediaPlayer.reset();//停止播放
init();
}
break;
default:
break;
}
}
//释放相关资源
@Override
protected void onDestroy() {
super.onDestroy();
if (mediaPlayer != null){
mediaPlayer.stop();
mediaPlayer.release();
}
}
}
- 最后千万不要忘了权限声明