Best practices for retrieving and cropping images from a camera (album), best practices for tailoring

Source: Internet
Author: User

Best practices for retrieving and cropping images from a camera (album), best practices for tailoring

In the process of developing some apps, we may involve Avatar processing, such as getting an Avatar from a mobile phone or album, cropping it into an avatar you need, and setting or uploading an avatar. There are too many materials related to the Internet, but there are often various problems in practical applications, and there is no perfect solution. Due to the needs of the recent project, I have studied it. At present, it seems that there are no problems.

Here we will only discuss acquisition, cropping and setting. The upload process will be added based on your business needs. First, go to a flowchart:

This figure was drawn using Google Drive's drawing tool. I had to wonder that Google could make the online editing tool so powerful. Okay, I am Google's brain powder! Back to the topic, this is my design philosophy. Next we will analyze it in detail:

1. There are only two ways to obtain images. The first is camera shooting, and the second is to obtain images from a local album.

2. I created a folder on the SD card with two Uris. One is used to save the original image and the other is the saved cropped image. I have previously considered using the same Uri to save the image, but in practice I encountered a problem. If the image is not cropped after being taken, the next time I get the image from the SD card, it will be the big image saved by the photo, not only does the previously cropped image be lost, but memory crashes due to large image loading. Based on this consideration, I have selected two URIs to save the image separately.

3. When the camera is shooting, we use Intent to call the System camera and set the settings output to SDCard \ xx \ photo_file.jpg. The following is a code snippet:

// Call the System camera Intent intentCamera = new Intent (MediaStore. ACTION_IMAGE_CAPTURE); // Save the photo result to the Uri of photo_file, which is not retained in the album intentCamera. putExtra (MediaStore. EXTRA_OUTPUT, imagePhotoUri); startActivityForResult (intentCamera, PHOTO_REQUEST_CAREMA );

During callback, the program uses the system tool to crop photo_file.jpg and sets the output to SDCard \ xx \ crop_file.jpg. The following is a code snippet:

