2012-07-26

Android camera應用(二) - 使用Android內部照相機功能

上一篇我們使用了呼叫其它一樣有照相功能的應用程式幫我們完成拍照這種比較簡單、

也較方便的用法,此篇我們來用比較原始的方法,使用的是Android內部的拍照功能

相對也比較複雜一點,使用的時機是客製化比較重而呼叫其他拍照應用程式也無法完成的


這時就必須用到原始的方法,直接用內部拍照預覽功能了,開始為您介紹

使用之前,因為調用照相功能有權限問題,所以必須在AndroidManifest.xml內加入照相權限


及相關我們需要用到的自動對焦功能


//拍照權限

<uses-permission android:name="android.permission.CAMERA" />     
//自動對焦      
 <uses-feature android:name="android.hardware.camera" />                
 <uses-feature android:name="android.hardware.camera.autofocus" />
//螢幕轉為橫向顯示,因為預設為橫向拍照,如果不設計成橫向
//預覽畫面和拍出來的照片會自動轉90度
android:screenOrientation="landscape"



AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jim.camera"
    android:versionCode="1"
    android:versionName="1.0" android:installLocation="auto">

    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8"/>
 <uses-permission android:name="android.permission.CAMERA" />   
 <uses-feature android:name="android.hardware.camera" />   
 <uses-feature android:name="android.hardware.camera.autofocus" />  
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".CameraActivity"
            android:label="@string/app_name"
            android:screenOrientation="landscape" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:weightSum="1">


 <LinearLayout android:layout_height="wrap_content" android:id="@+id/linearLayout1" android:layout_width="fill_parent">

  <SurfaceView
      android:id="@+id/surfaceView1"
      android:layout_width="250dp"
      android:layout_height="250dp" />

  <Button android:text="拍照" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>




  <ImageView
      android:id="@+id/imageView1"
      android:layout_width="250dp"
      android:layout_height="250dp" />

 </LinearLayout>
</LinearLayout>

CameraActivity.java
package jim.camera;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class CameraActivity extends Activity implements SurfaceHolder.Callback{
 SurfaceHolder surfaceHolder;
 SurfaceView surfaceView1;
 Button button1;
 ImageView imageView1;
 Camera camera;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        button1=(Button)findViewById(R.id.button1);
        //在AndroidManifest.xml中設定或是用下面的setRequestedOrientation(0)設定也可以
        //0代表橫向、1代表縱向
        setRequestedOrientation(0);
        //設為横向顯示。因為攝影頭會自動翻轉90度,所以如果不横向顯示,看到的畫面就是翻轉的。
         
        surfaceView1=(SurfaceView)findViewById(R.id.surfaceView1);
        imageView1=(ImageView)findViewById(R.id.imageView1);
        surfaceHolder=surfaceView1.getHolder();
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        surfaceHolder.addCallback(this);
        button1.setOnClickListener(new OnClickListener(){
 
   public void onClick(View v) {
    
    //自動對焦
    camera.autoFocus(afcb);
    
   }});
 
    }
    PictureCallback jpeg =new PictureCallback(){
 
  public void onPictureTaken(byte[] data, Camera camera) {
   
   Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);
   //byte數组轉換成Bitmap
   imageView1.setImageBitmap(bmp);
   //拍下圖片顯示在下面的ImageView裡
   FileOutputStream fop;
   try {
    fop=new FileOutputStream("/sdcard/dd.jpg");
    //實例化FileOutputStream,參數是生成路徑
    bmp.compress(Bitmap.CompressFormat.JPEG, 100, fop);
    //壓缩bitmap寫進outputStream 參數:輸出格式  輸出質量  目標OutputStream
    //格式可以為jpg,png,jpg不能存儲透明
    fop.close();
    System.out.println("拍照成功");
    //關閉流
   } catch (FileNotFoundException e) {
    
    e.printStackTrace();
    System.out.println("FileNotFoundException");
 
   } catch (IOException e) {
    
    e.printStackTrace();
    System.out.println("IOException");
   }
   camera.startPreview();
   //需要手動重新startPreview,否則停在拍下的瞬間
  }
 
    };
 
 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  
 
 }
 
 public void surfaceCreated(SurfaceHolder holder) {
  
  camera=Camera.open();
  try {
 
   Camera.Parameters parameters=camera.getParameters();
   parameters.setPictureFormat(PixelFormat.JPEG);
   parameters.setPreviewSize(320, 220);
   camera.setParameters(parameters);
   //設置參數
   camera.setPreviewDisplay(surfaceHolder);
   //鏡頭的方向和手機相差90度,所以要轉向
   //camera.setDisplayOrientation(90);
   //攝影頭畫面顯示在Surface上
   camera.startPreview();
  } catch (IOException e) {
   
   e.printStackTrace();
  }
 
 }
 
 public void surfaceDestroyed(SurfaceHolder holder) {
  
  System.out.println("surfaceDestroyed");
  camera.stopPreview();
  //關閉預覽
  camera.release();
  // 
 }
 
 //自動對焦監聽式
 AutoFocusCallback afcb= new AutoFocusCallback(){
 
  public void onAutoFocus(boolean success, Camera camera) {
   
   if(success){
    //對焦成功才拍照
    camera.takePicture(null, null, jpeg);
   }
  }
 
 
 };
}

