做过类似需求的同学都知道,在Activity中通过如下代码可以启动相机,然后在重写的onActivityResult方法中可以获取到返回的照片数据:
1
2
<code>Intent openCameraIntent = </code><code>new</code> <code>Intent(MediaStore.ACTION_IMAGE_CAPTURE);</code>
<code>startActivityForResult(openCameraIntent, TAKE_PICTURE);</code>
在onActivityResult方法里通过Intent的getData方法获取的数据转换成bitmap并显示在界面上,有时候会有取不到数据,或者显示的bitmap会非常小,如果将bitmap保存到sd卡后会发现,图片的分辨率很低,并且图片大小也是经过压缩的,不管将相机的像素设置多高,最后通过这种方式返回的bitmap总是经过压缩了的。如果想获得理想的照片大小和分辨率改如何处理呢?
大家都知道,现在手机像素少则500W或800W,多则4KW(某亚),就拿常见的800W像素的相机拍出来的照片来说,分辨率大概在3200*2400左右,照片大小在2M左右。试想一下,在Android系统中bitmap占用4个字节,3200*2400*4=?,结果大家自己算算,如果为了一张图片,耗用这么大的内存,肯定是不合理的,并且,官方文档中有说明,Android系统分配给每个应用的最大内存是16M,所以,系统为了防止应用内存占用过大,对于在应用内通过相机拍摄的图片最终返回来的结果进行了压缩,压缩后的图片变得很小,通过之前说的getData的方式只能满足比如显示个头像这样的需求。
如果要显示大图,就会出现模糊的情况。那如何获取清晰的大图呢?我的解决思路如下:
1.拍照时,将拍得的照片先保存在本地,通过修改之前的代码如下:
<code>Uri imageUri = Uri.fromFile(</code><code>new</code> <code>File(Environment.getExternalStorageDirectory(),</code><code>"image.jpg"</code><code>));</code>
//指定照片保存路径(SD卡),image.jpg为一个临时文件,每次拍照后这个图片都会被替换
<code>openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);</code>
如何调取相机拍照,代码如下:
3
4
5
6
7
8
9
10
11
<code>/**拍照获取相片**/</code>
<code> </code><code>private</code> <code>void</code> <code>doTakePhoto() {</code>
<code> </code><code>Intent intent = </code><code>new</code> <code>Intent(MediaStore.ACTION_IMAGE_CAPTURE); </code><code>//调用系统相机</code>
<code> </code>
<code> </code><code>Uri imageUri = Uri.fromFile(</code><code>new</code> <code>File(Environment.getExternalStorageDirectory(),</code><code>"image.jpg"</code><code>));</code>
<code> </code><code>//指定照片保存路径(SD卡),image.jpg为一个临时文件,每次拍照后这个图片都会被替换</code>
<code> </code><code>intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);</code>
<code> </code><code>//直接使用,没有缩小</code>
<code> </code><code>startActivityForResult(intent, PHOTO_WITH_CAMERA); </code><code>//用户点击了从相机获取</code>
<code> </code><code>}</code>
2.在onActivityResult方法中再将图片取出,并经过缩小处理再显示在界面上或上传给服务器(压缩比例自定义)
12
13
14
15
16
17
18
19
20
21
<code>@Override</code>
<code> </code><code>protected</code> <code>void</code> <code>onActivityResult(</code><code>int</code> <code>requestCode, </code><code>int</code> <code>resultCode, Intent data) {</code>
<code> </code><code>super</code><code>.onActivityResult(requestCode, resultCode, data);</code>
<code> </code><code>if</code> <code>(resultCode == RESULT_OK) {</code>
<code> </code><code>switch</code> <code>(requestCode) {</code>
<code> </code><code>case</code> <code>TAKE_PICTURE:</code>
<code> </code><code>//将保存在本地的图片取出并缩小后显示在界面上</code>
<code> </code><code>Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+</code><code>"/image.jpg"</code><code>);</code>
<code> Bitmap newBitmap = zoomBitmap(bitmap, bitmap.getWidth() / SCALE, bitmap.getHeight() / SCALE);</code>
<code> </code>
<code> </code><code>//由于Bitmap内存占用较大,这里需要回收内存,否则会报out of memory异常</code>
<code> </code><code>bitmap.recycle();</code>
<code> </code><code>//将处理过的图片显示在界面上,并保存到本地</code>
<code> </code><code>iv_image.setImageBitmap(newBitmap);</code>
<code> </code><code>savePhotoToSDCard(newBitmap, Environment.getExternalStorageDirectory().getAbsolutePath(), </code><code>String</code><code>.valueOf(System.currentTimeMillis()));</code>
<code> </code><code>break</code><code>;</code>
<code> </code><code>default</code><code>:</code>
<code> </code><code>}</code>
<code> </code><code>}</code>
由于Android给bitmap分配的内存最大不超过8M,所以对使用完的较大的Bitmap要释放内存,调用其recycle()方法即可。然后将缩小后的bitmap显示在界面上或保存到SD卡,至于之前保存的原图,可以删掉,也可以放在那,下次拍照时,这张原图就会被下一张照片覆盖,所以SD卡上使用只有一张临时图片,占用也不是很大。
以上讲的是拍照获取图片,如果是从相册中获取图片又如何处理呢,我的方法如下:
1.打开相册选取图片:
<code>Intent openAlbumIntent = </code><code>new</code> <code>Intent(Intent.ACTION_GET_CONTENT);</code>
<code> </code><code>openAlbumIntent.setType(</code><code>"image/*"</code><code>);</code>
<code> </code><code>startActivityForResult(openAlbumIntent, CHOOSE_PICTURE);</code>
2.在onActivity方法中处理获取到的图片,思路和之前类似
22
23
24
25
26
27
28
29
30
31
32
<code> </code><code>case</code> <code>CHOOSE_PICTURE:</code>
<code> </code><code>ContentResolver resolver = getContentResolver();</code>
<code> </code><code>//照片的原始资源地址</code>
<code> </code><code>Uri originalUri = data.getData();</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>//使用ContentProvider通过URI获取原始图片</code>
<code> </code><code>Bitmap photo = MediaStore.Images.Media.getBitmap(resolver, originalUri);</code>
<code> </code><code>if</code> <code>(photo != </code><code>null</code><code>) {</code>
<code> </code><code>//为防止原始图片过大导致内存溢出,这里先缩小原图显示,然后释放原始Bitmap占用的内存</code>
<code> </code><code>Bitmap smallBitmap = zoomBitmap(photo, photo.getWidth() / SCALE, photo.getHeight() / SCALE);</code>
<code> </code><code>//释放原始图片占用的内存,防止out of memory异常发生</code>
<code> </code><code>photo.recycle();</code>
<code> </code>
<code> </code><code>iv_image.setImageBitmap(smallBitmap);</code>
<code> </code><code>}</code>
<code> </code><code>} </code><code>catch</code> <code>(FileNotFoundException e) {</code>
<code> </code><code>e.printStackTrace();</code>
<code> </code><code>} </code><code>catch</code> <code>(IOException e) {</code>
<code> </code><code>}</code>
<code> </code>
还有一个方法 zoomBitmap(),代码如下:
<code>/** 缩放Bitmap图片 **/</code>
<code> </code><code>public</code> <code>Bitmap zoomBitmap(Bitmap bitmap, </code><code>int</code> <code>width, </code><code>int</code> <code>height) {</code>
<code> </code><code>int</code> <code>w = bitmap.getWidth();</code>
<code> </code><code>int</code> <code>h = bitmap.getHeight();</code>
<code> </code><code>Matrix matrix = </code><code>new</code> <code>Matrix();</code>
<code> </code><code>float scaleWidth = ((float) width / w);</code>
<code> </code><code>float scaleHeight = ((float) height / h);</code>
<code> </code><code>matrix.postScale(scaleWidth, scaleHeight);</code><code>// 利用矩阵进行缩放不会造成内存溢出</code>
<code> </code><code>Bitmap newbmp = Bitmap.createBitmap(bitmap, </code><code>0</code><code>, </code><code>0</code><code>, w, h, matrix, </code><code>true</code><code>);</code>
<code> </code><code>return</code> <code>newbmp;</code>
至此,功能已实现。
下面是本人项目中所实现的功能在这里总结一下:
1.要想对从图库选择的照片进行裁剪:
<code>/**从相册获取图片**/</code>
<code> </code><code>private</code> <code>Intent doPickPhotoFromGallery() {</code>
<code> </code><code>Intent intent = </code><code>new</code> <code>Intent();</code>
<code> </code><code>intent.setType(</code><code>"image/*"</code><code>); </code><code>// 开启Pictures画面Type设定为image</code>
<code> </code><code>intent.setAction(Intent.ACTION_GET_CONTENT); </code><code>//使用Intent.ACTION_GET_CONTENT这个Action</code>
<code> </code>
<code> </code><code>//实现对图片的裁剪,必须要设置图片的属性和大小</code>
<code> </code><code>intent.setType(</code><code>"image/*"</code><code>); </code><code>//获取任意图片类型</code>
<code> </code><code>intent.putExtra(</code><code>"crop"</code><code>, </code><code>"true"</code><code>); </code><code>//滑动选中图片区域</code>
<code> </code><code>intent.putExtra(</code><code>"aspectX"</code><code>, </code><code>1</code><code>); </code><code>//裁剪框比例1:1</code>
<code> </code><code>intent.putExtra(</code><code>"aspectY"</code><code>, </code><code>1</code><code>);</code>
<code> </code><code>intent.putExtra(</code><code>"outputX"</code><code>, </code><code>300</code><code>); </code><code>//输出图片大小</code>
<code> </code><code>intent.putExtra(</code><code>"outputY"</code><code>, </code><code>300</code><code>);</code>
<code> </code><code>intent.putExtra(</code><code>"return-data"</code><code>, </code><code>true</code><code>); </code><code>//有返回值</code>
<code> </code><code>return</code> <code>intent;</code>
调用此方法处:
<code>Intent intent2 = doPickPhotoFromGallery();</code>
<code>startActivityForResult(intent2, PHOTO_WITH_DATA);</code>
ActivityForResult中和上面一样
2.项目中要拍多少张 就保存多少张,显示图片列表:
A.将拍照的照片或者图库选择的图片,保存到本地
创建图片名,不能重复哦!
<code>/** 为图片创建不同的名称用于保存,避免覆盖 **/</code>
<code>public</code> <code>static</code> <code>String</code> <code>createFileName() {</code>
<code> </code><code>String</code> <code>fileName = </code><code>""</code><code>;</code>
<code> </code><code>Date</code> <code>date = </code><code>new</code> <code>Date</code><code>(System.currentTimeMillis()); </code><code>// 系统当前时间</code>
<code> </code><code>SimpleDateFormat dateFormat = </code><code>new</code> <code>SimpleDateFormat(</code>
<code> </code><code>"'IMG'_yyyyMMdd_HHmmss"</code><code>);</code>
<code> </code><code>fileName = dateFormat.format(date) + </code><code>".jpg"</code><code>;</code>
<code> </code><code>return</code> <code>fileName;</code>
<code>}</code>
保存图片到SD卡
33
34
<code>/**Save image to the SD card**/</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>savePhotoToSDCard(</code><code>String</code> <code>path, </code><code>String</code> <code>photoName,</code>
<code> </code><code>Bitmap photoBitmap) {</code>
<code> </code><code>if</code> <code>(android.os.Environment.getExternalStorageState().equals(</code>
<code> </code><code>android.os.Environment.MEDIA_MOUNTED)) {</code>
<code> </code><code>File dir = </code><code>new</code> <code>File(path);</code>
<code> </code><code>if</code> <code>(!dir.exists()) {</code>
<code> </code><code>dir.mkdirs();</code>
<code> </code><code>File photoFile = </code><code>new</code> <code>File(path, photoName); </code><code>//在指定路径下创建文件</code>
<code> </code><code>FileOutputStream fileOutputStream = </code><code>null</code><code>;</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>fileOutputStream = </code><code>new</code> <code>FileOutputStream(photoFile);</code>
<code> </code><code>if</code> <code>(photoBitmap != </code><code>null</code><code>) {</code>
<code> </code><code>if</code> <code>(photoBitmap.compress(Bitmap.CompressFormat.PNG, </code><code>100</code><code>,</code>
<code> </code><code>fileOutputStream)) {</code>
<code> </code><code>fileOutputStream.flush();</code>
<code> </code><code>} </code><code>catch</code> <code>(FileNotFoundException e) {</code>
<code> </code><code>photoFile.</code><code>delete</code><code>();</code>
<code> </code><code>e.printStackTrace();</code>
<code> </code><code>} </code><code>catch</code> <code>(IOException e) {</code>
<code> </code><code>} </code><code>finally</code> <code>{</code>
<code> </code><code>fileOutputStream.close();</code>
B.最后就是显示图片列表,因为我们要用到listView,自然少不了Adapter了,我们将保存到SD卡上的图片名获取到集合中,在自定义的适配器中根据名字加载图片喽!
自定义图片列表适配器代码:
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<code>/**</code>
<code> </code><code>* 插入图片列表适配器</code>
<code> </code><code>* @author ZHF</code>
<code> </code><code>*</code>
<code> </code><code>*/</code>
<code>public</code> <code>class</code> <code>ImagesListAdapter </code><code>extends</code> <code>BaseAdapter {</code>
<code> </code>
<code> </code><code>private</code> <code>Context context;</code>
<code> </code><code>private</code> <code>List<</code><code>String</code><code>> imagesList; </code><code>//各个图片的路径</code>
<code> </code><code>public</code> <code>ImagesListAdapter(Context context, List<</code><code>String</code><code>> imagesList) {</code>
<code> </code><code>this</code><code>.context = context;</code>
<code> </code><code>this</code><code>.imagesList = imagesList;</code>
<code> </code><code>/**得到总的数量**/</code>
<code> </code><code>@Override</code>
<code> </code><code>public</code> <code>int</code> <code>getCount() {</code>
<code> </code><code>// TODO Auto-generated method stub</code>
<code> </code><code>return</code> <code>imagesList.size();</code>
<code> </code><code>/**根据ListView位置返回View**/</code>
<code> </code><code>public</code> <code>Object</code> <code>getItem(</code><code>int</code> <code>position) {</code>
<code> </code><code>return</code> <code>imagesList.</code><code>get</code><code>(position); </code><code>//返回当前选中的item图片的路径</code>
<code> </code><code>/**根据ListView位置得到List中的ID**/</code>
<code> </code><code>public</code> <code>long getItemId(</code><code>int</code> <code>position) {</code>
<code> </code><code>return</code> <code>position; </code><code>//返回当前选中项的Id</code>
<code> </code><code>/**根据位置得到View对象**/</code>
<code> </code><code>public</code> <code>View getView(</code><code>int</code> <code>position, View convertView, ViewGroup parent) {</code>
<code> </code>
<code> </code><code>if</code><code>(convertView == </code><code>null</code><code>) {</code>
<code> </code><code>convertView = LayoutInflater.from(context).inflate(R.layout.newwrite_image_item, </code><code>null</code><code>);</code>
<code> </code><code>ImageView img = (ImageView) convertView.findViewById(R.id.newwrite_et_content_image); </code><code>//图片列表项</code>
<code> </code><code>if</code><code>(!imagesList.</code><code>get</code><code>(position).equals(</code><code>""</code><code>)) {</code><code>//没有图片</code>
<code> </code><code>Bitmap tempBitmap = BitmapFactory.decodeFile(imagesList.</code><code>get</code><code>(position));</code><code>//根据路径显示对应的图片</code>
<code> </code><code>Bitmap newBitmap = </code><code>new</code> <code>ImageManager(context).zoomBitmap(tempBitmap, tempBitmap.getWidth(), tempBitmap.getHeight() / </code><code>3</code><code>);</code>
<code> </code><code>img.setImageBitmap(newBitmap);</code><code>//对应的行上显示对应的图片</code>
<code> </code><code>return</code> <code>convertView;</code>
ok!完了,在显示图片的时候大家可能会碰到OOM(OutOfMemory)异常,在下一篇博客中我会具体解决了一下~
Demo已上传!下载附件即可!
本文转自zhf651555765 51CTO博客,原文链接:http://blog.51cto.com/smallwoniu/1248695,如需转载请自行联系原作者