Android 4.4前后版本读取图库图片和拍照完美解决方案

Android 4.4 图片URI处理
本文针对Android 4.4及以上版本中图片URI的变化进行了深入探讨,并提供了兼容不同版本的图片路径获取方法。文章还介绍了如何根据不同类型的图库正确处理URI,确保应用程序在新旧版本间的稳定运行。

4.3或以下,选了图片之后,根据Uri来做处理,很多帖子都有了,我就不详细说了.主要是4.4,如果使用上面pick的原生方法来选图,返回的uri还是正常的,但如果用ACTION_GET_CONTENT的方法,返回的uri跟4.3是完全不一样的,4.3返回的是带文件路径的,而4.4返回的却是content://com.android.providers.media.documents/document/image:3951这样的,没有路径,只有图片编号的uri.这就导致接下来无法根据图片路径来裁剪的步骤了.

来自overflow大牛的方法,4.4得到的uri,需要以下方法来获取文件的路径:

 /** 
 * <br>功能简述:4.4及以上获取图片的方法
 * <br>功能详细描述:
 * <br>注意:
 * @param context
 * @param uri
 * @return
 */
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getPath(final Context context, final Uri uri) {

	final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

	// DocumentProvider
	if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
		// ExternalStorageProvider
		if (isExternalStorageDocument(uri)) {
			final String docId = DocumentsContract.getDocumentId(uri);
			final String[] split = docId.split(":");
			final String type = split[0];
			if ("primary".equalsIgnoreCase(type)) {
				return Environment.getExternalStorageDirectory() + "/" + split[1];
			}
		}
		// DownloadsProvider
		else if (isDownloadsDocument(uri)) {

			final String id = DocumentsContract.getDocumentId(uri);
			final Uri contentUri = ContentUris.withAppendedId(
			Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

			return getDataColumn(context, contentUri, null, null);
		}
		// MediaProvider
		else if (isMediaDocument(uri)) {
			final String docId = DocumentsContract.getDocumentId(uri);
			final String[] split = docId.split(":");
			final String type = split[0];

			Uri contentUri = null;
			if ("image".equals(type)) {
				contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
			} else if ("video".equals(type)) {
				contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
			} else if ("audio".equals(type)) {
				contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
			}

			final String selection = "_id=?";
			final String[] selectionArgs = new String[] { split[1] };

			return getDataColumn(context, contentUri, selection, selectionArgs);
		}
	}
	// MediaStore (and general)
	else if ("content".equalsIgnoreCase(uri.getScheme())) {

		// Return the remote address
		if (isGooglePhotosUri(uri))
			return uri.getLastPathSegment();

		return getDataColumn(context, uri, null, null);
	}
	// File
	else if ("file".equalsIgnoreCase(uri.getScheme())) {
		return uri.getPath();
	}

	return null;
}

public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {

	Cursor cursor = null;
	final String column = "_data";
	final String[] projection = { column };

	try {
		cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,null);
		if (cursor != null && cursor.moveToFirst()) {
			final int index = cursor.getColumnIndexOrThrow(column);
			return cursor.getString(index);
		}
	} finally {
		if (cursor != null)
			cursor.close();
	}
	return null;
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
	return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
	return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
	return "com.android.providers.media.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
	return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}


为什么会不一样呢?

Android 4.4(含)开始,通过方式访问图库后,返回的Uri如下(访问“最近”):

Uri is:content://com.android.providers.media.documents/document/image%3A18838  
2 Uri.getPath is :/document/image:18838  
3 对应的图片真实路径:/storage/emulated/0/Pictures/Screenshots/Screenshot_2014-09-22-21-40-53.png
不但如此,对于不同类型图库,返回的Uri形式并不相同(访问普通相册):

Uri is:content://media/external/images/media/18822  
2 Uri.getPath is :/external/images/media/18822  
3 对应的图片真实路径:/storage/emulated/0/Download/20130224235013.jpg
而4.4之前返回的Uri只存在一种形式,如下:

Uri is:content://media/external/images/media/14046  
2 Uri.getPath is :/external/images/media/14046  
3 对应的图片真实路径:/storage/emulated/0/DCIM/Camera/20130224235013.jpg
因此,在Android 4.4或更高版本设备上,通过简单的getDataColumn(Context, Uri, null, null)进行图片数据库已经不能满足所有需求,因此在获取图片真实路径的时候需要根据不同类型区分对待。

版本判断:
// 版本比较:是否是4.4及以上版本  
final boolean mIsKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; 
从相册选择照片方法比较:

