天天看點

【移動開發】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,如需轉載請自行聯系原作者