1. 程式人生 > >Android 呼叫系統相機拍照並且顯示在相簿中,以及中間可能會遇到的一些問題的解決

Android 呼叫系統相機拍照並且顯示在相簿中,以及中間可能會遇到的一些問題的解決

主要思路是在使用照相機拍照,然後為拍得的照片在SD卡新開一個儲存照片的檔案

程式碼:因為要呼叫照相機和SD卡所以需要新增以下許可權:

在manifests中新增
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission 
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

activity_main.xml中

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
> <Button android:id="@+id/button" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="點選按鈕拍照" /> <ImageView android:id="@+id/imageView" android:layout_below="@id/button" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>

MainActivity中:

package com.example.administrator.myapplication1;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.io.IOException;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.os.Environment;
import java.io.FileOutputStream;
import java.io.File;
import java.io.File;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import android.app.Activity;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.MediaStore;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ImageView;
import android.os.Environment;
public class MainActivity extends AppCompatActivity {

    private Button mButton;
        private ImageView mImageView;//用於顯示照片
private File mPhotoFile;
         private String mPhotoPath;
         public final static int CAMERA_RESULT = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new ButtonOnClickListener());
mImageView = (ImageView) findViewById(R.id.imageView);
}

                 private class ButtonOnClickListener implements View.OnClickListener {
                 public void onClick(View v) {
                         try {
                                 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");//開始拍照
mPhotoPath = getSDPath()+"/"+ getPhotoFileName();//設定圖片檔案路徑,getSDPath()和getPhotoFileName()具體實現在下面mPhotoFile = new File(mPhotoPath);
                                if (!mPhotoFile.exists()) {
                                        mPhotoFile.createNewFile();//建立新檔案
}
                                 intent.putExtra(MediaStore.EXTRA_OUTPUT,//Intent有了圖片的資訊
Uri.fromFile(mPhotoFile));
        // 傳送廣播通知相簿更新資料。顯示所拍攝的照片
    Context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(mPhotoFile)));

  startActivityForResult(intent, CAMERA_RESULT);//跳轉介面傳回拍照所得資料} catch (Exception e) {
}
}
}
public String getSDPath(){
File sdDir = null;
boolean
sdCardExist = Environment.getExternalStorageState() .equals(android.os.Environment.MEDIA_MOUNTED); //判斷sd卡是否存在 if (sdCardExist){ sdDir = Environment.getExternalStorageDirectory();//獲取跟目錄}
return
sdDir.toString();}
private String getPhotoFileName() {
Date date = new Date(System.currentTimeMillis());SimpleDateFormat dateFormat = new SimpleDateFormat( "'IMG'_yyyyMMdd_HHmmss");
return
dateFormat.format(date) +".jpg
 
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if
(requestCode == CAMERA_RESULT) {
Bitmap bitmap = BitmapFactory.decodeFile(mPhotoPath, null);mImageView.setImageBitmap(bitmap);}
}
}

下面對獲取SD卡路徑進行補充,有兩種情況,我用了第二種

用第二種需要加一個包

import android.os.Environment;

獲取sd卡路徑 
方法一: private String folder = "/sdcard/DCIM/Camera/"(SD卡上拍照程式的圖片儲存路徑); //寫死絕對路徑,不贊成使用

方法二: 
public String getSDPath(){ 
       File sdDir = null; 
       boolean sdCardExist = Environment.getExternalStorageState()   
                           .equals(Android.os.Environment.MEDIA_MOUNTED);   //判斷sd卡是否存在 
       if   (sdCardExist)   
       {                               
         sdDir = Environment.getExternalStorageDirectory();//獲取跟目錄 
      }   
       return sdDir.toString(); 
       
}

然後:在後面加上斜槓,在加上檔名 
String fileName = getSDPath() +"/" + name;//以name存在目錄中

原因分析

於是我查看了Android系統框架Camera應用程式,找到了關於系統照相機如何處理返回值data問題!

