《第一行代码》读书笔记(七)

wshunli
2018-06-04 / 0 评论 / 50 阅读 / 正在检测是否收录...

《第一行代码》读书笔记 -- 多媒体资源

《第一行代码》读书笔记(一)-- 平台架构 (第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);

最终通知显示效果:

7.Notification.png

这时候点击通知是没有效果的,对通知 添加点击效果

使用 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();

以上显示结果:

7.Notification.png 7.Notification.png 7.Notification.png
通知文字过长 显示长文字效果 显示大图片效果

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

评论 (0)

取消