如何使用com.android.camera.action.CROP设置输出图像

8

我有一段裁剪图像的代码,就像这样:

public void doCrop(){
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setType("image/");
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent,0);
int size = list.size();
if (size == 0 ){
   Toast.makeText(this, "Cant find crop app").show();
   return;
} else{
   intent.setData(selectImageUri);
   intent.putExtra("outputX", 300);
   intent.putExtra("outputY", 300);
   intent.putExtra("aspectX", 1);
   intent.putExtra("aspectY", 1);
   intent.putExtra("scale", true);
   intent.putExtra("return-data", true);
   if (size == 1) {
       Intent i = new Intent(intent);
       ResolveInfo res = list.get(0);
       i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
       startActivityForResult(i, CROP_RESULT);
   }
}
}

public void onActivityResult (int requestCode, int resultCode, Intent dara){
   if (resultCode == RESULT_OK){
      if (requestCode == CROP_RESULT){
          Bundle extras = data.getExtras();
          if (extras != null){
              bmp = extras.getParcelable("data");
          }
          File f = new File(selectImageUri.getPath());
          if (f.exists()) f.delete();
          Intent inten3 = new Intent(this, tabActivity.class);
          startActivity(inten3);
      }
   }
}

据我所读,代码 intent.putExtra("outputX", 300); intent.putExtra("outputY", 300); 用于设置裁剪结果的分辨率,但为什么我无法获得高于 300x300 的结果图像分辨率?当我设置 intent.putExtra("outputX", 800); intent.putExtra("outputY", 800); 时,裁剪功能没有结果或崩溃,针对这种情况有什么想法吗?
日志显示“! ! ! FAILED BINDER TRANSACTION ! ! !”

只是一个提示:如果安装了多个提供CROP操作的应用程序,则if(size == 1)将使您的应用程序受到影响。如果我是你,我会跳过这一部分。此外,请考虑使用Intent#createChooser(..)方法,而不是启动第一个可用组件-它可能不是用户首选的应用程序。 - Jens
哈哈哈,是的,我修改了我的代码并跳过了 if (size==1)。 - adi.zean
2个回答

28

这个问题在stackoverflow上到处都是。我很高兴看到它,因为最近我不得不自己解决这个问题。我会尽力标记一些重复的内容,但我更喜欢这个问题,因为它涉及有限的图像大小问题。

简短回答

简短回答是不要使用return-data选项。 在这里阅读更多关于该选项以及如何检索图像的信息:http://www.androidworks.com/crop_large_photos_with_android。 该文章在列出Intent的(已知)配置选项以及如何使用它们方面做得非常好。

