【问题标题】:Always Null returned after cropping a photo from a Uri in Android Lollipop?从 Android Lollipop 中的 Uri 裁剪照片后总是返回 Null?
【发布时间】: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 上,我确实认为返回了数据,但我不知道它在哪里以及如何获取它。
  • 你有解决办法吗?

标签: android crop onactivityresult


【解决方案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】:
    1. 使用意图获取图像:

      Intent pickImageIntent = new Intent(Intent.ACTION_PICK, 
              android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
      pickImageIntent.setType("image/*");
      startActivityForResult(pickImageIntent, 100);
      
    2. 使用 Activity Result 接收数据并获取 Uri。

    3. 使用android-crop 裁剪并获取图像。

    【讨论】:

      【解决方案4】:

      在 Lollipop 上,裁剪后的图像作为对结果数据操作中 uri 的字符串引用返回。示例:

      final String action = data.getAction();
      Uri imageUri = Uri.parse(action)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-25
        • 2013-03-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多