天天看点

【移动开发】Android相机、相册获取图片显示并保存到SD卡

   做过类似需求的同学都知道,在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&lt;</code><code>String</code><code>&gt; imagesList; </code><code>//各个图片的路径</code>

<code>    </code><code>public</code> <code>ImagesListAdapter(Context context, List&lt;</code><code>String</code><code>&gt; 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,如需转载请自行联系原作者