从 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

Android 5.0 Lollipop介绍

java Android 5.0(Lollipop)中的SurfaceTexture,TextureView,SurfaceView和GLSurfaceView

android lollipop 中的顶部栏如何隐藏?

无法从我的服务类(Android Lollipop)启动设备管理员活动