Android big picture cropping finally

About a few months ago, I was troubled by the company's APP to take screenshots on Android phones.

Searching on the Internet, there are indeed many examples, most of which are copied and copied, and most of them are in the form of demos, which can be used to explain knowledge points, but when they encounter actual projects, they are full of loopholes.

At that time, I used a popular solution and temporarily made a function of taking screenshots, which seemed to be very good. The problem followed, I was using a Xiaomi phone, and it worked fine on other phones, but Xiaomi always hit a nail here. Although I am a rational rice fan, I secretly greeted Xiaomi's engineers. What a shame!

I can't find an answer in the documentation. I have always had a lot of questions about com.android.camera.action.CROP. Where does it come from, what can it do, and what type of data does it receive and process? Google is very secretive about this. In the official document, there are only a few words in the Intent, but it is not very detailed.

With the drive of the project, I can't hold the mentality of not moving forward without understanding the principle, the only thing to do is to solve the problem. Finally, I found a solution on Dewen, saying that even rice is no problem. At that time, I changed the code with joy, and it did run on all mobile phones. I was relieved for a while, and my doubts about this were also forgotten.

Until a month ago, BOSS required that the resolution of pictures uploaded to the server be doubled. OK, twice as simple, just increase outputX and outputY?

intent.putExtra("outputX", outputX);
intent.putExtra("outputY", outputY);

This increase surprised me. The photos taken by BOSS’s mobile phone are almost like thumbnails, but Xiaomi, who was greeted by all the engineers, showed the style of the domestic magic machine at this time, and the dimensions on Xiaomi were all normal. Why is this? I have a general idea of ​​the cause, but don't know how to fix it.

In Android, the Intent triggers the Camera program, and after the photo is taken, the data will be returned, but considering the memory problem, the Camera will not return the full-size image to the calling Activity. Under normal circumstances, it is possible to return a thumbnail image , such as 120*160px.

Why is this? It's not a bug, it's by design, but not transparent to the developer.

Taking my Xiaomi mobile phone as an example, the camera is 800W pixels, and the size of the picture taken according to my current settings is 3200*2400px. Some people say, then go back, it's a big deal to consume 1-2M of memory, yes, a picture of this size is indeed only about 1.8M in size. But what you don't expect is that the Bitmap corresponding to this size will consume all the memory of your application. Android just gives you a shabby thumbnail for security reasons.

In Android 2.3, the default Bitmap is 32 bits and the type is ARGB_8888, which means that one pixel occupies 4 bytes of memory. Let's do a simple calculation problem: 3200*2400*4 bytes = 30M.

Such an amazing number! Even if you are willing to spend such a huge amount of memory for a bitmap whose life cycle does not exceed 10s, Android will not allow it.

Mobile devices typically have constrained system resources.
Android devices can have as little as 16MB of memory available to a single application.

 This is the original text of the Android Doc. Although manufacturers of different mobile phone systems may slightly increase the number around 16M, this 30M is really unaffordable for ordinary mobile phones. Only a machine like Xiaomi, whose memory is comparable to a personal PC, can do it with the domineering arrogance of a local rich man.

OK, having said so much, it is nothing more than spitting bitterness and exploding personal experience. Where is the actual solution?

I also came to Google. If you have a problem with Baidu, you can use Google or StackOverFlow directly, but you have to read English.

In the end, after tossing and turning, I found the corresponding solution in the blog of an Android team abroad, which confirmed my conjecture and gave the actual code.

I have translated this article into Chinese as the basis of this blog, and it is recommended to read it in detail.

【Translation】How to crop large images using Android MediaStore  http://www.linuxidc.com/Linux/2012-11/73939.htm

The great thing about this URL is that it solves Android's limit on the size of the returned image, and it explains in detail the specific meaning of the additional data of the Intent of the cropped image. OK, I'm just standing on the shoulders of giants, improving the program and adapting to wider needs.

Take the picture to tell the story:



 

All optional data corresponding to Intent("com.android.camera.action.CROP") is clear at a glance. After understanding the meaning of each of the above options, we will focus on three extremely important options:

data、MediaStore.EXTRA_OUTPUT以及return-data。

Both data and MediaStore.EXTRA_OUTPUT are optional incoming data options. You can choose to set data as a Bitmap, or associate the corresponding data with a URI, and you can also choose whether to return data (return-data: true).

Why is there an option not to return data? If you know enough about URI, you should know that URI is similar to File. All your operations, such as cropping, save data in URI. You already hold the corresponding URI, so you don’t need to do anything more and return to Bitmap.

As mentioned earlier, data can be set to Bitmap, but the limitation of this operation is that your Bitmap cannot be too large. Therefore, our way forward seems to be clear: use URI for large pictures and Bitmap for small pictures.

I organized this idea into a picture:



 

 

According to our analysis and summary, the sources of pictures include photos and albums, and the actions that can be taken include:

• Use Bitmap and return data

• Using Uri does not return data

