通知
通知能向用户发送一些提示信息,而应用程序不需要再前台运行,通知可以在活动、广播接收器、服务中创建,需要一个 NotificationManager 来对通知进行管理,可以调用 Context 的 getSystemService() 方法获取到,该方法接收一个字符串参数用于确定获取系统的那个服务,获取通知的服务就传入 Context.NOTIFICATION_SERVICE
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
需要使用一个 Builder 构造器来创建 Notification 对象
showNotice.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(NotificationActivity.this, "default")
.setContentTitle("这是标题")
.setContentText("这是内容")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.img_3) //不能缺少的一个属性
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.img_3))
.build();
manager.notify(1,notification);
}
});
NotificationCompat.Builder() 接收两个参数,第一个是 context,第二个是 channelId 字符串,作用是将notification进行分类,如设置不同优先级等
setContentTitle() 方法用于指定通知的标题内容
setContentText() 方法用于指定通知的正文内容
setWhen() 方法用于指定通知被创建的时间(毫秒)
setSmallIcon() 方法用于设置通知的小图标,只能用纯 alpha 图层的图片进行设置,否则无法显示,小图标会在系统状态栏上显示
setLargeIcon() 方法用于设置通知的大图标
最后调用 NotificationManager 的 notify() 方法就可以显示通知,notify()接收两个参数,第一个是 id,要保证为每个通知指定的 id 都是不同的,第二个是 Notification 对象
但是现在点击这条通知是没有任何反应的,需要配置 PendingIntent 才能实现点击效果,PendingIntent 与 Intent 类似,Intent 倾向于立刻去执行某个动作,PendingIntent 倾向于在某个合适的实际去执行某个动作
PendingIntent 提供了5个静态方法用于获取实例,
- getActivity():从系统取得一个用于启动一个 Activity 的 PendingIntent 对象
- getActivities():与上面相同,但一次可以打开多个 Activity
- getBroadcast():从系统取得一个用于向 BroadcastReceiver 的发送广播的 PendingIntent 对象
- getService():从系统取得一个用于启动一个 Service 的 PendingIntent 对象
- getForegroundService():与上面的相同,但启动的是一个前台服务
这几个方法接收的参数是相同的,第一个参数是 Context,第二个参数是请求码通常传入0,第三个参数是 Intent 对象,可以通过这个对象构建出 PensingIntent 要执行的动作,第四个参数用于确定 PensingIntent 的行为通常传入0,有 :
- FLAG_ONE_SHOT:该 PendingIntent 只作用一次。在该 PendingIntent 对象通过 send() 方法触发过后,PendingIntent 将自动调用 cancel() 进行销毁,那么如果你再调用 send() 方法的话,系统将会返回一个SendIntentException
- FLAG_NO_CREATE:如果当前系统中不存在相同的 PendingIntent 对象,系统将不会创建该 PendingIntent 对象而是直接返回null,如果之前设置过,这次就能获取到
- FLAG_CANCEL_CURRENT:如果当前系统中已经存在一个相同的 PendingIntent 对象,那么就将先将已有的 PendingIntent 取消,然后重新生成一个 PendingIntent 对象
- FLAG_UPDATE_CURRENT:如果系统中有一个和你描述的 PendingIntent 对等的PendingInent,那么系统将使用该 PendingIntent 对象,但是会使用新的 Intent 来更新之前PendingIntent 中的 Intent 对象数据,例如更新Intent中的Extras
NotificationCompat 对象可以连缀一个 setContentIntent() 方法,接收一个 PendingIntent 对象参数
点击通知后程序做出相应的动作后,应该对通知进行取消,有两种方法,一种是在 NotificationCompat 中连缀一个 setAutoCancel() 方法,一种是显示调用 NotificationManager 的 cancel() 方法,接收一个参数 id,这个 id 就是创建通知时调用 notify() 方法传入的 id
showNotice.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(NotificationActivity.this, ContentProviderActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(NotificationActivity.this, 0, intent, 0);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(NotificationActivity.this, "default")
.setContentTitle("这是标题")
.setContentText("这是内容")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.img_3))
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build();
manager.notify(1,notification);
}
});
点击通知后,通知消失,跳转到前面写的一个 ContentProviderActivity 界面
注意事项:当点击通知跳转到Activity的时候,Activity会重新走生命周期,想要保持原来的状态,需要给Activity配置一个launchMode = “singleTask”,或者在代码中做好状态保存
通知拓展
setSonund() 方法
在通知发出的时候播放一段音频提示用户有通知到来,setSonund() 方法接收一个 Uri 参数,可以是自己应用里的音频,也可以示 /system/media/audio/ringtones 目录下游很多系统自带的音频文件
setVibrate() 方法
处理音频还可以在通知来的时候让手机震动,接收一个长整型数组,用于设置手机静止和震动的时长,以毫秒为单位,数组下标0表示静止时长,下标1表示震动时长,下表2表示静止时长,下表3表示震动时长,以此类推,注意,控制手机震动要声明权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.helloworld">
......
<uses-permission android:name="android.permission.VIBRATE" />
......
</manifest>
setLights() 方法
用于设置前置 LED 灯,接收三个参数,第一个是 LED 灯的颜色,第二个是 LED 灯亮起的时长(毫秒单位),第三个是 LED 灯暗去的时长(毫秒单位)
setDefaults() 方法
上面很多繁杂的设置,如果不需要自定义的话可以使用通知系统默认的效果,接收一个整型参数,用于指定那些效果需要默认,
NotificationCompat.DEFAULT_ALL
NotificationCompat.DEFAULT_LIGHTS
NotificationCompat.DEFAULT_SOUND
NotificationCompat.DEFAULT_VIBRATE
Notification notification = new NotificationCompat.Builder(NotificationActivity.this, "default")
......
.setDefaults(NotificationCompat.DEFAULT_ALL)
.build();
setStyle() 方法
这个方法允许构建出富文本的通知内容,也就是说通可以包含更多的东西,先前的通知如果内容很长,多余的部分会用省略号老代替,但是通过 setStyle() 方法可以将长文本全部显示出来
Notification notification = new NotificationCompat.Builder(NotificationActivity.this, "default")
.setContentTitle("这是标题")
//.setContentText("这是内容")
.setStyle(new NotificationCompat.BigTextStyle().bigText("你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好"))
......
.build();
在 setStyle() 方法中创建了一个 NotificationCompat.BigTextStyle 对象,调用它的 bigText() 方法将文字内容传入进去就行
除此之外还能显示一张大图
Notification notification = new NotificationCompat.Builder(NotificationActivity.this, "default")
.setContentTitle("这是标题")
.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(), R.drawable.img_4)))
......
.build();
在 setStyle() 方法中创建了一个 NotificationCompat.BigPictureStyle 对象,调用它的 bigPicture() 方法将图片传入进去,通过 BitmapFactory 的 decodeResource() 方法将图片解析成 Bitmap 对象
setPriority() 方法
用于设置通知的重要程度,接收一个整型,有5个常量值可选:
- PRIORITY_DEFAULT:默认值
- PRIORITY_MIN:最低重要程度,系统可能只会在特定的场景才显示这条通知
- PRIORITY_LOW:较低的重要程度,系统可能会将这类通知缩小,或将其排在更重要的通知之后
- PRIORITY_HEIGHT:较高的重要程度,系统可能会将这类通知放大,或将其排在比较靠前的位置
- PRIORITY_MAX:最高重要程度,这类通知必须要让用户立即看到,并做出相应操作
Notification notification = new NotificationCompat.Builder(NotificationActivity.this, "default")
......
.setPriority(NotificationCompat.PRIORITY_MAX)
.build();
设置成最高重要程度后,通知不再是系统状态栏显示的一个小图标了,而是弹出了一个横幅,并显示了详细内容,不管用户在做什么操作,这条通知都会显示在最上方
注意
注意:创建通知的时候,需要判断是否为8.0以上,8.0以上需要创建 通知渠道 Channel,不创建通知渠道会报下面的错误
No Channel found for pkg=com.example.xx.xx, channelId=null, id=1001, tag=null…
important_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(NotificationActivity.this, ContentProviderActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(NotificationActivity.this, 0, intent, 0);
Notify notify = new Notify()
.setTicker("这是Ticker")
.setContentTitle("这是标题")
.setContentText("这是内容")
.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(), R.drawable.img_4)))
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.img_3))
.setAutoCancel(true)
.setSound("/system/media/audio/ringtones/Candy.ogg")
.setVibrate(new long[]{0, 1000, 1000, 1000})
.setLight(Color.GREEN)
.setPriority(NotificationManager.IMPORTANCE_HIGH);
notification(pendingIntent, notify, 4);
}
});
Notify 是自己定义的类,包含设置通知的常用属性,和 getter、setter 方法
private void notification(PendingIntent pendingIntent, @NonNull Notify notify, int id) {
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
String channelId = "default";
String channelName = "默认通知";
NotificationChannel channel = new NotificationChannel(channelId,channelName,NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(true); //是否在桌面icon右上角展示小红点
channel.setLightColor(Color.RED); //小红点颜色
channel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知
channel.setImportance(NotificationManager.IMPORTANCE_HIGH);
manager.createNotificationChannel(channel);
builder = new NotificationCompat.Builder(NotificationActivity.this, channelId);
}else{
builder = new NotificationCompat.Builder(NotificationActivity.this);
// 设置通知重要程度
builder.setPriority(notify.getPriority());
}
//Ticker是状态栏显示的提示
builder.setTicker(notify.getTicker());
//第一行内容 通常作为通知栏标题
builder.setContentTitle(notify.getContentTitle());
//第二行内容 通常是通知正文
builder.setContentText(notify.getContentText());
//第三行内容 通常是内容摘要什么的 在低版本机器上不一定显示
builder.setSubText(notify.getSubText());
//ContentInfo 在通知的右侧 时间的下面 用来展示一些其他信息
builder.setContentInfo(notify.getContentInfo());
builder.setStyle(notify.getStyle());
//通知时间
builder.setWhen(System.currentTimeMillis());
//系统状态栏显示的小图标
builder.setSmallIcon(notify.getSmallIcon());
//下拉显示的大图标
builder.setLargeIcon(notify.getLargeIcon());
//点击跳转的intent
builder.setContentIntent(pendingIntent);
//true:点击通知栏,通知消失
builder.setAutoCancel(notify.getAutoCancel());
if(notify.getDefaults()){
//通知默认的声音 震动 呼吸灯
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
}else{
builder.setSound(Uri.fromFile(new File(notify.getSound())));
builder.setVibrate(notify.getVibrate());
builder.setLights(notify.getLight(), 1000, 1000);
}
manager.notify(id,builder.build());
}
注意:通知重要性由通知目标发布渠道的 importance
属性决定,8.0以下由通知的 priority
属性决定,并且 8.0 以上使用的通知等级与 8.0 以下不同,8.0 以上使用的是 NotificationManager 类里的通知等级,8.0 一下是使用 NotificationCompat 里的通知等级,使用横幅通知,需要在项目的 AndroidManifest.xml 中添加权限
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
如果没有通知,可能是没有赋予足够的权限,被系统拦截了
摄像头和相册
拍摄照片上传
takeCamera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//创建 File 对象,用于存储拍照后的图片
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
try {
if(outputImage.exists()){
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if (Build.VERSION.SDK_INT >= 24){
imageUri = FileProvider.getUriForFile(CameraAlbumActivity.this, "com.example.helloworld.fileprovider", outputImage);
}else {
imageUri = Uri.fromFile(outputImage);
}
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, 1);
}
});
点击按钮后开始处理调用摄像头的逻辑,先创建了一个 File 对象,用于存储摄像头拍下的图片,命名为output_image.jpg,并将他放在手机 SD 卡的应用关联缓存目录下,调用 getExternalCacheDir() 方法可以获得这个缓存目录,具体位置在 /sdcard/Android/data/<packagename>/cache
在 Android 7.0 以上的版本,使用本地真实路径的 Uri 被认为是不安全的,会抛出 FileUriExposedException 的异常,而 FileProvider 是一种特殊的内容提供器,它使用了和内容提供器类似的机制来保护数据,调用 FileProvider 的 getUriForFile() 方法将 File 对象转换成一个封装过的 Uri 对象,getUriForFile() 接收3个参数,第一个是 Context,第二个是一个任意唯一的字符串,第三个是创建的 File 对象
最后构建出一个 Intent 对象,指定这个 Intent 的 action 为 android.media.action.IMAGE_CAPTURE,再调用 Intent 的 putExtra() 方法指定图片的输出地址,这里传入刚刚创建的 Uri 对象,最后调用 startActivityForResult() 来启动活动(照相机),在拍完照后 startActivityForResult() 来启动的活动会有返回结果,返回到 onActivityResult() 方法中
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case 1:
if (resultCode == RESULT_OK){
//将拍摄的照片显示出来
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
picture.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
拍照成功后,使用 BitmapFactory 的 decodeStream() 方法将 output_image.jpg 这张图片解析成 Bitmap 对象,然后将这张图片设置到 ImageView 中显示出来
注意:FileProvider 是一类特殊的内容提供器,所以要在 AndroidManifest.xml 文件中进行注册
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.helloworld.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
注意 android:exported="false"
android:name 属性是固定值,老版本SDK使用的是 android.support.v4.content.FileProvider ,android:authorities 属性必须要和 FileProvider.getUriForFile() 方法中的第二个唯一的字符串相同,<meta-data> 指定 Uri 的共享路径,引用了一个 @xml/file_paths,在 res文件夹下建立 xml 目录,在 xml 目录下建立 file_paths.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_image" path="."/>
</paths>
- <root-path/> 代表设备的根目录new File("/");
- <files-path/> 代表context.getFilesDir()
- <cache-path/> 代表context.getCacheDir()
- <external-path/> 代表Environment.getExternalStorageDirectory()
- <external-files-path> 代表context.getExternalFilesDirs()
- <external-cache-path> 代表getExternalCacheDirs()
external-path 用来指定 Uri 共享目录的,name 属性值可以是任意的字符串,path 属性表示具体的路径,这里表示将整个 SD 卡进行共享
注意:Android 4.4 系统之前,访问 SD 卡的应用关联目录是需要权限声明的,为了能兼容老版本,需要在 AndroidManifest.xml 文件中添加访问 SD 卡权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
从相册中选择照片
chooseFromAlbum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(ContextCompat.checkSelfPermission(CameraAlbumActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(CameraAlbumActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}else {
openAlbum();
}
}
});
添加了一个按钮,用于从相册中选择上传照片,同样操作相册需要赋权
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
switch (requestCode) {
case 1:
openAlbum();
break;
default:
break;
}
}else {
Toast.makeText(this, "未授权"+grantResults.length+"::"+grantResults[0], Toast.LENGTH_SHORT).show();
}
}
不要忘记在 AndroidManifest.xml 文件中申请权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
用户授权后调用 openAlbum() 方法打开相册
private void openAlbum(){
Intent intent = new Intent("android.intent.action.GET_CONTENT");
//intent.setType("audio/*"); //选择音频(mp4 3gp 是android支持的视频格式)
//intent.setType("video/;image/");//同时选择视频和图片
intent.setType("image/*");
startActivityForResult(intent, 2);
}
构建出一个 Intent ,并将它的 action 指定为 android.intent.action.GET_CONTENT 设置一下必要的参数,然后调用 startActivityForResult() 方法就可以打开相册程序了,与照相机相同,当选择照片后会回到 onActivityResult() 方法,在这里就对选择的照片进行处理
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case 1:
// 照相机操作 ......
break;
case 2:
if (resultCode == RESULT_OK){
if(Build.VERSION.SDK_INT >= 19){
handleImageOnKitKat(data);
}else {
handleImageBeforeKitKat(data);
}
}
default:
break;
}
}
和上面的照相机缓存文件类似,Android 系统从 4.4 版本以上,选取相册中的图片不再返回图片的真实 Uri ,而是一个封装过的 Uri,所以为了兼容要做一个版本判断
private String getImagePath(Uri uri, String selection){
String path = null;
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
if (cursor != null){
if (cursor.moveToNext()){
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
private void displayImage(String imagePath){
if (imagePath!=null){
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
picture.setImageBitmap(bitmap);
}else {
}
}
@TargetApi(19)
private void handleImageOnKitKat(Intent data){
String imagePath = null;
Uri uri = data.getData();
if(DocumentsContract.isDocumentUri(this, uri)){
String docId = DocumentsContract.getDocumentId(uri);
if ("com.android.providers.media.documents".equals(uri.getAuthority())){
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID + "=" + id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
}else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
imagePath = getImagePath(contentUri, null);
}
}else if("content".equalsIgnoreCase(uri.getScheme())){
imagePath = getImagePath(uri, null);
}else if("file".equalsIgnoreCase(uri.getScheme())){
imagePath = uri.getPath();
}
displayImage(imagePath);
}
private void handleImageBeforeKitKat(Intent data){
Uri uri = data.getData();
String imagePath = getImagePath(uri, null);
displayImage(imagePath);
}
getImagePath() 方法是获取图片的真实物理路径,就是通过内容提供器查询相册程序的数据库,查找出相应图片的信息,将信息中的物理地址返回
displayImage() 方法没什么好说的,就是将图片放到 ImageView 中
通过 data.getData() 方法获得这个图片的Uri,然后判断这个 Uri 的类型,如果是 doucument 类型的话,就取出 id 进行处理,不是就用普通的方式处理,如果 Uri 的 authority 是media 格式的话,doucument 的 id 还需要再进行一次解析,通过字符串分割的方式取出后半部分才能得到真正的数字 id,取出 id 用于构建新的 Uri 和条件,最后传入 getImagePath() 获取真实绝对路径,再调用 displayImage() 显示图片
handleImageBeforeKitKat() 方法没有对 Uri 进行封装,所以直接使用就能获得图片的真实路径了。
注意:有些照片可能会很大,直接加载到内存中可能会导致程序崩溃,最好先将照片进行适当的压缩,然后再加载到内存中
播放多媒体文件
播放音频
Android 使用 MediaPlayer 类进行音频的管理播放等操作
方法名 | 藐视 |
---|---|
setDataSource() | 设置播放的音频文件位置 |
prepare() | 在开始播放之前调用这个方法进行准备工作 |
start() | 开始或继续播放音频 |
pause() | 暂停播放音频 |
reset() | 将 MediaPlayer 对象重置到刚刚创建的状态 |
seekTo() | 从指定的位置开始播放音频 |
stop() | 停止播放音频,调用这个方法后 MediaPlayer 对象无法再次播放音频 |
release() | 释放掉与 MediaPlayer 对象相关的资源 |
isPlaying() | 判断当前的 MediaPlayer 是否在播放音频 |
getDuration | 获取载入音频文件的时长 |
private MediaPlayer mediaPlayer = new MediaPlayer();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_media);
Button playButton= (Button)findViewById(R.id.media_play);
Button pauseButton = (Button)findViewById(R.id.media_pause);
Button stopButton = (Button)findViewById(R.id.media_stop);
playButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!mediaPlayer.isPlaying()){
mediaPlayer.start();
}
}
});
pauseButton .setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
}
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mediaPlayer.isPlaying()){
mediaPlayer.reset();
initMediaPalyer();
}
}
});
if(ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MediaActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}else {
initMediaPalyer();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mediaPlayer!=null){
mediaPlayer.stop();
mediaPlayer.release();
}
}
public void initMediaPalyer(){
File file = new File(Environment.getExternalStorageDirectory(), "Music/music.mp3");
try {
mediaPlayer.setDataSource(file.getPath());
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
先创建了一个 MediaPlayer 的实例,然后在 onCreate() 方法中进行运行时权限处理,onRequestPermissionsResult() 方法与前面一样就省略了,注意要在 AndroidManifiest.xml 中添加权限
initMediaPalyer() 方法为 MediaPlayer 对象进行初始化操作,实现在 SD 卡根目录下的 Music 文件夹下放置了一个名为 music.mp3 的音频文件 ,后面一次调用了 setDaataSource() 方法和 prepare() 方法进行初始化,定义了三个按钮,分别是播放,暂停,停止
播放视频
播放视屏与播放音频类似,使用 VideoView 类进行视屏的管理播放等操作
方法名 | 描述 |
---|---|
setVideoPath() | 设置要播放的视频文件的位置 |
start() | 开始或继续播放视屏 |
pause() | 暂停播放视屏 |
resume | 将视屏重新开始播放 |
seekTo() | 从指定位置开始播放视屏 |
isPlaying() | 判断当前视屏是否正在播放 |
getDuration() | 获取载入视屏文件的时长 |
private VideoView videoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_media);
videoView = (VideoView)findViewById(R.id.video_view);
Button videoPlay = (Button)findViewById(R.id.video_play);
Button videoPause = (Button)findViewById(R.id.video_pause);
Button videoStop = (Button)findViewById(R.id.video_stop);
videoPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!videoView.isPlaying()){
videoView.start();
}
}
});
videoPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(videoView.isPlaying()){
videoView.pause();
}
}
});
videoStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(videoView.isPlaying()){
videoView.resume();
}
}
});
if(ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MediaActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}else {
// initMediaPalyer();
initVideoPath();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(videoView != null){
videoView.suspend();
}
}
private void initVideoPath(){
File file = new File(Environment.getExternalStorageDirectory(), "Movies/movie.mp4");
videoView.setVideoPath(file.getPath());
}
与音频相同,解释略