Android 拍照或选择图片并剪裁
t817in56
8年前
<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>