/**  
 * <br>功能简述:4.4及以上从相册选择照片 
 * <br>功能详细描述: 
 * <br>注意: 
 */  
 @TargetApi(Build.VERSION_CODES.KITKAT)  
 private void SelectImageUriAfterKikat() {  
      Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);  
      intent.addCategory(Intent.CATEGORY_OPENABLE);  
      intent.setType("image/*");  
      startActivityForResult(intent, SELECET_A_PICTURE_AFTER_KIKAT);  
  }  
/**  
 * <br>功能简述:4.4以下从相册选照片并剪切 
 * <br>功能详细描述: 
 * <br>注意: 
 */  
 private void cropImageUri() {  
     Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);  
     intent.setType("image/*");  
     intent.putExtra("crop", "true");  
     intent.putExtra("aspectX", 1);  
     intent.putExtra("aspectY", 1);  
     intent.putExtra("outputX", 640);  
     intent.putExtra("outputY", 640);  
     intent.putExtra("scale", true);  
     intent.putExtra("return-data", false);  
     intent.putExtra(MediaStore.EXTRA_OUTPUT,  
     Uri.fromFile(new File(IMGPATH, TMP_IMAGE_FILE_NAME)));  
     intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());  
     intent.putExtra("noFaceDetection", true); // no face detection  
     startActivityForResult(intent, SELECT_A_PICTURE);  
 }
4.4及以上选取照片后需要调用剪切方法:

/** 
 * <br>功能简述: 4.4及以上选取照片后剪切方法
 * <br>功能详细描述:
 * <br>注意:
 * @param uri
 */
private void cropImageUriAfterKikat(Uri uri) {
	Intent intent = new Intent("com.android.camera.action.CROP");
	intent.setDataAndType(uri, "image/*");
	intent.putExtra("crop", "true");
	intent.putExtra("aspectX", 1);
	intent.putExtra("aspectY", 1);
	intent.putExtra("outputX", 640);
	intent.putExtra("outputY", 640);
	intent.putExtra("scale", true);
	intent.putExtra("return-data", true); //返回数据bitmap
	intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
	intent.putExtra("noFaceDetection", true); // no face detection
	startActivityForResult(intent, SET_ALBUM_PICTURE_KITKAT);
}
拍照通用方法,对返回数据做处理:

/** 
* <br>功能简述:对拍照的图片剪切
* <br>功能详细描述:
* <br>注意:
* @param uri
*/
private void cameraCropImageUri(Uri uri) {
       Intent intent = new Intent("com.android.camera.action.CROP");
       intent.setDataAndType(uri, "image/jpeg");
       intent.putExtra("crop", "true");
       intent.putExtra("aspectX", 1);
       intent.putExtra("aspectY", 1);
       intent.putExtra("outputX", 640);
       intent.putExtra("outputY", 640);
       intent.putExtra("scale", true);
       if (mIsKitKat) {
           intent.putExtra("return-data", true);
       } else {
           intent.putExtra("return-data", false);
           intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
       }
       intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
       intent.putExtra("noFaceDetection", true);
       startActivityForResult(intent, SET_PICTURE);
   }
显示等处理如下:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == SELECT_A_PICTURE) {
        if (resultCode == RESULT_OK && null != data) {
            Log.i("zou", "4.4以下的");
            Bitmap bitmap = decodeUriAsBitmap(Uri.fromFile(new File(IMGPATH, TMP_IMAGE_FILE_NAME)));
            mAcountHeadIcon.setImageBitmap(bitmap);
        }
    } else if (requestCode == SELECET_A_PICTURE_AFTER_KIKAT) {
        if (resultCode == RESULT_OK && null != data) {
            Log.i("zou", "4.4以上上的");
            mAlbumPicturePath = getPath(MainActivity.this, data.getData());
            cropImageUriAfterKikat(Uri.fromFile(new File(mAlbumPicturePath)));
        }
    } else if (requestCode == SET_ALBUM_PICTURE_KITKAT) {
        Log.i("zou", "4.4以上上的 RESULT_OK");
        Bitmap bitmap = data.getParcelableExtra("data");
        mAcountHeadIcon.setImageBitmap(bitmap);
    } else if (requestCode == TAKE_A_PICTURE) {
        Log.i("zou", "resultCode:" + resultCode);
        cameraCropImageUri(Uri.fromFile(new File(IMGPATH, IMAGE_FILE_NAME)));
    } else if (requestCode == SET_PICTURE) {
        Log.i("zou", "SET_PICTURE-resultCode:" + resultCode);
        Bitmap bitmap = null;
        if (mIsKitKat) {
            if (null != data) {
                bitmap = data.getParcelableExtra("data");
            }
        } else {
            bitmap = decodeUriAsBitmap(Uri.fromFile(new File(IMGPATH, IMAGE_FILE_NAME)));
        }
        mAcountHeadIcon.setImageBitmap(bitmap);
    }
}
提供GIthub工程地址: https://github.com/ZBJDSBJ/CameraDemo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值