拍照、相册及裁剪的终极实现(二)——相册选择及裁剪功能实现

来源:互联网 发布:linux 关闭mysql服务 编辑:程序博客网 时间:2024/06/06 01:04

在上篇中,我们看到有关拍照和裁剪的方案,这里带大家看看如何从相册中选择文件并裁剪。


一、从相册选择图像并显示

先看看效果图:

如图一,有四个按钮,这里用第一个按钮:(相册选择——返回值),它的意思是通过intent.的data域来获取返回的图像数据;点击后转到图片选择页面,选择后,在底部将显示的图片显示出来。

                    (一)                                                (二)                                                        (三)

  

在上篇中我们提到过启动相册的ACTION为:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static final java.lang.String ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT";  
所以隐匿启动相册Intent的代码为:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);  
  2. intent.setType("image/*");  
  3. startActivityForResult(intent, <span style="font-family: Arial, Helvetica, sans-serif;">RESULT_ALBUM_ONLY_THROUGH_DATA</span><span style="font-family: Arial, Helvetica, sans-serif;">);</span>  

在开启相册之后,选择对应的图片,然后接收Result:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  2.     super.onActivityResult(requestCode, resultCode, data);  
  3.     if (resultCode != Activity.RESULT_OK) {  
  4.         return;  
  5.     }  
  6.     switch (requestCode) {  
  7.         case RESULT_ALBUM_ONLY_THROUGH_DATA: {  
  8.             //照片的原始资源地址  
  9.             try {  
  10.                 //使用ContentProvider通过URI获取原始图片  
  11.                 Bitmap photo = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());  
  12.                 if (photo != null) {  
  13.                     Bitmap smallBmp = setScaleBitmap(photo, 2);  
  14.                     mImageView.setImageBitmap(smallBmp);  
  15.                 }  
  16.             } catch (FileNotFoundException e) {  
  17.                 e.printStackTrace();  
  18.             } catch (IOException e) {  
  19.                 e.printStackTrace();  
  20.             }  
  21.         }  
  22.         break;  
  23.     }  
  24. }  

这里要注意一下,这里没有将return-data设为false,也就是使用了默认值,即直接使用Intent中的data域来传递结果值,但问题在于Data域最大传递的值的大小为1M,所以图片的BITMAP当超过1M时就会失败。从而显示缩略图。

这里先演示这种方法的效果,至于从相册选择的终极方案,同样,我们会在讲过裁剪后给出。

二、从相册选取并裁剪(通过URI返回)

先看下效果:

点击“相册并截取——URI”(图一),转入相册页面(图二),选中一张图片,转到裁剪页面(图三),将裁剪后的图片显示在imageView中(图四)

(一) (二)

  

(三)                                      (四)

  

还记得上篇中的裁剪用的N多参数么,这里再给大家贴一下

