目录
9.动画与多媒体(二)
2.播放音频与视频
Android提供了对常用音频和视频格式的支持,它所支持的音频格式有MP3 ( .mp3)、3GPP(.3gp)、Ogg (.ogg)和WAVE (.wav)等,支持的视频格式有3GPP (.3gp) 和MPEG-4 (.mp4)等。通过Android API提供的相关方法,可以在Android中实现音频与视频的播放。
2-1.使用MediaPlayer类播放音频
在Android中,提供了MediaPlayer 类来播放音频。使用MediaPlayer 类播放音频比较简单,只需创建该类的对象,并为其指定要播放的音频文件,然后调用该类的start()方法播放即可。MediaPlayer中有许多方法,其中比较常用的方法及其描述如表9.6 所示。
表9.6 MediaPlayer 类中的常用方法
方法 | 描述 |
---|---|
create(Context context, int resid) | 根据指定的资源ID创建一个 MediaPlayer 对象 |
create(Context context, Uri uri) | 根据指定的URI创建一个 MediaPlayer 对象 |
setDataSource() | 指定要装载的资源 |
prepare() | 准备播放(在播放前调用) |
start() | 开始播放 |
stop() | 停止播放 |
pause() | 暂停播放 |
reset() | 恢复MediaPlayer到未初始化状态 |
下面对如何使用MediaPlayar类播放音频进行详细介绍。
1.创建MediaPlayer对象并装载音频文件
创建MediaPlayer对象并装载音频文件,可以使用MediaPlayer类提供的静态方法create()来实现,也可以通过其无参构造方法来创建并实例化该类的对象来实现。
(1)使用create()方法创建MediaPlayer对象并装载音频文件。
MediaPlayer类的静态方法create()常用的语法格式有以下两种:
◆create(Context context, int resid)
用于从资源ID所对应的资源文件中装载音频,并返回新创建的MediaPlayer对象。例如,要创建装载音频资源(res\raw\d.wav) 的MediaPlayer对象,可以使用下面的代码:
MediaPlayer player=MediaPlayer.create(this,R.raw.d);
◆create(Context context, Uri uri)
用于根据指定的URI来装载音频并返回新创建的MediaPlayer对象。例如要创建装载了音频文件(URI地址为https://www.hhh.com/sound/hhh.mp3)的MediaPlayer对象,可以使用下面的代码:
MediaPlayer player=MediaPlayer.create(this,Uri.parse("https://www.hhh.com/sound/hhh.mp3"));
注:在访问网络中的资源时,要在AndroidManifest.xml文件中授于该程序访问网络的权限,具体的授权代码如下:
<uses-permission android:name="android.permission.INTERNET"/>
(2)通过无参的构造方法来创建MediaPlayer对象并装载音频文件。
使用无参的构造方法来创建MediaPlayer对象时,需要单独指定要装载的资源,这可以使用MediaPlayer类的setDataSource()方法实现。
在使用setDataSource()方法装载音频文件后,实际上MediaPlayer并未真正装载该音频文件,还需要调用MediaPlayer的prepare()方法去真正装载音频文件。使用无参的构造方法来创建MediaPlayer对象并装载指定的音频文件,可以使用下面的代码:
MediaPlayer player1=new MediaPlayer();
try {
player1.setDataSource("/sdcard/music.mp3");//指定要装载的音频文件
player1.prepare();//预加载音频
}catch (I0Exception e) {
e.printStackTrace();
}
注:通过MediaPlayer类的静态方法create()来创建MediaPlayer对象时,已经装载了要播放的音频,所以这种方法适用于播放单独的音频文件时。而通过无参的构造方法来创建MediaPlayer对象并装载音频文件时,可以根据需要来随时改变要加载的文件,所以这种方法适用于连续播放多个文件时。
2.开始或恢复播放
在获取到MediaPlayer对象后,就可以使用MediaPlayer类提供的start()方法来开始播放音频或恢复播放已经暂停的音频。例如,已经创建了一 个名称为“player” 的对象,并且装载了要播放的音频,可以使用下面的代码播放该音频:
player.start();//开始播放
3.停止播放
使用MediaPlayer类提供的stop()方法可以停止正在播放的音频。例如,已经创建了一个名称为“player” 的对象,并且已经开始播放装载的音频,可以使用下面的代码停止播放该音频:
player.stop();//停止播放
4.暂停播放
使用MediaPlayer类提供的pause()方法可以暂停正在播放的音频。例如,已经创建了一个名称为“player”的对象,并且已经开始播放装载的音频,可以使用下面的代码暂停播放该音频:
player.pause();//暂停播放
例:
MainActivity.java
package com.example.musicplayer;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaParser;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
private MediaPlayer mediaPlayer;//定义MediaPlayer对象
private boolean isPause = false;//定义是否暂停
private File file;//定义要播放的音频文件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar=getSupportActionBar();
actionBar.hide();
//设置全屏显示
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
//获取“播放/暂停”按钮
final ImageButton btn_play=(ImageButton) findViewById(R.id.main_ib1);
//获取“停止”按钮
final ImageButton btn_stop=(ImageButton) findViewById(R.id.main_ib2);
file=new File("/sdcard/music.mp3");
//如果音频文件存在
if(file.exists()){
//创建MediaPlayer对象,并解析要播放的音频文件
mediaPlayer=MediaPlayer.create(this, Uri.parse(file.getAbsolutePath()));
}else{
//提示音频文件不存在
Toast.makeText(MainActivity.this, "要播放的音频文件不存在!", Toast.LENGTH_SHORT).show();
return;
}
//为MediaPlayer添加完成事件监听器,实现当音频播放完毕后,重新开始播放音频
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
play();//调用play()方法,实现播放功能
}
});
btn_play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mediaPlayer.isPlaying() && !isPause){//暂停播放
mediaPlayer.pause();
isPause=true;
//更换为播放图标
((ImageButton) view).setImageDrawable(getResources()
.getDrawable(R.drawable.play,null));
}else{//继续播放
mediaPlayer.start();
//更换为暂停图标
((ImageButton) view).setImageDrawable(getResources()
.getDrawable(R.drawable.pause,null));
isPause=false;
}
}
});
btn_stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mediaPlayer.stop();//停止播放
//更换为播放图标
btn_play.setImageDrawable(getResources()
.getDrawable(R.drawable.play,null));
}
});
}
//音频播放
private void play(){
try{
mediaPlayer.reset();//重置MediaPlayer对象
mediaPlayer.setDataSource(file.getAbsolutePath());//重新设置要播放的音频
mediaPlayer.prepare();//预加载音频
mediaPlayer.start();//开始播放
} catch (Exception e) {
e.printStackTrace();//输出异常信息
}
}
//当前Activity销毁时
protected void onDestory(){
if(mediaPlayer.isPlaying()){
mediaPlayer.stop();
}
mediaPlayer.release();//释放资源
super.onDestroy();
}
}
注:要访问SD卡的文件,需要在AndroidManifest.xml文件中赋予程序访问SD卡的权限,关键代码如下:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
application节点下
android:requestLegacyExternalStorage="true"
2-2.使用SoundPool类播放音频
Android还提供了另一个播放音频的类——SoundPool (即音频池),可以同时播放多个短小的音频,而且占用的资源较少。使用SoundPool类播放音频,首先需要创建SoundPool对象,然后加载所要播放的音频,最后调用play()方法播放音频。
1.创建SoundPool对象
SoundPool类提供了一个构造方法,用来创建SoundPool对象,该构造方法的语法格式如下:
SoundPool(int maxStreams,int streamType,int srcQuality)
参数说明如下:
◆maxStreams:指定可以容纳多少个音频。
◆streamType::指定声音类型,可以通过AudioManager类提供的常量进行指定,通常使用STREAM_MUSIC。
◆srcQuality:指定音频的品质,默认值为0。
例如,创建个可以容纳 10个音频的SoundPool对象,可以使用下面的代码:
//创建一个SoundPoo1对象, 该对象可以容纳10个音频流
oundPool soundpool = new SoundPool(10,AudioManager.STREAM_SYSTEM, 0);
2.加载所要播放的音频
创建SoundPool 对象后,可以调用load()方法来加载要播放的音频。load()方法的语法格式有以下4种:
◆public int load (Context context, int resld, int priority):通过指定的资源ID来加载音频。
◆public int load (String path, int priority):通过音频文件的路径来加载音频。
◆public int load (AssetFileDescriptor afd, int priority):从AssetFileDescriptor所对应的文件中加载音频。
◆public int load (FileDescriptor fd, long offset, long length, int priority):加载FileDescriptor对象中从offset开始,长度为length的音频。
例如,要通过资源ID来加载音频文件ding.wav,可以使用下面的代码:
soundpool.load(this,R.raw.ding,1);
注:为了更好地管理所加载的每个音频,一般使用HashMap<Integer, Integer>对象来管理这些音频。这时可以先创建一个HashMap<Integer, Integer>对象,然后应用该对象的put()方法将加载的音频保存到该对象中。例如,创建一个HashMap<Integer, Integer>对象,并应用put() 方法添加一个音频,可以使用下面的代码:
//创建一个HashMap对象
HashMap<Integer,Integer> soundmap = new HashMap<Integer,Integer>();
soundmap.put(1, soundpool.load(this, R.raw.chimes, 1));
3.播放音频
调用SoundPool对象的play()方法可播放指定的音频。play() 方法的语法格式如下:
play (int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
play()方法各参数的说明如表9.7 所示。
表9.7 play() 方法的参数说明
参数 | 描述 |
---|---|
soundID | 指定要播放的音频,该音频为通过load() 方法返回的音频 |
leftVolume | 指定左声道的音量,取值范围为0.0~1.0 |
rightVolume | 指定右声道的音量,取值范围为0.0~1.0 |
priority | 指定播放音频的优先级,数值越大,优先级越高 |
loop | 指定循环次数,0为不循环,-1 为循环 |
rate | 指定速率,正常为1,最低为0.5, 最高为2 |
例如,要播放raw资源中保存的音频文件notify.wav,可以使用下面的代码:
//播放指定的音频
soundpool.play(soundpool.load(MainActivity.this,R.raw.notify, 1), 1, 1, 0, 0, 1);
例:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".MainActivity">
<ListView
android:id="@+id/main_lv1"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_gravity="center"
android:id="@+id/title"
android:textSize="22sp"
/>
</LinearLayout>
MainActivity.java
package com.example.selectringtone;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.media.AudioAttributes;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar=getSupportActionBar();
actionBar.hide();
ListView listView=(ListView) findViewById(R.id.main_lv1);//获取列表视图
String[] title=new String[]{"江南","关键词","爱不会绝迹","修炼爱情",
"那些你很冒险的梦","爱笑的眼睛","一千年以后"};//定义并初始化保存列表项文字的数组
//创建一个list集合
List<Map<String,Object>> listItems=new ArrayList<Map<String,Object>>();
//通过for循环将列表项文字保存到Map中,并添加到List集合中
for(int i=0;i<title.length;i++){
Map<String,Object> map=new HashMap<String,Object>();//实例化Map对象
map.put("name",title[i]);
listItems.add(map);//将Map对象添加到List集合中
}
AudioAttributes attr=new AudioAttributes.Builder()//设置音效相关属性
.setUsage(AudioAttributes.USAGE_GAME)//设置音效使用场景
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)//设置音效的类型
.build();
final SoundPool soundPool=new SoundPool.Builder()//创建SoundPool对象
.setAudioAttributes(attr)//设置音效池的属性
.setMaxStreams(10)//设置最多可容纳10个音频流
.build();
final HashMap<Integer,Integer> soundmap=new HashMap<Integer,Integer>();
//保存要播放的音频流到HashMap对象中
soundmap.put(0,soundPool.load(this,R.raw.jiangnan,1));
soundmap.put(1,soundPool.load(this,R.raw.guanjianci,1));
soundmap.put(2,soundPool.load(this,R.raw.aibuhuijueji,1));
soundmap.put(3,soundPool.load(this,R.raw.xiulianaiqing,1));
soundmap.put(4,soundPool.load(this,R.raw.naxienihenmaoxiandemeng,1));
soundmap.put(5,soundPool.load(this,R.raw.aixiaodeyanjing,1));
soundmap.put(6,soundPool.load(this,R.raw.yiqiannianyihou,1));
//创建SimpleAdapter适配器
SimpleAdapter adapter=new SimpleAdapter(this,listItems,
R.layout.main,new String[]{"name",},new int[]{R.id.title});
listView.setAdapter(adapter);//将适配器与ListView关联
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
//获取选项的值
Map<String,Object> map=(Map<String, Object>) adapterView.getItemAtPosition(i);
//播放所选音频
soundPool.play(soundmap.get(i),1,1,0,0,1);
}
});
}
}
2-3.使用 VideoView组件播放视频
在Android中提供了VideoView组件用于播放视频文件。要想使用VideoView组件播放视频,首先需要在布局文件中添加该组件, 然后在Activity 中获取该组件,并应用其setVideoPath()方法或setVideoURI()方法加载要播放的视频,最后调用start()方法来播放视频。另外,VideoView组件还提供了stop()和pause()方法,分别用于停止和暂停视频的播放。
在布局文件中添加VideoView组件的基本语法格式如下:
<VideoView
属性列表>
</VideoView>
VideoView组件支持的XML属性如表9.8所示。
表9.8 VideoView 组件支持的XML 属性
XML属性 | 描述 |
---|---|
android:id | 设置组件的ID |
android:background | 设置背景,可以设置背景图片,也可以设置背景颜色 |
android:layout_gravity | 设置对齐方式 |
android:layout_width | 设置宽度 |
android:layout_height | 设置高度 |
在Android中还提供了一个可以与VideoView组件结合使用的MediaController组件。MediaController组件用于通过图形控制界面来控制视频的播放。
例:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".MainActivity">
<VideoView
android:id="@+id/main_vv1"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
MainActivity.java
package com.example.videoviewandmediacontroller;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.view.WindowManager;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;
import java.io.File;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar=getSupportActionBar();
actionBar.hide();
//设置全屏显示
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
VideoView videoView=(VideoView) findViewById(R.id.main_vv1);
//指定播放模拟器SD卡上的视频文件
File file=new File(Environment.getExternalStorageDirectory()+"/aishang.mp4");
//创建android.widget.MediaController对象,控制视频的播放
MediaController mediaController=new MediaController(MainActivity.this);
if(file.exists()){//判断要播放的视频文件是否存在
videoView.setVideoPath(file.getAbsolutePath());//指定要播放的视频
videoView.setMediaController(mediaController);//设置VideoView与MediaController相关联
videoView.requestFocus();//让VideoView获得焦点
try {
videoView.start();//开始播放视频
} catch (Exception e) {
e.printStackTrace();//输出异常信息
}
//为VideoView添加完成事件监听器,实现视频播放结束后的提示信息
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
//弹出消息提示框显示播放完毕
Toast.makeText(MainActivity.this, "视频播放完毕!", Toast.LENGTH_SHORT).show();
}
});
}
else {
//弹出消息提示框提示文件不存在
Toast.makeText(MainActivity.this,"要播放的视频文件不存在", Toast.LENGTH_SHORT).show();
}
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
AndroidManifest.xml的application节点下:
android:requestLegacyExternalStorage="true"
4.难点解答
4-1.MediaPlayer 与SoundPool的区别
使用MediaPlayer每次只能播放一-个音频,适用于播放长音乐或是背景音乐;使用SoundPool可以同时播放多个短小的音频,而且占用的资源较少,适用于播放按键音或者消息提示音等。
4-2.MediaController 的作用
MediaController是一个与MediaPlayer相匹配的Android控件,它主要集合了媒体播放器的控制功能,用于控制MediaPlayer播放状态的同步。通常包含“播放/暂停”“后退”“快进”和进度滑块等按钮。实例化此类后,MediaController 将创建一组默认的控件, 并且浮动在界面的最上层。如果空闲3秒钟,窗口将消失,当用户触摸锚定视图时,窗口将重新出现。