Android 相機開發詳解

來源:互聯網
上載者:User

標籤:

在android中應用相機功能,一般有兩種:一種是直接調用系統相機,一種自己寫的相機。
我將分別示範兩種方式的使用:
第一種:是使用Intent跳轉到系統相機,action為:android.media.action.STILL_IMAGE_CAMERA
關鍵代碼:

Intent intent = new Intent(); //調用照相機  intent.setAction("android.media.action.STILL_IMAGE_CAMERA");   startActivity(intent); 

 想要測試的,可以直接建立一個項目,並且把主activity的代碼換成上面的,然後運行,我測試了一下,上面這個代碼並不需要許可權,畢竟只是調用系統內建的程式。當然網上還有一些其他相關的調用方法,只要設定對了action,那麼系統就會調用系統內建的相機.

第二種:

(1)首先我們要自己建立一個照相,必須考慮用什麼控制項顯示照相機中的預覽效果,顯然android已經幫我們做好了選擇,那就是
SurfaceView,而控制SurfaceView則需要一個surfaceHolder,他是系統提供的一個用來設定surfaceView的一個對象,而它通過surfaceView.getHolder()這個方法來獲得。而Camera提供一個setPreviewDisplay(SurfaceHolder)的方法來串連
surfaceHolder,並通過他來控制surfaceView,而我們則使用android的Camera類提供了startPreview()和stopPreview()來開啟和關閉預覽.
關係如下:
Camera -- -->SurfaceHolder------>SurfaceView.


(2)知道怎麼預覽,當然也要知道怎麼開啟相機.Camera.open()這是個靜態方法,如果相機沒有別人用著,則會返回一個 相機引用,如果被人用著,則會拋出異常。很奇怪的是,這個方法,不能隨便放,如放在構造方法或者onCreate()方法中,都會照成沒有預覽效果.


(3)
SurfaceHolder.Callback,這是個holder用來顯示surfaceView 資料的介面,他分別必須實現3個方法
surfaceCreated()這個方法是surface 被建立後調用的
surfaceChanged()這個方法是當surfaceView發生改變後調用的
surfaceDestroyed()這個是當surfaceView銷毀時調用的.
surfaceHolde通過addCallBack()方法將響應的介面綁定到他身上.
surfaceHolder還必須設定一個setType()方法,查看api的時候,發現這個方法已經過時,但是沒有寫,又會報錯。。各種奇怪。


(4)
我用以上知識寫了一個MySurfaceView類,他繼承於SurfaceView,並在裡面實現了照相機的預覽功能.這個我覺得最簡單的照相機預覽代碼:
MySurfaceView.java