執行後畫面



說明:

         1.Android的照相功能預計是橫向拍照,所以如果您是用直立的方式拍照

您就需要把預覽畫面轉向90度在surfaceCreated方法中加入camera.setDisplayOrientation(90);

預覽畫面就會轉90度,再來把得到的照片也要轉90度,不然得到的照片還是會是轉了90度

這方面可以自行實驗一下就會大概知道它的原理了。

public class CameraActivity extends Activity implements SurfaceHolder.Callback

        2.相機功能除了繼承Activity之外,也需要實現SurfaceHolder.Callback這個預覽界面接口

用來接收攝影鏡頭預覽界面變化的訊息,它有三個方法:

(1)surfaceCreated:當預覽界面創建時調用此方法

(2)surfaceChanged:當預覽界面的格式和大小改變時調用此方法

(3)surfaceDestroyed:當預覽界面被關閉時調用此方法

         3.照相的流程大概是:

(1)在佈局檔main.xml中會有一個SurfaceView

(2)加入surfaceHolder以控制隨時在變動的SurfaceView


(3)創始應用程式時觸發surfaceCreated,開啟攝影鏡頭及設定相關參數(相片格式及尺寸)
    攝影頭畫面顯示在 SurfaceView 


(4)Button內的onClick事件中使用自動對焦camera.autoFocus(afcb);


(5)自動對焦監聽式AutoFocusCallback afcb= new AutoFocusCallback()
    對焦成功才會調用camera.takePicture(null, null, jpeg);拍照

(6)得到的照片進入PictureCallback jpeg =new PictureCallback()處理

(7)完成關閉程式時會觸發surfaceDestroyed關閉預覽及釋放資源


後記:

          本人在使用上是比較習慣直接用Intent呼叫其它拍照服務的方法在做


除非呼叫的方法不能達到我想要的目的,我才會想要用這個方法做,不然這個方法


是比較麻煩的,因為看程式碼和說明也知道,比上一篇複雜上很多倍,以上就是為


您介紹使用內部照相功能,有問題請留言或來信告知,感謝。

4 則留言:

  1. 我弄下來測試發現,沒錯誤但不能執行
    方便給我原始檔案嗎?

    回覆刪除
  2. 我也不能執行 能給原始檔嗎?

    回覆刪除
  3. 這是有縮圖的作法, 如何做到原圖儲存?

    回覆刪除
  4. 移除parameters.setPreviewSize(320, 220); 就可以執行了

    回覆刪除

您的寶貴建議是我前進的動力!