选项#2:如果将return-data设置为“false”,则您将无法从onActivityResult Intent中直接接收位图,而必须将MediaStore.EXTRA_OUTPUT设置为Uri(仅文件模式),其中您希望存储位图。 这有一些限制,首先需要一个临时文件系统位置才能提供文件URI,这不是什么大问题(除了一些没有SD卡的设备)。

    /** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    thiz = this;
    setContentView(R.layout.main);
    mBtn = (Button) findViewById(R.id.btnLaunch);
    photo = (ImageView) findViewById(R.id.imgPhoto);
    mBtn.setOnClickListener(new OnClickListener(){

        public void onClick(View v) {
            try {
                // Launch picker to choose photo for selected contact
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
                intent.setType("image/*");
                intent.putExtra("crop", "true");
                intent.putExtra("aspectX", aspectX);
                intent.putExtra("aspectY", aspectY);
                intent.putExtra("outputX", outputX);
                intent.putExtra("outputY", outputY);
                intent.putExtra("scale", scale);
                intent.putExtra("return-data", return_data);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());
                intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
                intent.putExtra("noFaceDetection",!faceDetection); // lol, negative boolean noFaceDetection
                if (circleCrop) {
                    intent.putExtra("circleCrop", true);
                }

                startActivityForResult(intent, PHOTO_PICKED);
            } catch (ActivityNotFoundException e) {
                Toast.makeText(thiz, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
            }
        }
    });
}

private Uri getTempUri() {
    return Uri.fromFile(getTempFile());
}

private File getTempFile() {
    if (isSDCARDMounted()) {
        File f = new File(Environment.getExternalStorageDirectory(),TEMP_PHOTO_FILE);
        try {
            f.createNewFile();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            Toast.makeText(thiz, R.string.fileIOIssue, Toast.LENGTH_LONG).show();
        }
        return f;
    } else {
        return null;
    }
}

private boolean isSDCARDMounted(){
    String status = Environment.getExternalStorageState();    
    if (status.equals(Environment.MEDIA_MOUNTED))
        return true;
    return false;
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    switch (requestCode) {
        case PHOTO_PICKED:
            if (resultCode == RESULT_OK) {
                if (data == null) {
                    Log.w(TAG, "Null data, but RESULT_OK, from image picker!");
                    Toast.makeText(this, R.string.no_photo_picked,
Toast.LENGTH_SHORT).show();
                    return;
                }

            final Bundle extras = data.getExtras();
            if (extras != null) {
                File tempFile = getTempFile();
                // new logic to get the photo from a URI
                if (data.getAction() != null) {
                    processPhotoUpdate(tempFile);
                }
            }
        }
        break;
    }
}

来自以下网址的代码示例: http://www.androidworks.com/crop_large_photos_with_android

更多信息

详细回答是不要使用该意图。请继续阅读以了解原因。

非官方 API

问题的核心是非官方意图。非官方的意思是不属于公共 API 的一部分。目前它可以在大多数设备上工作,但这还远远不够。此外,谷歌可以随时更改此意图,而不让您知道。这将破坏所有使用它的应用程序。这有点像日历 API 曾经是非官方的情况。实际上,这个剪裁意图已经改变过一次。因此,请避免使用此意图。有替代方案。如果您愿意,请忽略此建议。

为了验证“适用于某些设备”的说法,请跟随此链接并享受沮丧的 Android 开发人员讨论什么应该被视为核心 Android 功能(但却不是):https://code.google.com/p/android/issues/detail?id=1480

是时候给出示例代码了吗?请查看此 GitHub 项目:https://github.com/lorensiuswlt/AndroidImageCrop

关于大小限制

我在探索此意图时遇到的另一个问题是图像裁剪大小限制。只需使用上面的示例代码和大于 300 像素的图像大小即可轻松重现。这基本上就是原始问题所涉及的内容。在最好的情况下,您的应用程序将崩溃。但我见过更糟糕的情况,甚至导致设备挂起,只能通过拆卸电池来重置。

现在,如果您删除该“return-data”选项,则可以再次运行它。有关如何获得结果的更多信息可以在简短的答案中找到,其中我已经引用了此链接:http://www.androidworks.com/crop_large_photos_with_android

解决方案

所以有很多问题。问题需要解决。在 Google 推出公共 API 之前,唯一的合理解决方案是提供自己的裁剪意图。只需获取一个适当的裁剪库,比如在 GitHub 上的这个:https://github.com/lvillani/android-cropimage

该项目缺乏一些文档,但由于它是非官方的 Android 裁剪意图的提取,因此您可以使用上面列出的示例来开始。只要确保不使用“return-data”选项即可。啊,请查看 CropImageIntentBuilder 类。这应该让您轻松创建用于裁剪的意图。别忘了将此 Activity 添加到您的清单中,并添加写入外部数据存储的权限。

private void doCrop(File croppedResult){
        CropImageIntentBuilder builder = new CropImageIntentBuilder(600,600, croppedResult);
        // don't forget this, the error handling within the library is just ignoring if you do
        builder.setSourceImage(mImageCaptureUri);
        Intent  intent = builder.getIntent(getApplicationContext());
        // do not use return data for big images
        intent.putExtra("return-data", false);
        // start an activity and then get the result back in onActivtyResult
        startActivityForResult(intent, CROP_FROM_CAMERA);
    }

使用此库还可以打开更多自定义的门。需要知道的是用于调整大小的核心位图功能:如何在Android中裁剪解析的图像?
就是这样。享受吧!

我尝试将这个库与我的应用程序集成,但是我无法做到。您能否提供如何做到这一点的说明?目前,我将文件复制并粘贴到我的应用程序中,希望有一种方法可以在不将其引入我的应用程序的情况下引用此库。 - Parth Shah
你真是神奇的。通过图像裁剪解决了我所有的问题。 - Jigar
1
链接已经失效了 :( - ezain

8

该Intent不属于公共的Android API,不能保证在所有设备上都可用。它在早期版本的Android 1.x和2.x中使用,但现在已不再使用,并且不建议使用。这可能是为什么它在各个地方崩溃或工作不正常的原因。

使用诸如Bitmap.createBitmap(..)Bitmap.createScaledBitmap(..)之类的方法创建您原始图像的调整大小或裁剪版本。它们是Android API的一部分并且可以保证运行。

请参阅官方文档herehere

要裁剪位图,您可以使用Bitmap.createBitmap(Bitmap, int x, int y, int width, int height)。例如,如果您需要从位图的每侧裁剪10个像素,则使用以下内容:

Bitmap croppedBitmap = Bitmap.createBitmap(originalBitmap, 10, 10, originalBitmap.getWidth() - 20, originalBitmap.getHeight() - 20);

如果您需要向用户显示选择器,那么可以按照以下方式操作:
private static final String TEMP_PHOTO_FILE = "temporary_holder.jpg";  

Intent photoPickerIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
photoPickerIntent.setType("image/*");
photoPickerIntent.putExtra("crop", "true");
photoPickerIntent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());
photoPickerIntent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
startActivityForResult(photoPickerIntent, REQ_CODE_PICK_IMAGE);


    private Uri getTempUri() {
    return Uri.fromFile(getTempFile());
    }

    private File getTempFile() {
    if (isSDCARDMounted()) {

    File f = new File(Environment.getExternalStorageDirectory(),TEMP_PHOTO_FILE);
    try {
    f.createNewFile();
    } catch (IOException e) {

    }
    return f;
    } else {
    return null;
    }
    }

    private boolean isSDCARDMounted(){
    String status = Environment.getExternalStorageState();
    if (status.equals(Environment.MEDIA_MOUNTED))
    return true;
    return false;
    }




protected void onActivityResult(int requestCode, int resultCode,
        Intent imageReturnedIntent) {
    super.onActivityResult(requestCode, resultCode, imageReturnedIntent);

    switch (requestCode) {
    case REQ_CODE_PICK_IMAGE:
        if (resultCode == RESULT_OK) {  
          if (imageReturnedIntent!=null){



               File tempFile = getTempFile();

              String filePath= Environment.getExternalStorageDirectory()
            + "/temporary_holder.jpg";
              System.out.println("path "+filePath);


    Bitmap selectedImage =  BitmapFactory.decodeFile(filePath);
    _image = (ImageView) findViewById(R.id.image);
    _image.setImageBitmap(selectedImage );

}
}
}

代码来自这里


嗯,我不知道如何制作像这个意图那样的选择区域,所以我使用了这个意图。你知道如何制作选择区域吗? - adi.zean
我已经更新了我的回答,你可以使用我发布的代码来创建一个选择区域,使用一个意图调用用户拥有的任何可以裁剪图像的应用程序。然后,您可以检索裁剪后的图像并将其设置为所示。 - Anup Cowkur
1
是的,我已经尝试了代码并且它有效,但是还有一个问题,我可以获取正方形的坐标吗? 我想编辑原始位图,并将裁剪后的图像替换为原始位图中的相同位置。 - adi.zean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接