import java.io.IOException;  public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{       SurfaceHolder holder;       Camera myCamera;      public MySurfaceView(Context context)       {           super(context);           holder = getHolder();//獲得surfaceHolder引用           holder.addCallback(this);           holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//設定類型        }       @Override       public void surfaceChanged(SurfaceHolder holder, int format, int width,  int height) {           myCamera.startPreview();              }       @Override       public void surfaceCreated(SurfaceHolder holder) {           // TODO Auto-generated method stub           if(myCamera == null)           {               myCamera = Camera.open();//開啟相機,不能放在建構函式中,不然不會顯示畫面.               try {                   myCamera.setPreviewDisplay(holder);               } catch (IOException e) {                   // TODO Auto-generated catch block                   e.printStackTrace();               }           }              }       @Override       public void surfaceDestroyed(SurfaceHolder holder) {           // TODO Auto-generated method stub           myCamera.stopPreview();//停止預覽           myCamera.release();//釋放相機資源           myCamera = null;           Log.d("ddd", "4");        }   }

  而且必須給應用添加許可權: <uses-permission android:name="android.permission.CAMERA"></uses-permission>

(5)能夠預覽了,接下來就是拍照了,拍照用到了一個camera.tackPiture()這個方法,這個方法,有三個參數分別是
ShutterCallBack shutter,PictureCallBack raw,PictureCallBack jpeg.
下面是對他們的實現

private ShutterCallback shutter = new ShutterCallback() {              @Override       public void onShutter() {           // TODO Auto-generated method stub          Log.d("ddd", "shutter");               }   };   private PictureCallback raw = new PictureCallback() {              @Override       public void onPictureTaken(byte[] data, Camera camera) {           // TODO Auto-generated method stub           Log.d("ddd", "raw");                }  };   private PictureCallback jpeg = new PictureCallback() {      @Override      public void onPictureTaken(byte[] data, Camera camera) {           // TODO Auto-generated method stub          Log.d("ddd","jpeg");       }  };  

  當開始拍照時,會依次調用shutter的onShutter()方法,raw的onPictureTaken方法,jpeg的onPictureTaken方法.
三個參數的作用是shutter--拍照瞬間調用,raw--獲得沒有壓縮過的圖片資料,jpeg---返回jpeg的圖片資料
當你不需要對照片進行處理,可以直接用null代替.
注意,當調用camera.takePiture方法後,camera關閉了預覽,這時需要調用startPreview()來重新開啟預覽。

我用以上知識,加到上面的那個例子,就形成了下面的代碼:

package com.wjh.camera;   import java.io.IOException;  import android.content.Context;   .... public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{       SurfaceHolder holder;      Camera myCamera;       private ShutterCallback shutter = new ShutterCallback() {         @Override          public void onShutter() {               // TODO Auto-generated method stub               Log.d("ddd", "shutter");           }      };       private PictureCallback raw = new PictureCallback() {          @Override           public void onPictureTaken(byte[] data, Camera camera) {               // TODO Auto-generated method stub               Log.d("ddd", "raw");                         }      };       private PictureCallback jpeg = new PictureCallback() {                      @Override           public void onPictureTaken(byte[] data, Camera camera) {               // TODO Auto-generated method stub               Log.d("ddd","jpeg");                          }       };       public MySurfaceView(Context context)      {           super(context);           holder = getHolder();//獲得surfaceHolder引用           holder.addCallback(this);           holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//設定類型        }       public void tackPicture()      {           myCamera.takePicture(null,null,null);       }       public void voerTack()      {           myCamera.startPreview();       }      @Override       public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {           myCamera.startPreview();              }       @Override       public void surfaceCreated(SurfaceHolder holder) {           // TODO Auto-generated method stub           if(myCamera == null)         {               myCamera = Camera.open();//開啟相機,不能放在建構函式中,不然不會顯示畫面.               try {                   myCamera.setPreviewDisplay(holder);               } catch (IOException e) {                   // TODO Auto-generated catch block                   e.printStackTrace();              }           }              }       @Override       public void surfaceDestroyed(SurfaceHolder holder) {           // TODO Auto-generated method stub           myCamera.stopPreview();//停止預覽           myCamera.release();//釋放相機資源          myCamera = null;       }   }

  CameraTest_3.java

import android.app.Activity;  ....import android.view.View.OnClickListener;   public class CameraTest_3 extends Activity implements OnClickListener  {       /** Called when the activity is first created. */       MySurfaceView mySurface;      boolean isClicked = false;       @Override       public void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           mySurface = new MySurfaceView(this);          setContentView(mySurface);          mySurface.setOnClickListener(this);       }       @Override      public void onClick(View v) {           // TODO Auto-generated method stub           if(!isClicked)           {          mySurface.tackPicture();           isClicked = true;           }else           {               mySurface.voerTack();              isClicked = false;          }            }  

  這樣就是實現了拍照的功能,那麼怎樣要圖片儲存呢?那麼這是就需要在那個參數中的jpeg的
方法裡面進行處理了,那個方法的data參數,就是相片的資料。
我們通過BitmapFactory.decodeByteArray(data, 0, data.length)來獲得圖片並通過io處理,將圖片儲存到想要儲存的位置
下面這段代碼,是將照片儲存到/sdcard/wjh.jpg;並把一些沒有用到的代碼全部刪掉,剩下一些必須的代碼

package com.wjh.camera;   import java.io.BufferedInputStream;   import java.io.BufferedOutputStream;   ..... public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{       SurfaceHolder holder;       Camera myCamera;       private PictureCallback jpeg = new PictureCallback() {        @Override           public void onPictureTaken(byte[] data, Camera camera) {               // TODO Auto-generated method stub              try               {                   Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);                   File file = new File("/sdcard/wjh.jpg");                   BufferedOutputStream bos  = new BufferedOutputStream(new FileOutputStream(file));                   bm.compress(Bitmap.CompressFormat.JPEG,100,bos);                   bos.flush();                   bos.close();              }catch(Exception e)               {                   e.printStackTrace();               }           }       };       public MySurfaceView(Context context)      {           super(context);           holder = getHolder();//獲得surfaceHolder引用          holder.addCallback(this);           holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//設定類型        }       public void tackPicture()       {           myCamera.takePicture(null,null,jpeg);       }      public void voerTack()      {           myCamera.startPreview();     }      @Override       public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {           myCamera.startPreview();             }       @Override       public void surfaceCreated(SurfaceHolder holder) {           // TODO Auto-generated method stub           if(myCamera == null)           {              myCamera = Camera.open();//開啟相機,不能放在建構函式中,不然不會顯示畫面.               try {                   myCamera.setPreviewDisplay(holder);              } catch (IOException e) {                   // TODO Auto-generated catch block                   e.printStackTrace();               }           }              }       @Override       public void surfaceDestroyed(SurfaceHolder holder) {           // TODO Auto-generated method stub           myCamera.stopPreview();//停止預覽           myCamera.release();//釋放相機資源           myCamera = null;       }  }  

  CameraTest_3.java跟上面的一樣
注意,這是必須添加在sd卡上寫資料的許可權
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

(7)能夠拍照了,這下子要考慮如何讓圖片更好看了,這顯然是專業人士的強項,但是我們在程式上,也可以做一些處理,
向上面的那些,因為我直接把surfaceView當做整體布局,就可能出現螢幕被拉開了,不是很好看,所以這時,就可以不要把
surfaceView弄成整體布局,把他弄到到一個布局管理器,在設定相關的參數.

這是需要注意的是有些參數不能隨便亂設,
如以下代碼:Camera.Parameters parames = myCamera.getParameters();//獲得參數對象
parames.setPictureFormat(PixelFormat.JPEG);//設定圖片格式
parames.setPreviewSize(640,480);//這裡面的參數只能是幾個特定的參數,否則會報錯.(176*144,320*240,352*288,480*360,640*480)
myCamera.setParameters(parames);

還有自動對焦,當然有些手機沒有這個功能,自動對焦是通過autoFocus()這個方法調用一個自動對焦的介面,並在裡面進行處理。
注意,這個方法必須在startPreview()和stopPreview()中間。
AutoFocusCallback是自動對焦的介面,實現它必須實現public void onAutoFocus(boolean success, Camera camera)這個方法,
所以我們可以將拍照方法放在這裡面,然後對焦後再進行拍攝。。效果會好很多。

注意自動對焦需要添加<uses-feature android:name="android.hardware.camera.autofocus" />
下面我叫直接把上面的使用例子直接寫出。
CameraTest_4.java

import java.io.BufferedOutputStream;   import java.io.File;   import java.io.FileOutputStream;  import java.io.IOException;   import android.app.Activity;   import android.content.pm.ActivityInfo;   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.Window;   import android.view.SurfaceHolder.Callback;   import android.view.View.OnClickListener;   public class CameraTest_4 extends Activity implements Callback, OnClickListener, AutoFocusCallback{       SurfaceView mySurfaceView;//surfaceView聲明       SurfaceHolder holder;//surfaceHolder聲明       Camera myCamera;//相機聲明      String filePath="/sdcard/wjh.jpg";//照片儲存路徑       boolean isClicked = false;//是否點擊標識       //建立jpeg圖片回調資料對象       PictureCallback jpeg = new PictureCallback() {                     @Override          public void onPictureTaken(byte[] data, Camera camera) {               // TODO Auto-generated method stub               try               {// 獲得圖片               Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);               File file = new File(filePath);               BufferedOutputStream bos =  new BufferedOutputStream(new FileOutputStream(file));               bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);//將圖片壓縮到流中               bos.flush();//輸出               bos.close();//關閉               }catch(Exception e)               {                   e.printStackTrace();              }         }       };      /** Called when the activity is first created. */       @Override     public void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           requestWindowFeature(Window.FEATURE_NO_TITLE);//無標題                     //設定拍攝方向           this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);           setContentView(R.layout.main);           //獲得控制項           mySurfaceView = (SurfaceView)findViewById(R.id.surfaceView1);           //獲得控制代碼           holder = mySurfaceView.getHolder();           //添加回調           holder.addCallback(this);           //設定類型           holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);           //設定監聽           mySurfaceView.setOnClickListener(this);      }      @Override      public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {           // TODO Auto-generated method stub           //設定參數並開始預覽           Camera.Parameters params = myCamera.getParameters();           params.setPictureFormat(PixelFormat.JPEG);           params.setPreviewSize(640,480);           myCamera.setParameters(params);           myCamera.startPreview();                 }      @Override       public void surfaceCreated(SurfaceHolder holder) {          // TODO Auto-generated method stub          //開啟相機           if(myCamera == null)           {               myCamera = Camera.open();               try {                   myCamera.setPreviewDisplay(holder);              } catch (IOException e) {                  // TODO Auto-generated catch block                  e.printStackTrace();               }           }               }       @Override       public void surfaceDestroyed(SurfaceHolder holder) {           // TODO Auto-generated method stub           //關閉預覽並釋放資源           myCamera.stopPreview();          myCamera.release();           myCamera = null;               }       @Override      public void onClick(View v) {           // TODO Auto-generated method stub          if(!isClicked)          {               myCamera.autoFocus(this);//自動對焦               isClicked = true;           }else          {               myCamera.startPreview();//開啟預覽               isClicked = false;          }                }       @Override       public void onAutoFocus(boolean success, Camera camera) {          // TODO Auto-generated method stub          if(success)           {               //設定參數,並拍照               Camera.Parameters params = myCamera.getParameters();               params.setPictureFormat(PixelFormat.JPEG);               params.setPreviewSize(640,480);               myCamera.setParameters(params);               myCamera.takePicture(null, null, jpeg);           }           }   }

  AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"       package="com.wjh.camera"       android:versionCode="1"      android:versionName="1.0">    <uses-sdk android:minSdkVersion="7" />    <uses-permission android:name="android.permission.CAMERA"></uses-permission> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>     <application android:icon="@drawable/icon" android:label="@string/app_name">         <activity android:name=".CameraTest_4"                   android:label="@string/app_name">            <intent-filter>                 <action android:name="android.intent.action.MAIN" />                 <category android:name="android.intent.category.LAUNCHER" />             </intent-filter>         </activity>     </application> </manifest>

  main.xml

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"     androidrientation="vertical"       android:layout_width="fill_parent"       android:layout_height="fill_parent"       >       <surfaceview android:id="@+id/surfaceView1"     android:layout_width="640px"        android:layout_height="480px"        android:layout_gravity="center">

  

Android 相機開發詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.