从 Android Lollipop 中的 Uri 裁剪照片后总是返回 Null?
Posted
技术标签:
【中文标题】从 Android Lollipop 中的 Uri 裁剪照片后总是返回 Null?【英文标题】:Always Null returned after cropping a photo from a Uri in Android Lollipop? 【发布时间】:2015-09-24 13:23:36 【问题描述】:我在拍照或挑选照片后尝试从 Uri 裁剪图像。我的代码是这样的:
public static void cropImage(Uri uri, Activity activity, int action_code)
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", 600);
intent.putExtra("outputY", 600);
intent.putExtra("scale", true);
intent.putExtra("return-data", true);
if (intent.resolveActivity(activity.getPackageManager()) != null)
activity.startActivityForResult(intent, action_code);
else
Toast.makeText(activity, "No Crop App Available", Toast.LENGTH_SHORT).show();
然后像这样覆盖onActivityResult()
:
if (resultCode == Activity.RESULT_OK && requestCode == Utils.CODE_CROP_IMAGE)
Bundle extras = data.getExtras();
showCenterToast("ccc");
if (extras != null)
showCenterToast("CCC");
Bitmap photo = extras.getParcelable("data");
ivAvatar.setImageBitmap(photo); // display image in ImageView
FileOutputStream fos = null;
try
fos = new FileOutputStream(Utils.AVATAR_FILE);
photo.compress(Bitmap.CompressFormat.PNG, 100, fos);// (0-100)compressing file
showCenterToast("DDD");
Utils.AVATAR_FILE_TMP.delete();
catch (FileNotFoundException e)
e.printStackTrace();
finally
IoUtil.closeSilently(fos);
在 Android Pre-Lollipop 的设备上,我能够获取 Bitmap photo
并将其显示在 ImageView
中。 但是,在 Android Lollipop 上,我总是从 data.getExtras();
得到 null
。
我在 Google 上搜索了很多,但几乎没有关于在 Android Lollipop 上裁剪图像的有用信息。
Android 更改了其在 Lollipop 上裁剪 com.android.camera.action.CROP
的返回机制。那么,新机制是什么?在 Lollipop 上裁剪后如何获得返回的Bitmap
?
任何提示将不胜感激。提前致谢。
【问题讨论】:
我从来没有做过 CROP,但是你能检查一下你是否从 data.getData() 得到了什么吗?它可能是一个 URI? 我拍照后得到data.getData()
的Uri
,拍照后成功转换成Uri
。我用这个Uri
进行裁剪,但在Bitmap photo = extras.getParcelable("data");
按照建议完成裁剪过程后什么也没有。
我的意思是当 requestCode == Utils.CODE_CROP_IMAGE 时检查数据中的所有内容。可能里面有 Uri,也可能裁剪后的图片也在 content provider 中。
当当前版本等于或大于 Lollipop 时,我尝试了 Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), data.getData());
,但抛出了 NPE。在 Lollipop 上,我确实认为返回了数据,但我不知道它在哪里以及如何获取它。
你有解决办法吗?
【参考方案1】:
我认为您的问题与Android版本无关,而是您要裁剪的图像。在 com.android.gallery3d.filtershow.crop.CropActivity#BitmapIOTask
类中处理的裁剪图像。当图片太大无法返回时,会尝试返回图片的拇指,有时会返回null。
为避免这种情况,您可以通过设置intent.putExtra(MediaStore.EXTRA_OUTPUT, tmpUri);
来获取裁剪图像的uri 而不是位图,其中tmpUri
是为保存结果而创建的uri。然后你可以从tmpUri
获取位图。
示例代码:
private static final String IMAGE_FILE_LOCATION = "file:///sdcard/temp.jpg";//temp file
private static Uri tmpUri = Uri.parse(IMAGE_FILE_LOCATION);//The Uri to store the big bitmap
public static void cropImage(Uri uri, Activity activity, int action_code)
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", 600);
intent.putExtra("outputY", 600);
intent.putExtra("scale", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, tmpUri);
if (intent.resolveActivity(activity.getPackageManager()) != null)
activity.startActivityForResult(intent, action_code);
else
Toast.makeText(activity, "No Crop App Available", Toast.LENGTH_SHORT).show();
在函数onActivityResult中:
if (resultCode == Activity.RESULT_OK && requestCode == Utils.CODE_CROP_IMAGE)
// Bundle extras = data.getExtras();
Uri uri = data.getData();
showCenterToast("ccc");
if (uri != null)
showCenterToast("CCC");
// Bitmap photo = null;
// if (tmpUri != null)
// photo = decodeBitmapFromUri(tmpUri); // Get bitmap from uri.
//
Bitmap photo = decodeUriAsBitmap(uri);
ivAvatar.setImageBitmap(photo); // display image in ImageView
FileOutputStream fos = null;
try
fos = new FileOutputStream(Utils.AVATAR_FILE);
photo.compress(Bitmap.CompressFormat.PNG, 100, fos);// (0-100)compressing file
showCenterToast("DDD");
Utils.AVATAR_FILE_TMP.delete();
catch (FileNotFoundException e)
e.printStackTrace();
finally
IoUtil.closeSilently(fos);
else
showCenterToast("Uri is NULL");
private Bitmap decodeUriAsBitmap(Uri uri)
Bitmap bitmap = null;
try
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
catch (FileNotFoundException e)
e.printStackTrace();
return null;
return bitmap;
我没有测试我的代码是否正确,但我认为你可以修复错误。
【讨论】:
@SilentKnight Hava 你在onActivity
中有一个uri?如果 uri 不为空,则可以只获取文件的绝对路径并从文件中解码位图。
是的,我试过了。 BitmapFactory.decodeStream(getContentResolver().openInputStream(uri))
或 BitmapFactory.decodeFile(filePath)
,均无效。
我稍微编辑了我的代码并进行了测试,效果很好。是否将权限android.permission.WRITE_EXTERNAL_STORAGE
添加到 AndroidManifest.xml 中?
这是一个很好的答案。它解决了我的问题。非常感谢:)
哇!它确实有效,但我仍然困惑为什么会这样!当您设置为 imgview 时的 uri 它可以工作,但是如果您尝试 BitmapFactory.decodeFile(image_url, bitmapOptions decode 这会给您一个找不到文件或空异常。正如您提到的大位图,那么在这种情况下我们该怎么做这样的函数?顺便说一句,我认为 openstream 函数是位图案例中最好的函数之一。【参考方案2】:
我解决了它并正在工作
基本上,在片段中裁剪图像时,问题就在这里,当您使用活动时,您应该以这种方式将意图传递到活动结果中,以便按照此库进行裁剪的最佳方法
编译'com.theartofdev.edmodo:android-image-cropper:2.5.+'。
CropImage.activity(imageUri)
.setGuidelines(CropImageView.Guidelines.ON)
.start(getActivity());
当你使用 Fragment 时,你应该使用:
Uri selectedImageUri = data.getData();
CropImage.activity(selectedImageUri)
.setGuidelines(CropImageView.Guidelines.ON)
.start(getContext(), this);
有时 getcontext 需要 api 23 这是因为你使用 app.fragment,所以使用 android.support.v4.app。
使用活动和片段裁剪图像不同,请参阅此处我已成功实施此事物。请按照此处的链接获取指南。 how cropping image with in activity and fragment !!!
我在片段中裁剪图像时感觉很困难,所以它没有解决。首先,您从相机或图库中获取图像。
private void openGallery()
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, REQUEST_CODE_GALLERY);
如果从相机拍摄图像,请不要忘记包含文件提供者 Manifest fil ,如果您觉得麻烦,那么在下一步之前请点击此链接。
Android - file provider - permission denial
private String mCurrentPhotoPath;
private void openCameranoughat()
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null)
Uri photoURI = null;
try
File photoFile = createImageFile();
path = photoFile.getAbsolutePath();
photoURI = FileProvider.getUriForFile(getActivity(),
BuildConfig.APPLICATION_ID + ".provider",
createImageFile());
catch (IOException ex)
Log.e("TakePicture", ex.getMessage());
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
takePictureIntent.setClipData(ClipData.newRawUri("", photoURI));
takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(takePictureIntent, REQUEST_CODE_CAPTURE);
private File createImageFile() throws IOException
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File file = new File(path, "DemoPicture.jpg");
try
// Make sure the Pictures directory exists.
path.mkdirs();
catch (Exception e)
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = "file:" + file.getAbsolutePath();
return file;
现在关于 Fragment Activity 的活动结果,您必须编写此代码。
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
if (resultCode != RESULT_OK)
return;
switch (requestCode)
case ACTION_REQUEST_EDITIMAGE://
handleEditorImage(data);
break;
case REQUEST_CODE_GALLERY:
if (mCurrentPhotoPath == null)
Uri selectedImageUri = data.getData();
CropImage.activity(selectedImageUri)
.setGuidelines(CropImageView.Guidelines.ON)
.start(getContext(), this);
break;
case REQUEST_CODE_CAPTURE:
try
Uri imageUri = Uri.parse(mCurrentPhotoPath);
if (imageUri == null)
else
CropImage.activity(imageUri)
.setGuidelines(CropImageView.Guidelines.ON)
.start(getContext(), this);
MediaScannerConnection.scanFile(getActivity(),
new String[]imageUri.getPath(), null,
new MediaScannerConnection.OnScanCompletedListener()
public void onScanCompleted(String path, Uri uri)
);
mCurrentPhotoPath = null;
catch (Exception e)
break;
case CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE:
CropImage.ActivityResult result = CropImage.getActivityResult(data);
Uri resultUri = result.getUri();
if (resultUri != null)
path = resultUri.getPath();
if (TextUtils.isEmpty(path))
return;
Intent it = new Intent(getActivity(), EditImageActivity.class);
it.putExtra(EditImageActivity.FILE_PATH, path);
File outputFile = FileUtils.getEmptyFile("tietu"
+ System.currentTimeMillis() + ".jpg");
it.putExtra(EditImageActivity.EXTRA_OUTPUT,
outputFile.getAbsolutePath());
startActivityForResult(it,
ACTION_REQUEST_EDITIMAGE);
break;
case CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE:
Toast.makeText(getActivity(), "error in cropping", Toast.LENGTH_SHORT).show();
break;
super.onActivityResult(requestCode, resultCode, data);
【讨论】:
【参考方案3】:使用意图获取图像:
Intent pickImageIntent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
pickImageIntent.setType("image/*");
startActivityForResult(pickImageIntent, 100);
使用 Activity Result 接收数据并获取 Uri。
使用android-crop 裁剪并获取图像。
【讨论】:
【参考方案4】:在 Lollipop 上,裁剪后的图像作为对结果数据操作中 uri 的字符串引用返回。示例:
final String action = data.getAction();
Uri imageUri = Uri.parse(action)
【讨论】:
以上是关于从 Android Lollipop 中的 Uri 裁剪照片后总是返回 Null?的主要内容,如果未能解决你的问题,请参考以下文章
Android 5.0 Lollipop 中的 ActionMode ActionBar 样式(带有 AppCompat)
Android Lollipop 问题 - 无法将图像从相机加载到 ImageView
java Android 5.0(Lollipop)中的SurfaceTexture,TextureView,SurfaceView和GLSurfaceView