起因
Android在4.4之后的版本(包括4.4)中,从相册中选取图片返回Uri进行了改动。所以我们无法通过该Uri来取得文件路径,从而解码图片,将其显示出来。
在4.3或以下可以直接用Intent.ACTION_GET_CONTENT打开相册;在4.4或以上,官方建议用ACTION_OPEN_DOCUMENT打开相册
4.4之前的版本
在4.4之前的版本,返回的Uri如下:content://media/external/images/media/8302
我们可以通过ContentResolver的查询方法来获取路径:
Uri uri = "content://media/external/images/media/8302";
String imagePath = getImagePath(uri, null);
private String getImagePath(Uri uri, String selection) {
String path = null;
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;
}
代码分析,当我们通过uri得到了相册数据库图片的表,然后通过索引MediaStore.Images.Media.DATA
获取所得行的"_data"列的值。这样我们就得到了具体的文件路径,可以通过创建输入流来获取相应的Bitmap,并进行显示。
4.4之后的版本,包括4.4
在4.4之后的,包括4.4的版本,返回的Uri有可能是以下的一种:
- content://com.android.providers.media.documents/document/image%3A8302
- content://com.android.providers.downloads.documents/document/5
- content://media/external/images/media/8302
我们不能直接通过前两种Uri直接获取到对应的表,所以需要"翻译一下":
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())) {
//Log.d(TAG, uri.toString());
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())) {
//Log.d(TAG, uri.toString());
Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
Long.valueOf(docId));
imagePath = getImagePath(contentUri, null);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
//Log.d(TAG, "content: " + uri.toString());
imagePath = getImagePath(uri, null);
}
}
代码分析:
判断步骤:
- 首先对Uri的authority进行判断。是document类型的Uri还是普通的media类型的Uri。
- 由于document类型有两种:media和download类型,所以需要进一步判断。因为这里涉及Uri的id部分不同。
- 如果是普通类型,那么和4.4之前的处理完全一样。可以直接通过Uri获取文件路径。
如果是media类型的document Uri,我们首先通过DocumentsContract.getDocumentId(uri);
获取到"image%3A8302"。然后通过String.split方法来获取真正的id。
这里为什么用":"来作为分隔符我也不是很清楚...而且还学到一个东西ContentResolver.query()方法中selection参数可以不用占位符"?"直接获取指定想要的列。
MediaStore.Images.Media._ID指的是我们获取到指定image文件的id在表中的列名。MediaStore.Images.Media.EXTERNAL_CONTENT_URI则是相册图片表真正的标示符。
这里的Uri获取是通过打开相册选定图片后,返回的Intent中获取的。
当我们获取到选中图片真正的Uri后,就可以通过之前的getImagePath()
方法来获取表中的文件路径。最终达到解码图片的目的。
实际效果
参考
《第一行代码》