預設情況下,即不需要指定intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);照相機有自己預設的儲存路徑,拍攝的照片將返回一個縮圖。如果想訪問原始圖片,可以通過dat extra能夠得到原始圖片位置。即,如果指定了目標uri,data就沒有資料,如果沒有指定uri,則data就返回有資料!現在想想,這種設計還是很合理的!

  1. @Override
  2. protectedvoid onActivityResult(int requestCode, int resultCode, Intent data) {  
  3.     switch (requestCode) {  
  4.     case REQUEST_CODE_CAMERA:  
  5.         if (resultCode == RESULT_OK) {  
  6.             if(data !=null){ //可能尚未指定intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
  7.                 //返回有縮圖
  8.                 if(data.hasExtra("data")){  
  9.                     Bitmap thumbnail = data.getParcelableExtra("data");  
  10.                     //得到bitmap後的操作
  11.                 }  
  12.             }else{  
  13.                 //由於指定了目標uri,儲存在目標uri,intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
  14.                 // 通過目標uri,找到圖片
  15.                 // 對圖片的縮放處理
  16.                 // 操作
  17.             }  
  18.         }  
  19.     }  
  20. }  

Android系統照相機部分關鍵原始碼

  1. // First handle the no crop case -- just return the value.  If the
  2. // caller specifies a "save uri" then write the data to it's
  3. // stream. Otherwise, pass back a scaled down version of the bitmap
  4. // directly in the extras.
  5. if (mSaveUri != null) { //存在mSaveUri,即指定了目標uri
  6.     OutputStream outputStream = null;  
  7.     try {  
  8.         outputStream = mContentResolver.openOutputStream(mSaveUri);  
  9.         outputStream.write(data);  
  10.         outputStream.close();  
  11.         setResult(RESULT_OK);   //直接返回RESULT_OK,並沒有指定intent
  12.         finish();  
  13.     } catch (IOException ex) {  
  14.         // ignore exception
  15.     } finally {  
  16.         Util.closeSilently(outputStream);  
  17.     }  
  18. else {  
  19.     Bitmap bitmap = createCaptureBitmap(data);  
  20.     // 返回RESULT_OK,幷包含一個Intent物件,其中Extra中科key為data,value為一個bitmap
  21.     setResult(RESULT_OK, new Intent("inline-data").putExtra("data", bitmap));  
  22.     finish();  
  23. }  

常見問題及解決辦法

如果我們設定了照片的儲存路徑,那麼很可能會遇到一下三種問題:
問題一:onActivityResult 方法中的data 返回為空(資料表明,93%的機型的data 將會是Null,所以如果我們指定了路徑,就不要使用data 來獲取照片,起碼在使用前要做空判斷)
問題二:照片無法儲存,如果自定義儲存路徑是/mnt/sdcard/lowry/,而手機SD 卡下在拍照前沒有名為lowry 的資料夾,那麼部分手機拍照後圖片不會儲存,導致我們無法獲得照片,大多數手機的相機遇到資料夾不存在的情況都會自己創建出不存在的資料夾,而個別手機卻不會建立,其代表機型為:三星I8258、華為H30-T00、紅米等。解決的方法就是在指定儲存路徑前先判斷路徑中的資料夾是否都存在,不存在先建立再呼叫相機。
問題三:照片可以儲存,但是名字不對
file:///mnt/sdcard/123 1.jpg,由於Uri 的fromFile 方法會將路徑中的空格用“%20”取代。其實對於大多數的手機這都不算事,手機在解析儲存路徑的時候都會將“%20”替換為空格,這樣實際上最終的照片名字還是我們當初指定的名字:123 1.jpg,遺憾的是個別手機(如酷派7260)系統自帶的相機沒有將“%20”讀成空格,拍照後的照片的名字是123%201.jpg,我們用路徑“file:///mnt/sdcard/123 1.jpg”能找到照片才怪!!


解決辦法:

(1)使用 onActivityResult 中的 intent(data)前要做空判斷。
(2)指定拍照路徑時,先檢查路徑中的資料夾是否都存在,不存在時先建立資料夾再呼叫相機拍照。
(3)指定拍照儲存路徑時,照片的命名中不要包含空格等特殊符號。

參考博文連結:http://blog.csdn.net/zimo2013/article/details/16916279;http://blog.csdn.net/a751608624/article/details/50728336;http://blog.csdn.net/llxlqy/article/details/51280564;http://www.cnblogs.com/dongweiq/p/6478393.html