Case PHOTO_REQUEST_CAREMA: if (resultCode = RESULT_ OK) {// retrieve the image from the Uri saved by the camera and call the system tailoring tool if (imagePhotoUri! = Null) {CropUtils. cropImageUri (this, imagePhotoUri, imageUri, ibUserIcon. getWidth (), ibUserIcon. getHeight (), PHOTO_REQUEST_CUT);} else {ToastUtils. show (this, "No picture taken") ;}} else if (resultCode = RESULT_CANCELED) {ToastUtils. show (this, "cancel photo");} else {ToastUtils. show (this, "photo failed");} break;
// Call the system to crop and save the image to the imageUri.
Public static void cropImageUri (Activity activity, Uri orgUri, Uri desUri, int width, int height, int requestCode) {Intent intent = new Intent ("com. android. camera. action. CROP "); intent. setDataAndType (orgUri, "image/*"); intent. putExtra ("crop", "true ");
Intent. putExtra ("aspectX", 1); intent. putExtra ("aspectY", 1); intent. putExtra ("outputX", width); intent. putExtra ("outputY", height); intent. putExtra ("scale", true); // Save the cropped image to the destination Uri. putExtra (MediaStore. EXTRA_OUTPUT, desUri); intent. putExtra ("return-data", false); intent. putExtra ("outputFormat", Bitmap. compressFormat. JPEG. toString (); intent. putExtra ("noFaceDetection", true); activity. startActivityForResult (intent, requestCode );
}

At the end of the page, you need to extract crop_file.jpg from the callback. Because the image has been compressed during cropping, you don't have to worry about the memory. Here I provide two methods. The first one is to directly obtain the Bitmap of the original image, the second is to get the original image and make it circular. I believe most people are interested in the latter. Haha! The following is a code snippet:

Case PHOTO_REQUEST_CUT: if (resultCode = RESULT_ OK) {Bitmap bitmap = decodeUriiAsBimap (this, imageCropUri)} else if (resultCode = RESULT_CANCELED) {ToastUtils. show (this, "cancel cropping images");} else {ToastUtils. show (this, "failed to cut");} break;
// Obtain an image in the Bitmap format from the Uri private static Bitmap decodeUriAsBitmap (Context context, Uri uri) {Bitmap bitmap; try {bitmap = BitmapFactory. decodeStream (context. getContentResolver (). openInputStream (uri);} catch (FileNotFoundException e) {e. printStackTrace (); return null;} return bitmap ;}
// Obtain the circular image public static Bitmap getRoundedCornerBitmap (Bitmap bitmap) {if (bitmap = null) {return null;} Bitmap output = Bitmap. createBitmap (bitmap. getWidth (), bitmap. getHeight (), Bitmap. config. ARGB_8888); Canvas canvas = new Canvas (output); final Paint paint = new Paint ();/* de-sawtooth */paint. setAntiAlias (true); paint. setFilterBitmap (true); paint. setDither (true); // It Must Be Square and int width = bitmap is drawn from the center. getWidth (); int height = bitmap. getHeight (); int w; int deltaX = 0; int deltaY = 0; if (width <= height) {w = width; deltaY = height-w ;} else {w = height; deltaX = width-w;} final Rect rect = new Rect (deltaX, deltaY, w, w); final RectF rectF = new RectF (rect ); paint. setAntiAlias (true); canvas. drawARGB (0, 0, 0, 0); // circle, all use only one int radius = (int) (Math. sqrt (w * 2.0d)/2); canvas. drawRoundRect (rectF, radius, radius, paint); paint. setXfermode (new porterduxfermode (PorterDuff. mode. SRC_IN); canvas. drawBitmap (bitmap, rect, rect, paint); return output ;}

4. This is also the most difficult part to get from the album. In versions earlier than Android 4.4, the image Uri obtained from the album can call the system tailoring tool perfectly, or the Intent of the cropped image can be directly selected from the album, and the effect is very perfect. However, in Android 4.4 and later versions, the obtained Uri cannot call the system tailoring tool, which directly causes the program to crash. I also studied it for a long time and found that there is a big difference between the two Uris. In the official Google documentation, developers can use Intent. ACTION_GET_CONTENT replaces the previous actions, and even if you still use the previous actions, a new type of Uri is returned. I personally guess that Google makes all the content retrieved and shared into a uniform Uri, if any, please correct me! After thinking about this, the problem becomes simple. I re-encapsulate this new Uri and thought "file :\\... "the absolute path strength of the standard was successfully passed into the system tailoring tool, but the encapsulation process was difficult. After reading a lot of information, I finally got it. The specific steps are as follows:

1. Call the system album. The following is a code snippet:

// Call the system album Intent photoPickerIntent = new Intent (Intent. ACTION_GET_CONTENT); photoPickerIntent. setType ("image/*"); startActivityForResult (photoPickerIntent, PHOTO_REQUEST_GALLERY );

Second, in the callback process, re-register the URL and use the system clipping tool to set the output to crop_file.jpg. The code of the system cropping tool has been pasted in the steps for obtaining the photo. Here, we will not duplicate the wheel and re-encapsulate the Uri code. The following code snippets are:

Case PHOTO_REQUEST_GALLERY: if (resultCode = RESULT_ OK) {// After the album is selected successfully, extract the absolute path of the image from the Uri and call cut Uri newUri = Uri. parse ("file: //" + CropUtils. getPath (this, data. getData (); if (newUri! = Null) {CropUtils. cropImageUri (this, newUri, imageUri, ibUserIcon. getWidth (), ibUserIcon. getHeight (), PHOTO_REQUEST_CUT);} else {ToastUtils. show (this, "no album image") ;}} else if (resultCode = RESULT_CANCELED) {ToastUtils. show (this, "cancel from album selection");} else {ToastUtils. show (this, "failed to select from album");} break;
@SuppressLint("NewApi")public static String getPath(final Context context, final Uri uri) {final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;// DocumentProviderif (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {  // ExternalStorageProvider  if (isExternalStorageDocument(uri)) {    final String docId = DocumentsContract.getDocumentId(uri);    final String[] split = docId.split(":");    final String type = split[0];    if ("primary".equalsIgnoreCase(type)) {      return Environment.getExternalStorageDirectory() + "/"+ split[1];    }  }  // DownloadsProvider  else if (isDownloadsDocument(uri)) {    final String id = DocumentsContract.getDocumentId(uri);    final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(id));    return getDataColumn(context, contentUri, null, null);  }  // MediaProvider  else if (isMediaDocument(uri)) {    final String docId = DocumentsContract.getDocumentId(uri);    final String[] split = docId.split(":");    final String type = split[0];    Uri contentUri = null;    if ("image".equals(type)) {      contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;    } else if ("video".equals(type)) {      contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;    } else if ("audio".equals(type)) {      contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;    }    final String selection = "_id=?";    final String[] selectionArgs = new String[]{split[1]};    return getDataColumn(context, contentUri, selection,selectionArgs);    }  }  // MediaStore (and general)  else if ("content".equalsIgnoreCase(uri.getScheme())) {    return getDataColumn(context, uri, null, null);  }  // File  else if ("file".equalsIgnoreCase(uri.getScheme())) {    return uri.getPath();  }  return null;}/*** Get the value of the data column for this Uri. This is useful for* MediaStore Uris, and other file-based ContentProviders.** @param context       The context.* @param uri           The Uri to query.* @param selection     (Optional) Filter used in the query.* @param selectionArgs (Optional) Selection arguments used in the query.* @return The value of the _data column, which is typically a file path.*/private static String getDataColumn(Context context, Uri uri,String selection, String[] selectionArgs) {  Cursor cursor = null;  final String column = "_data";  final String[] projection = {column};  try {    cursor = context.getContentResolver().query(uri, projection,selection, selectionArgs, null);    if (cursor != null && cursor.moveToFirst()) {      final int column_index = cursor.getColumnIndexOrThrow(column);      return cursor.getString(column_index);    }  } finally {    if (cursor != null)      cursor.close();  }  return null;}/*** @param uri The Uri to check.* @return Whether the Uri authority is ExternalStorageProvider.*/private static boolean isExternalStorageDocument(Uri uri) {  return "com.android.externalstorage.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is DownloadsProvider.*/private static boolean isDownloadsDocument(Uri uri) {  return "com.android.providers.downloads.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is MediaProvider.*/private static boolean isMediaDocument(Uri uri) {  return "com.android.providers.media.documents".equals(uri.getAuthority());}

The subsequent system tailoring tool calls are the same as the photo acquisition steps. For more information, see the code above.

5. All the steps have been completed, and the tests passed in the latest system of the Nexus 5 device have also been perfect for Xiaomi, Samsung, and other devices. If you have any defects on your device, please post them to me. Thank you!

 

Record this, hoping to help some friends who are encountering such problems!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.