手机在我们的生活中正扮演着越来越重要的角色,各种娱乐方式都可以在手机上进行。众多的娱乐方式少不了强大的多媒体功能的支持,而Android在这方面也做得非常出色。它提供了一系列的API,使得我们可以在程序中调用很多手机的多媒体资源,从而编写出更加丰富多彩的应用程序。
调用摄像头和相册
调用摄像头拍照
现在很多的应用都会要求用户上传一张图片来作为头像,这时打开摄像头拍张照是最简单快捷的。
启动摄像头
应用程序要启动手机的相机程序需要通过以下代码来实现:
// 启动相机程序 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(intent, 1);
- 使用Intent对象启动相机程序,Intent的action指定为android.media.action.IMAGE_CAPTURE;
- 调用Intent对象的putExtra()方法将图片的输出地址的Uri对象作为值传入,以MediaStore.EXTRA_OUTPUT作为键(与多媒体相关的可以从MediaStore这个类中得到)
指定照片存放路径
拍照需要指定图片存储路径,在上述代码中将Uri对象作为图片输出路径,通过以下代码来构建这个Uri对象:
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(); } if (Build.VERSION.SDK_INT >= 24) { imageUri = FileProvider.getUriForFile(MainActivity.this, "cn.chenjianlink.cameraalbumtest.fileprovider", outputImage); } else { imageUri = Uri.fromFile(outputImage); }
照片属于文件,用File类临时保存。
getExternalCacheDir()方法用于获取应用程序关联目录,具体的路径是:/sdcard/Android/data/<package name>/cache(这个目录是SD卡中专门用于存放当前应用缓存数据的位置,由于从Android 6.0系统开始,读写SD卡被列为了危险权限,如果将图片存放在SD卡的任何其他目录,都要进行运行时权限处理才行,而使用应用关联目录则可以跳过这一步)
Build.VERSION.SDK_INT用于获取当前系统的SDK版本
如果运行设备的系统版本低于Android 7.0,就调用Uri的fromFile()方法将File对象转换成Uri对象,这个Uri对象标识着output_image.jpg这张图片的本地真实路径。
从Android 7.0系统开始,直接使用本地真实路径的Uri被认为是不安全的,会抛出一个FileUriExposedException异常。而FileProvider则是一种特殊的内容提供器,它使用了和内容提供器类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高了应用的安全性。
在系统版本高于Android 7.0的设备上,调用FileProvider的getUriForFile()方法将File 对象转换成一个封装过的Uri对象,这个方法有三个参数:public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file)
- Context context:Context对象,一般传入调用的程序当前Activity
- String authority:任意唯一的字符串,作为唯一标识
- File file:将要转换的文件的file对象传入
使用FileProvider,需要在AndroidManifest.xml中对这个内容提供器进行注册
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.chenjianlink.cameraalbumtest"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> ... <provider android:name="android.support.v4.content.FileProvider" android:authorities="cn.chenjianlink.cameraalbumtest.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application> </manifest>
- 访问SD卡需要声明权限(Android 4.4系统以前)
- android:authorities属性的值必须要和FileProvider.getUriForFile()方法中的第二个参数一致
- <provider>标签中使用<meta-data>来指定Uri的共享路径,引用@xml/file_paths 资源,这个文件内容如下:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="my_images" path="" /> </paths>
external-path用来指定Uri共享,name属性的值可以随便填,path属性的值表示共享的具体路径(设置为空值表示将整个SD卡共享)
照片显示
拍完照之后,一般需要回到应用程序中,则需要重写Activity类中的onActivityResult()方法,在这个方法中执行相关逻辑。
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { //自定义逻辑 }
如果需要将拍摄好的照片在应用程序中显示,则需要使用调用BitmapFactory的
decodeStream()方法将照片解析成Bitmap 对象,然后把它设置到ImageView中显示出来:
// 将拍摄的照片显示出来 private ImageView picture = (ImageView) findViewById(R.id.picture); Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); picture.setImageBitmap(bitmap);
还需要在布局文件中引入ImageView这个控件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > ··· <ImageView android:id="@+id/picture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> ··· </LinearLayout>
从相册中选取照片
应用程序调用照片还可从手机相册里
打开相册
用Intent对象打开相册,相关代码如下:
Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent, 1); // 打开相册
打开相册需要动态申请WRITE_EXTERNAL_STORAGE 这个危险权限,相册中的照片都是存储在SD卡上的,要从SD卡中读取照片就需要申请这个权限。WRITE_EXTERNAL_STORAGE 表示同时授予程序对SD卡读和写的能力。
相册图片显示
Android系统从4.4版本开始,选取相册中的图片不再返回图片真实的Uri了,而是一个封装过的Uri,因此如果是4.4版本以上的手机就需要对这个Uri进行解析才能获取照片的真实路径。
在Android 4.4以下系统获取图片真实路径:
private void handleImageBeforeKitKat(Intent data) { Uri uri = data.getData(); String imagePath = getImagePath(uri, null); displayImage(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; } private void displayImage(String imagePath) { if (imagePath != null) { Bitmap bitmap = BitmapFactory.decodeFile(imagePath); picture.setImageBitmap(bitmap); } else { //··· } }
在Android 4.4以上系统获取图片真实路径:
@TargetApi private void handleImageOnKitKat(Intent data) { String imagePath = null; Uri uri = data.getData(); 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(); } displayImage(imagePath); // 根据图片路径显示图片 }
- 如果返回的Uri是document类型的话,那就取出document id 进行处理,如果不是的话,那就使用普通的方式处理。
- 如果Uri的authority是media格式的话,document id还需要再进行一次解析,要通过字符串分割的方式取出后半部分才能得到真正的数字id。
播放多媒体文件
手机上最常见的休闲方式毫无疑问就是听音乐和看电影了,Android在播放音频和视频方面也是做了相当不错的支持,它提供了一套较为完整的API,使得开发者可以很轻松地编写出一个简易的音频或视频播放器。
播放音频
在Android中播放音频文件一般都是使用MediaPlayer 类来实现的,它对多种格式的音频文件提供了非常全面的控制方法,从而使得播放音乐的工作变得十分简单。
MediaPlayer常用方法如下:
MediaPlayer的工作流程如下:
- 创建出一个MediaPlayer对象
- 调用setDataSource()方法来设置音频文件的路径
- 调用prepare()方法使MediaPlayer进入到准备状态
- 调用start()方法开始b播放
- 播放音频,调用pause()方法就会暂停播放,调用reset()方法就会停止播放
在不再使用MediaPlayer时,分别调用stop()方法和release()方法,将与MediaPlayer相关的资源释放掉
播放视频
播放视频文件使用VideoView类来实现,这个类将视频的显示和控制集于一身。
VideoView的用法和MediaPlayer也比较类似,主要有以下常用方法:
VideoView的工作流程如下:
- 创建出一个VideoView对象
- 调用setVideoPath()方法来设置音频文件的路径
- 调用start()方法开始播放
- 播放视频,调用pause()方法就会暂停播放
要在程序中显示播放的视频,需要在布局文件中添加VideoView这个控件。