Android 拍照或选择图片并剪裁

t817in56 4年前
   <p>在Android平常开发中拍照、选择图片并裁剪几乎是每个App所必须的功能,因为不同版本Android选择图片后返回处理方式不同,再加上不同品牌手机对Android系统的深度定制,导致App在使用Android原生图片处理上或多或少出现一些问题,像微信、QQ这种用户受众范围广对平台兼容性高的App它们都有一套自定义图片选择器,今天这篇文章主要整理一下Android使用原生控件拍照、选择图片以及拍照后裁剪或者选择图片后裁剪相关功能。</p>    <h3><strong>选择图片</strong></h3>    <p>在Android4.4之前一般选择图片都是使用下面方式,然后我们通过onActivityResult()方法通过Intent获得选择图片的Uri,使用ContentResolver拿到图片的绝对路径就可以了。</p>    <pre>  <code class="language-java">Intentintent = new Intent();  intent.setAction(Intent.ACTION_GET_CONTENT);  intent.addCategory(Intent.CATEGORY_OPENABLE);  intent.setType("image/*");  startActivityForResult(intent, IMAGE_SELECT);  </code></pre>    <p>但是在Android4.4以及更高的版本部分手机上面选择图片会拿不到正确的Uri,多数手机我们拿到的一个Uri如下:</p>    <p>content://media/external/images/media/258779</p>    <p>在部分Android4.4以上的手机我们拿到的Uri是如下格式:</p>    <p>content://com.android.providers.media.documents/document/image%3A256478</p>    <p>这种格式我们通过ContentResolver就拿不到到媒体数据库中图片的绝对路径了,在Android的4.4及其以上可以使用如下方式:</p>    <pre>  <code class="language-java">Intentintent = new Intent(Intent.ACTION_PICK,   android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);  intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");  startActivityForResult(intent, IMAGE_SELECT);  </code></pre>    <p>经过验证在低于4.4的版本上面也可以正确获取到图片的Uri。</p>    <p>拿到图片的绝对路径如何显示图片在本篇博客中就不介绍了,可以使用第三方库,也可以自己使用BitmapFactory.Options来加载图片防止OOM。</p>    <h3><strong>选择图片并裁剪</strong></h3>    <p>在Android原生应用中裁剪图片事实上就是使用自带应用Media Gallery裁剪的,通过Intent来带回我们想要的数据的。下面是使用Intent剪裁图片时可以携带的属性:</p>    <table>     <tbody>      <tr>       <th>附加选项</th>       <th>数据类型</th>       <th>描述</th>      </tr>      <tr>       <td>crop</td>       <td>String</td>       <td>发送裁剪信号</td>      </tr>      <tr>       <td>aspectX</td>       <td>int</td>       <td>X方向上的比例</td>      </tr>      <tr>       <td>aspectY</td>       <td>int</td>       <td>Y方向上的比例</td>      </tr>      <tr>       <td>outputX</td>       <td>int</td>       <td>裁剪区的宽</td>      </tr>      <tr>       <td>outputY</td>       <td>int</td>       <td>裁剪区的高</td>      </tr>      <tr>       <td>scale</td>       <td>boolean</td>       <td>是否保留比例</td>      </tr>      <tr>       <td>return-data</td>       <td>boolean</td>       <td>是否将数据保留在Bitmap中返回</td>      </tr>      <tr>       <td>data</td>       <td>Parcelable</td>       <td>相应的Bitmap数据</td>      </tr>      <tr>       <td>circleCrop</td>       <td>String</td>       <td>圆形裁剪区域?</td>      </tr>      <tr>       <td>noFaceDetection</td>       <td>boolean</td>       <td>是否人脸识别?</td>      </tr>      <tr>       <td>MediaStore.EXTRA_OUTPUT</td>       <td>Uri</td>       <td>将URI指向相应的file:///…,详见代码示例</td>      </tr>     </tbody>    </table>    <p>为了得到我们需要的数据有两个选项是需要重点关注的return-data和MediaStore.EXTRA_OUTPUT,这两个选项是互不影响的,一般情况下如果我们需要返回的图片很小可以直接使用return-data返回的数据,return-data将会直接返回一个Bitmap。但是如果要求的图片很大此时我们要使用MediaStore.EXTRA_OUTPUT,该属性是将图片输入一个指定的Uri,如果对Uri了解的话,应该知道Uri与File相似,裁剪后将数据就保存在了指定Uri所指向的目标文件中。</p>    <p>有</p>    <p>些文章中建议说如果是小图就使用return-data,大图就使用MediaStore.EXTRA_OUTPUT返回一个指定Uri,但是多数开发中我们都要同服务器交互,将修改后的图片上传至服务器端,所以个人认为不管什么情况下我们直接返回一个Uri最为方面,上传图片时可以根据Uri通过ContentResolver获取图片路径,或者直接创建一个文件将裁剪后的数据直接输入到指定文件中,这种情况下我们可以直接知道自己创建文件的路径。</p>    <p><strong>裁剪后直接获取图片</strong></p>    <pre>  <code class="language-java">Intentintent = new Intent(Intent.ACTION_GET_CONTENT, null);  intent.setType("image/*");  intent.putExtra("crop", "true");  intent.putExtra("aspectX", 2);  intent.putExtra("aspectY", 2);  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, IMAGE_CROP);  </code></pre>    <p>在onActivityResult方法中获取返回的Bitmap并显示方式如下:</p>    <pre>  <code class="language-java">Bitmapbitmap = data.getParcelableExtra("data");  imageView.setImageBitmap(bitmap);  </code></pre>    <p><strong>裁剪后返回Uri</strong></p>    <p>裁剪后将图片放入指定Uri的文件路径,我们可以先创建一个文件,然后将裁剪后的数据输入该文件:</p>    <pre>  <code class="language-java">private FilecreateImageFile() throws IOException {   String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")   .format(new Date());   String imageFileName = "JPEG_" + timeStamp + "_";   Fileimage = File.createTempFile(imageFileName, ".jpg",   Environment.getExternalStorageDirectory());   return image;  }  </code></pre>    <p>然后我们裁剪图片时通过MediaStore.EXTRA_OUTPUT将数据存入指定Uri。</p>    <pre>  <code class="language-java">FilephotoFile = createImageFile();  path=photoFile.getAbsolutePath();  Intentintent = new Intent(Intent.ACTION_GET_CONTENT, null);  intent.setType("image/*");  intent.putExtra("crop", "true");  intent.putExtra("aspectX", 2);  intent.putExtra("aspectY", 2);  intent.putExtra("outputX", 200);  intent.putExtra("outputY", 200);  intent.putExtra("scale", true);  intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));  intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());  intent.putExtra("noFaceDetection", true); // no face detection  startActivityForResult(intent, IMAGE_CROP);  </code></pre>    <p>这里我们有一个全局的变量用于存放自己裁剪文件的绝对路径,拿到图片绝对路径如何显示图片就很容易了。</p>    <pre>  <code class="language-java">Bitmapbm=BitmapFactory.decodeFile(path);  imageView.setImageBitmap(bm);  </code></pre>    <h3><strong>拍照后显示图片</strong></h3>    <p>在应用中我们要上传图片一般都会有拍照或者从相册选择两个选项,调用相机并返回拍照后的图片也是每个Android开发者所必须掌握的。但是我们发现现在的手机拍照后都特别大,一个普通的手机分别率是720*1280,但是拍出的照片竟然达到了3264*2448,文件大小接近3M。但是如果读入内存又有多大呢?若Bitmap类型是ARGB_8888,也就意味着一个像素点占用4个字节的内存,简单计算一下:3264*2448*4=30.48M,也就是说拍照后我们查看一下图片就会占用系统30M内存,但是在更早版本的Android手机中系统为每一个应用分配使用的最大内存才有16M,拍个照就会直接导致OOM。但是事实上我们拍照并不会引起OOM,这是因为Android系统会默认返回给我们一张缩略图,每次拍过照后系统会自动生成一张缩略图,而且还提供了一个类android.media.ThumbnailUtils,该类专门用于获取系统中的视频或图片文件的缩略图。</p>    <p><strong>拍照获取缩略图</strong></p>    <pre>  <code class="language-java">IntenttakePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);  startActivityForResult(takePictureIntent, CODE);  </code></pre>    <p>在onActivityResult方法中获取返回的Bitmap并显示方式如下:</p>    <pre>  <code class="language-java">protected void onActivityResult(int requestCode, int resultCode, Intentdata) {   if (resultCode == RESULT_OK&&requestCode==CODE) {   Bundleextras = data.getExtras();   Bitmapbm = (Bitmap) extras.get("data");  //height为175   imageView.setImageBitmap(bm);   }  }  </code></pre>    <p><strong>拍照后存入指定路径</strong></p>    <p>与上面图片裁剪实现方式类似,我们可以预先新建一个文件,然后使用MediaStore.EXTRA_OUTPUT将图片存入该路径。使用该种方式我们会发现 在onActivityResult方法中获取不到Intent了,Intent直接返回null。</p>    <pre>  <code class="language-java">IntenttakePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE,   null);  FilephotoFile = createImageFile();  if (photoFile != null) {   takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,   Uri.fromFile(photoFile));  }  startActivityForResult(takePictureIntent, CODE);  </code></pre>    <h3><strong>参考资料</strong></h3>    <p><a href="/misc/goto?guid=4959725634943703813" rel="nofollow,noindex">Android 拍照或从相册取图片并裁剪</a></p>    <p><a href="/misc/goto?guid=4959725635037001992" rel="nofollow,noindex">如何使用Android MediaStore裁剪大图片</a></p>    <p><a href="/misc/goto?guid=4959725635123439857" rel="nofollow,noindex">Android_照相机Camera_调用系统照相机返回data为空</a></p>    <p> </p>    <p>来自:http://www.sunnyang.com/594.html</p>    <p> </p>