前面我们了解到,使用Bitmap有可能会导致图片过大,而不能返回实际大小的图片,我将采用大图Uri,小图Bitmap的数据存储方式。

我们将要使用到URI来保存拍照后的图片:

private static final String IMAGE_FILE_LOCATION = "file:///sdcard/temp.jpg";//temp file
Uri imageUri = Uri.parse(IMAGE_FILE_LOCATION);//The Uri to store the big bitmap

不难知道,我们从相册选取图片的Action为Intent.ACTION_GET_CONTENT。

根据我们上一篇文章的分析,我准备好了两个实例的Intent。

一、从相册截大图:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
intent.setType("image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 2);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 600);
intent.putExtra("outputY", 300);
intent.putExtra("scale", true);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true); // no face detection
startActivityForResult(intent, CHOOSE_BIG_PICTURE);

二、从相册截小图

Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
intent.setType("image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 2);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 200);
intent.putExtra("outputY", 100);
intent.putExtra("scale", true);
intent.putExtra("return-data", true);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true); // no face detection
startActivityForResult(intent, CHOOSE_SMALL_PICTURE);

三、对应的onActivityResult可以这样处理返回的数据

switch (requestCode) {
case CHOOSE_BIG_PICTURE:
 Log.d(TAG, "CHOOSE_BIG_PICTURE: data = " + data);//it seems to be null
 if(imageUri != null){
  Bitmap bitmap = decodeUriAsBitmap(imageUri);//decode bitmap
  imageView.setImageBitmap(bitmap);
 }
 break;
case CHOOSE_SMALL_PICTURE:
 if(data != null){
  Bitmap bitmap = data.getParcelableExtra("data");
  imageView.setImageBitmap(bitmap);
 }else{
  Log.e(TAG, "CHOOSE_SMALL_PICTURE: data = " + data);
 }
 break;
default:
 break;
}

 

private Bitmap decodeUriAsBitmap(Uri uri){
 Bitmap bitmap = null;
 try {
  bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
 } catch (FileNotFoundException e) {
  e.printStackTrace();
  return null;
 }
 return bitmap;
}

 

 

拍照截图有点儿特殊,要知道,现在的Android智能手机的摄像头都是几百万的像素,拍出来的图片都是非常大的。因此,我们不能像对待相册截图一样使用Bitmap小图,无论大图小图都统一使用Uri进行操作。

一、首先准备好需要使用到的Uri:

private static final String IMAGE_FILE_LOCATION = "file:///sdcard/temp.jpg";//temp file
Uri imageUri = Uri.parse(IMAGE_FILE_LOCATION);//The Uri to store the big bitmap

二、使用MediaStore.ACTION_IMAGE_CAPTURE可以轻松调用Camera程序进行拍照:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//action is capture
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_BIG_PICTURE);//or TAKE_SMALL_PICTURE

三、接下来就可以在 onActivityResult中拿到返回的数据(Uri),并将Uri传递给截图的程序。

switch (requestCode) {
case TAKE_BIG_PICTURE:
 Log.d(TAG, "TAKE_BIG_PICTURE: data = " + data);//it seems to be null
 //TODO sent to crop
 cropImageUri(imageUri, 800, 400, CROP_BIG_PICTURE);
 
 break;
case TAKE_SMALL_PICTURE:
 Log.i(TAG, "TAKE_SMALL_PICTURE: data = " + data);
 //TODO sent to crop 
 cropImageUri(imageUri, 300, 150, CROP_SMALL_PICTURE);
 
 break;
default:
 break;
}

可以看到,无论是拍大图片还是小图片,都是使用的Uri,只是尺寸不同而已。我们将这个操作封装在一个方法里面。

private void cropImageUri(Uri uri, int outputX, int outputY, int requestCode){
 Intent intent = new Intent("com.android.camera.action.CROP");
 intent.setDataAndType(uri, "image/*");
 intent.putExtra("crop", "true");
 intent.putExtra("aspectX", 2);
 intent.putExtra("aspectY", 1);
 intent.putExtra("outputX", outputX);
 intent.putExtra("outputY", outputY);
 intent.putExtra("scale", true);
 intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
 intent.putExtra("return-data", false);
 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
 intent.putExtra("noFaceDetection", true); // no face detection
 startActivityForResult(intent, requestCode);
}

Fourth, the last step, we have passed the data into the cropping picture program, the next thing to do is to process the returned data:

switch (requestCode) {
case CROP_BIG_PICTURE://from crop_big_picture
 Log.d(TAG, "CROP_BIG_PICTURE: data = " + data);//it seems to be null
 if(imageUri != null){
  Bitmap bitmap = decodeUriAsBitmap(imageUri);
  imageView.setImageBitmap(bitmap);
 }
 break;
case CROP_SMALL_PICTURE:
 if(imageUri != null){
  Bitmap bitmap = decodeUriAsBitmap(imageUri);
  imageView.setImageBitmap(bitmap);
 }else{
  Log.e(TAG, "CROP_SMALL_PICTURE: data = " + data);
 }
 break;
default:
 break;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325581760&siteId=291194637
Recommended