Exta Options Table for image/* crop:

附加选项数据类型描述cropString发送裁剪信号aspectXintX方向上的比例aspectYintY方向上的比例outputXint裁剪区的宽outputYint裁剪区的高scaleboolean是否保留比例return-databoolean是否将数据保留在Bitmap中返回dataParcelable相应的Bitmap数据circleCropString圆形裁剪区域?MediaStore.EXTRA_OUTPUT ("output")URI将URI指向相应的file:///...,详见代码示例outputFormatString输出格式,一般设为Bitmap格式:Bitmap.CompressFormat.JPEG.toString()noFaceDetectionboolean是否取消人脸识别功能上篇我们说过这些参数可以随意匹配,那这里我们就用来裁剪从相册转过来的图片;

首先,启动选择相册Intent,然后配置各个参数:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);  
  2. intent.setType("image/*");  
  3. intent.putExtra("crop""true");  
  4. intent.putExtra("aspectX"1);  
  5. intent.putExtra("aspectY"1);  
  6. intent.putExtra("outputX"1000);  
  7. intent.putExtra("outputY"1000);  
  8. intent.putExtra("scale"true);  
  9. intent.putExtra("return-data"false);  
  10. intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);  
  11. intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());  
  12. intent.putExtra("noFaceDetection"true);  
  13. startActivityForResult(intent, RESULT_ALBUM_CROP_URI);  
上面这段代码首先将action设为启动相册Intent的action:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static final java.lang.String ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT";  
然后不让数据从Intent的data域返回,而是保存在我们指定的URI中;
然后在接收时:

直接从URI中获取图片,然后显示在imageView中;

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  2.     super.onActivityResult(requestCode, resultCode, data);  
  3.     if (resultCode != Activity.RESULT_OK) {  
  4.         return;  
  5.     }  
  6.     switch (requestCode) {  
  7.         case RESULT_ALBUM_CROP_URI: {  
  8.             try {  
  9.                 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));  
  10.                 if (bitmap != null) {  
  11.                     Bitmap smallBmp = setScaleBitmap(bitmap, 2);  
  12.                     mImageView.setImageBitmap(smallBmp);  
  13.                 }  
  14.             } catch (Exception e) {  
  15.                 e.printStackTrace();  
  16.             }  
  17.         }  
  18.         break;  
  19.     }  
  20. }  
看着好像没什么事情,但问题来了:在有些手机上根本出不来裁剪页面,这是为什么呢?
还记得在上篇中拍照时也出现过出不来裁剪页面的情况,当时是由于Intent之间传递数据最大只能传1M左右,当超过这个数据时就会传递不成功,所以在上篇中我们为了传递拍照的结果,把它暂存到本地图片中,然后在重新调用裁剪Intent,把数据传进去,让它裁剪。通过绕过Intent之间直接数据传递,而使用间接传递数据的方法来完成较大数据的传递。那这里要怎么办呢?有没有一种方法能找到用户点击的图片的地址呢?如果能找到地址,那在用户点击一个图片后,再调用裁剪的Intent,从本地读取图片数据传进去裁剪Intent让它来裁剪,这样就绕过了直接通过Intent数据传递的大小限制。

三、从相册选取并裁剪(通过Path:终极方案)

找来找去,终于在开源项目中找到了一个方法,通过传进去Intent的data域返回的URI数据,找到图片地址,代码如下:

我没看懂,只能给大家贴代码了,非常抱歉。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // 解析获取图片库图片Uri物理路径  
  2.  public static String parsePicturePath(Context context, Uri uri) {  
  3.   
  4.      if (null == context || uri == null)  
  5.          return null;  
  6.   
  7.      boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;  
  8.      // DocumentUri  
  9.      if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {  
  10.          // ExternalStorageDocumentsUri  
  11.          if (isExternalStorageDocumentsUri(uri)) {  
  12.              String docId = DocumentsContract.getDocumentId(uri);  
  13.              String[] splits = docId.split(":");  
  14.              String type = splits[0];  
  15.              if ("primary".equalsIgnoreCase(type)) {  
  16.                  return Environment.getExternalStorageDirectory() + File.separator + splits[1];  
  17.              }  
  18.          }  
  19.          // DownloadsDocumentsUri  
  20.          else if (isDownloadsDocumentsUri(uri)) {  
  21.              String docId = DocumentsContract.getDocumentId(uri);  
  22.              Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));  
  23.              return getDataColumn(context, contentUri, nullnull);  
  24.          }  
  25.          // MediaDocumentsUri  
  26.          else if (isMediaDocumentsUri(uri)) {  
  27.              String docId = DocumentsContract.getDocumentId(uri);  
  28.              String[] split = docId.split(":");  
  29.              String type = split[0];  
  30.              Uri contentUri = null;  
  31.              if ("image".equals(type)) {  
  32.                  contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  
  33.              } else if ("video".equals(type)) {  
  34.                  contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;  
  35.              } else if ("audio".equals(type)) {  
  36.                  contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;  
  37.              }  
  38.              String selection = "_id=?";  
  39.              String[] selectionArgs = new String[] {split[1]};  
  40.              return getDataColumn(context, contentUri, selection, selectionArgs);  
  41.          }  
  42.      }  
  43.      // MediaStore (general)  
  44.      else if ("content".equalsIgnoreCase(uri.getScheme())) {  
  45.          if (isGooglePhotosContentUri(uri))  
  46.              return uri.getLastPathSegment();  
  47.          return getDataColumn(context, uri, nullnull);  
  48.      }  
  49.      // File  
  50.      else if ("file".equalsIgnoreCase(uri.getScheme())) {  
  51.          return uri.getPath();  
  52.      }  
  53.      return null;  
  54.   
  55.  }  
整体过程是这样的:

先调用相册Intent:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);  
  2. intent.setType("image/*");  
  3. startActivityForResult(intent, RESULT_ALBUM_CROP_PATH);  
然后在接收时,找到图片路径,生成对应的URI,转给裁剪页面:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  2.     super.onActivityResult(requestCode, resultCode, data);  
  3.     if (resultCode != Activity.RESULT_OK) {  
  4.         return;  
  5.     }  
  6.     switch (requestCode) {  
  7.         case RESULT_ALBUM_CROP_PATH:{  
  8.             String picPath = parsePicturePath(MyActivity.this,data.getData());  
  9.             File file = new File(picPath);  
  10.             Uri uri = Uri.fromFile(file);  
  11.             cropImg(uri);  
  12.         }  
  13.         break;  
  14.     }  
  15. }  
然后是裁剪图片的代码:cropImg(Uri uri)
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void cropImg(Uri uri) {  
  2.     File tempFile = getTempFile();  
  3.     Intent intent = new Intent("com.android.camera.action.CROP");  
  4.     intent.setDataAndType(uri, "image/*");  
  5.     intent.putExtra("crop""true");  
  6.     intent.putExtra("aspectX"1);  
  7.     intent.putExtra("aspectY"1);  
  8.     intent.putExtra("outputX"700);  
  9.     intent.putExtra("outputY"700);  
  10.     intent.putExtra("return-data"false);  
  11.     intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));  
  12.     intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());  
  13.     intent.putExtra("noFaceDetection"true);  
  14.     startActivityForResult(intent, RESULT_CAMERA_CROP_PATH_RESULT);  
  15. }  
在裁剪图片后,将结果保存到暂存的本地图片URI中,然后在接收Result时:

将保存的本地的图片取出,设置到ImageView中,为了停止显示不出来所以将其缩小到1/2

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  2.     super.onActivityResult(requestCode, resultCode, data);  
  3.     if (resultCode != Activity.RESULT_OK) {  
  4.         return;  
  5.     }  
  6.     switch (requestCode) {  
  7.         case RESULT_CAMERA_CROP_PATH_RESULT: {  
  8.             Bundle extras = data.getExtras();  
  9.             if (extras != null) {  
  10.                 Bitmap bitmap = BitmapFactory.decodeFile(getTempFile().getAbsolutePath(), null);  
  11.                 if (bitmap != null) {  
  12.                     Bitmap smallBmp = setScaleBitmap(bitmap, 2);  
  13.                     mImageView.setImageBitmap(smallBmp);  
  14.                 }  
  15.             }  
  16.         }  
  17.         break;  
  18.     }  
  19. }  

四、从相册选择图像并显示的终极方案

在第一部分,我们讲过,当图片过大时,会出现数据传递失败的情况,因为Intent的data域最大只能传1M的数据,当数据超过1M时就会出现传递失败,上面我们有了一个方法根本返回的data找到对应的路径。这里大家是不是有什么想法了,这里知道了路径,直接从路径取图片不就好了,所以通过路径的显示选择图片的终极方案就出来了。
首先启动相册Intent:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);  
  2. intent.setType("image/*");  
  3. startActivityForResult(intent, RESULT_ALBUM_ONLY_THROUGH_URI);  
然后在接收时:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  2.     super.onActivityResult(requestCode, resultCode, data);  
  3.     if (resultCode != Activity.RESULT_OK) {  
  4.         return;  
  5.     }  
  6.     switch (requestCode) {  
  7.         case RESULT_ALBUM_ONLY_THROUGH_URI: {  
  8.             try {  
  9.                 String picPath = parsePicturePath(MyActivity.this, data.getData());  
  10.                 File file = new File(picPath);  
  11.                 Uri uri = Uri.fromFile(file);  
  12.                 Bitmap photo = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);  
  13.                 if (photo != null) {  
  14.                     Bitmap smallBmp = setScaleBitmap(photo, 2);  
  15.                     mImageView.setImageBitmap(smallBmp);  
  16.                 }  
  17.             } catch (Exception e) {  
  18.                 e.printStackTrace();  
  19.             }  
  20.         }  
  21.         break;  
  22.     }  
  23. }  
直接从data域获取图片路径,然后从路径加载出Bitmap,这就不会因为图片太大显示不出来了。

好了,这篇文章到这里就结束了,工作太忙,一直没时间写,今天总算赶完给大家了。


更正:

在《三、从相册选取并裁剪(通过Path:终极方案)》中,在接收时:

上面我是这样写的:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  2.     super.onActivityResult(requestCode, resultCode, data);  
  3.     if (resultCode != Activity.RESULT_OK) {  
  4.         return;  
  5.     }  
  6.     switch (requestCode) {  
  7.         case RESULT_CAMERA_CROP_PATH_RESULT: {  
  8.             Bundle extras = data.getExtras();  
  9.             if (extras != null) {  
  10.                 Bitmap bitmap = BitmapFactory.decodeFile(getTempFile().getAbsolutePath(), null);  
  11.                 if (bitmap != null) {  
  12.                     Bitmap smallBmp = setScaleBitmap(bitmap, 2);  
  13.                     mImageView.setImageBitmap(smallBmp);  
  14.                 }  
  15.             }  
  16.         }  
  17.         break;  
  18.     }  
  19. }  
由于在cropImg(Uri uri)中,我们设置了MediaStore.EXTRA_OUTPUT参数,即我们已经将结果转成URI,输出到tempFile中去了,所以在Intent的Data域可能就没有值了;(极少个别机型会出现),所以正确的接收方式应该为:

即直接从TempFile中获取当前保存的图片:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  2.     super.onActivityResult(requestCode, resultCode, data);  
  3.     if (resultCode != Activity.RESULT_OK) {  
  4.         return;  
  5.     }  
  6.     switch (requestCode) {  
  7.         case RESULT_CAMERA_CROP_PATH_RESULT: {  
  8.             if (getTempFile() != null) {  
  9.                 Bitmap bitmap = BitmapFactory.decodeFile(getTempFile().getAbsolutePath(), null);  
  10.                 if (bitmap != null) {  
  11.                     Bitmap smallBmp = setScaleBitmap(bitmap, 2);  
  12.                     mImageView.setImageBitmap(smallBmp);  
  13.                 }  
  14.             }  
  15.         }  
  16.         break;  
  17.     }  
  18. }  

注意,上面的更正在下面的源码中没有改过来,大家还需要在理解上基础上,自行更正。


另一篇很棒的文章:

《你需要知道的 Android 拍照适配方案》:http://diycode.cc/topics/101


源码有四个按钮:

1、相册选取——返回值:对应第一部分
2、相册选择——PATH(终极):对应第四部分
3、相册并截取——URI:对应第二部分
4、相册并截取——PATH(终极):对应第三部分


如果本文有帮到你,记得加关注哦。

源码下载地址:http://download.csdn.net/detail/harvic880925/8445281

请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/43314451,谢谢

0 0