《第一行代码》读书笔记 -- 多媒体资源
《第一行代码》读书笔记(一)-- 平台架构 (第1章)
《第一行代码》读书笔记(二)-- 应用组件之 Activity (第2、4章)
《第一行代码》读书笔记(三)-- 应用组件之 Service (第10章)
《第一行代码》读书笔记(四)-- 应用组件之 BroadcastReceiver (第5章)
《第一行代码》读书笔记(五)-- 应用组件之 ContentProvider (第7章)
《第一行代码》读书笔记(六)-- 数据存储方案 (第6章)
《第一行代码》读书笔记(七)-- 多媒体资源 (第8章)
《第一行代码》读书笔记(八)-- 网络编程 (第9章)
第8章 丰富你的程序
使用通知
1、通知的基本用法
通知 Notification 是 Android 系统中比较特色的功能,使用方式也很灵活。
首先获取 NotificationManager 对象,然后构建 Notification 实例,最后使用 notify 方法显示通知。
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
.setContentTitle("Hello Notification !")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setAutoCancel(true)
.build();
notificationManager.notify(2333, notification);
以上是书籍代码思路,在 Android 8.0 上是不显示通知的,我们作相应的修改。
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("2333", "wshunli",NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
}
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
.setContentTitle("Hello Notification !")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setAutoCancel(true)
.setChannelId("2333")
.build();
notificationManager.notify(2333, notification);
最终通知显示效果:
这时候点击通知是没有效果的,对通知 添加点击效果 。
使用 PendingIntent 来启动 Activity 、 Service 、发送广播等。
PendingIntent 会在何时和时机执行某个动作,而 Intent 会立即执行。
根据启动的 Android 组件不同可使用的方法也不同:
getActivity(Context, int, Intent, int),
getActivities(Context, int, Intent[], int),
getBroadcast(Context, int, Intent, int),
getService(Context, int, Intent, int);
其中最后一个参数取值也很丰富,FLAG_ONE_SHOT, FLAG_NO_CREATE, FLAG_CANCEL_CURRENT, FLAG_UPDATE_CURRENT 。
使用示例:
Intent intent = new Intent(getApplicationContext(), NotificationActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT);
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
.setContentTitle("Hello Notification !")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setAutoCancel(true)
.setChannelId("2333")
.setContentIntent(pendingIntent)
.build();
使用 setContentIntent() 方法设置 PendingIntent 对象参数,点击即可跳转到 NotificationActivity 界面。
2、通知的进阶技巧
NotificationCompat.Builder 中有非常丰富的 API 来创建多样的通知效果。
2.1、设置声音
Notification.Builder setSound(Uri sound)
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
···
.setSound(Uri.parse("/system/media/audio/ringtones/Luna.ogg"))
.build();
Android 8.0 要使用 NotificationChannel 设置:
void setSound (Uri sound, AudioAttributes audioAttributes)
NotificationChannel channel = new NotificationChannel("2333", "wshunli",NotificationManager.IMPORTANCE_HIGH);
channel.setSound(Uri.parse("/system/media/audio/ringtones/Luna.ogg"), null);
notificationManager.createNotificationChannel(channel);
2.2、设置震动
Notification.Builder setVibrate(long[] pattern)
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
···
.setVibrate(new long[]{0, 1000, 1000, 1000})
.build();
其中数组表示:先停顿 0 ms ,震动 1000 ms ,再停顿 1000 ms , 最后震动 1000 ms ,后面以此类推。
需要申请权限:
<uses-permission android:name="android.permission.VIBRATE" />
Android 8.0 要使用 NotificationChannel 设置:
void setVibrationPattern (long[] vibrationPattern)
NotificationChannel channel = new NotificationChannel("2333", "wshunli",NotificationManager.IMPORTANCE_HIGH);
channel.setVibrationPattern(new long[]{0, 1000, 1000, 1000});
notificationManager.createNotificationChannel(channel);
3.3、设置 LED 灯
Notification.Builder setLights (int argb, int onMs, int offMs)
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
···
.setLights(Color.GREEN, 1000, 1000)
.build();
Android 8.0 要使用 NotificationChannel 设置:
void enableLights (boolean lights)
channel.enableLights(true);
3.4、默认设置
前面那么多设置,嫌麻烦可以直接使用默认效果。
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
···
.setDefaults(NotificationCompat.DEFAULT_ALL)
.build();
Android 8.0 此方法过时,还要用前面适用于 8.0 的方法。
3、通知的高级功能
3.1、首先看 setStyle() 方法
Notification.Builder setStyle (Notification.Style style)
首先来看显示一段长文字:
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
···
.setContentText("com.wshunli.notification.demo/com.wshunli.notification.demo.MainActivity")
.build();
这样文字过长,会以省略号代替。
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
···
// 使用 setStyle() 方法
.setStyle(new NotificationCompat.BigTextStyle().bigText("com.wshunli.notification.demo/com.wshunli.notification.demo.MainActivity"))
.build();
这样文字会全部显示出来。
除了显示长文字外,通知还可以显示一张大图片:
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
···
.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(),R.drawable.pano)))
.build();
以上显示结果:
![]() |
![]() |
![]() |
---|---|---|
通知文字过长 | 显示长文字效果 | 显示大图片效果 |
3.2、通知的优先级
使用 setPriority 方法:
Notification.Builder setPriority (int pri)
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "wshunli")
···
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build();
其中优先级可取值:PRIORITY_DEFAULT, PRIORITY_LOW, PRIORITY_MIN, PRIORITY_HIGH or PRIORITY_MAX.
Andorid 8.0 使用 NotificationManager
中 IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, IMPORTANCE_LOW, IMPORTANCE_MAX, IMPORTANCE_MIN 代替。
方法也使用 NotificationChannel 中的 setImportance 方法:
void setImportance (int importance)
channel.setImportance(NotificationManager.IMPORTANCE_DEFAULT);
调用摄像头相册
1、调用摄像头拍照
首先应该申请相机权限:
<uses-permission android:name="android.permission.CAMERA" />
public static final int TAKE_PHOTO = 1;
private Uri imageUri;
// 创建 File 对象,用于存储拍照后的图片
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
// 将 File 转化为 Uri 对象
if (Build.VERSION.SDK_INT < 24) {
imageUri = Uri.fromFile(outputImage);
} else {
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.wshunli.camera.demo.fileprovider", outputImage);
}
// 启动相机程序
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO);
对于 Android 7.0 以前,File 转化为 Uri 比较简单,但是 7.0 以后认为直接使用真实路径的 Uri 是不安全的。
所以使用 FileProvider 内容提供器来对数据进行保护,可以选择性地将封装过的 Uri 共享给外部。
在 Manifest 中,添加 provider 内容提供器 :
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.wshunli.camera.demo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
在 /app/src/main/res/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_images"
path="/" />
</paths>
最后在 onActivityResult 中接收拍照完成后返回的数据:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case TAKE_PHOTO:
if (resultCode == RESULT_OK) {
try {
// 获取拍摄的照片
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
Log.d(TAG, "onActivityResult: " + bitmap.getRowBytes());
} catch (Exception e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
2、从相册中选择照片
同样应该声明权限,在系统版本 6.0 一样应该申请运行时权限。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
打开相册还算比较简单:
public static final int CHOOSE_PHOTO = 2;
// 打开相册
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOOSE_PHOTO);
结果处理有点复杂:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
···
case CHOOSE_PHOTO:
if (resultCode == RESULT_OK) {
// 判断手机系统版本号
if (Build.VERSION.SDK_INT >= 19) {
// 4.4 及以上系统使用这个方法处理图片
handleImageOnKitKat(data);
} else {
// 4.4 以下系统使用这个方法处理图片
handleImageBeforeKitKat(data);
}
}
break;
default:
break;
}
}
@TargetApi(19)
private void handleImageOnKitKat(Intent data) {
String imagePath = null;
Uri uri = data.getData();
Log.d("TAG", "handleImageOnKitKat: uri is " + uri);
if (DocumentsContract.isDocumentUri(this, uri)) {
// 如果是 document 类型的 Uri ,则通过 document id 处理
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())) {
String id = docId.split(":")[1]; // 解析出数字格式的 id
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())) {
// 如果是 content 类型的 Uri ,则使用普通方式处理
imagePath = getImagePath(uri, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
// 如果是 file 类型的 Uri ,直接获取图片路径即可
imagePath = uri.getPath();
}
Log.d(TAG, "handleImageOnKitKat: " + imagePath);
}
private void handleImageBeforeKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri, null);
Log.d(TAG, "handleImageBeforeKitKat: " + imagePath);
}
private String getImagePath(Uri uri, String selection) {
String path = null;
// 通过 Uri 和 selection 来获取真实的图片路径
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
播放多媒体文件
1、播放音频
播放音频主要使用 MediaPlayer 类。
MedaiaPlayer 设置资源有三种类型:
(1) 本地资源
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file);
mediaPlayer.start(); // no need to call prepare(); create() does that for you
(2) 通过 URI 共享的资源
Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();
(3) 通过网络 URL 资源
String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();
https://developer.android.com/guide/topics/media/mediaplayer
获取 MediaPlayer 实例就可以控制音频播放了。
mediaPlayer.prepare(); // 开始播放前准备工作
mediaPlayer.start(); // 开始播放
mediaPlayer.pause(); // 暂停播放
mediaPlayer.reset(); // 停止播放
mediaPlayer.seekTo(); // 指定位置播放
mediaPlayer.stop(); // 停止播放,后面不能再播放音频
mediaPlayer.release(); // 释放相关资源
2、播放视频
播放视频主要使用 VideoView 控件。
使用 setVideoPath (String path) 或者 setVideoURI (Uri uri) 设置视频资源。
然后就可以使用相应方法控制播放了.
videoView.start(); // 开始播放
videoView.pause(); // 暂停播放
videoView.resume(); // 重头开始播放
videoView.seekTo(); // 指定位置播放
对于音频视频播放还有官方框架 ExoPlayer :
https://github.com/google/ExoPlayer
参考资料
1、Android O: How to Use Notification Channels - Your Web App
http://forum.yourwebapp.mobi/android-o-how-to-use-notification-channels/
2、Android O 8.0系统下通知(Notification)、安装apk问题更新后的简单兼容写法 - CSDN博客
https://blog.csdn.net/weitao_666/article/details/79142592
3、Audio & Video | Android Developers
https://developer.android.com/guide/topics/media/
评论 (0)