Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義Camera

Android自定義Camera

編輯:關於Android編程

一些開發人員需要一個(為應用程序定制或提供特殊功能)的相機用戶界面(自定義相機)。創建一個定制的相機活動需要更多的代碼,但它可以為你的用戶提供更令人信服的體驗。

為您的應用程序創建自定義相機接口的一般步驟如下:

1.檢測和訪問攝像機-創建代碼,以檢查是否存在攝像頭和允許訪問。

2.創建一個預覽類,創建一個攝像機預覽類繼承SurfaceView實現SurfaceHolder接口。這類用於相機預覽。

3.建立一個預覽布局,一旦你有相機預覽類,創建一個視圖布局,集成了你想要預覽界面和用戶界面控件。

4.設置監聽(為捕獲),為像按鈕一樣的控件設置監聽。

5.捕獲並保存文件-設置捕獲圖片或視頻並保存到外部設備的代碼。

6.釋放相機(重要)-在使用相機後,您的應用程序必須正確地釋放它,供其他應用程序使用。

 

相機硬件是一個共享資源,必須小心管理,所以你的應用程序可能會與其他要使用它的其他應用程序發生沖突。

 

下面的章節將討論如何檢測相機的硬件,如何請求訪問一個攝像頭,如何捕捉照片或視頻,以及如何釋放相機:

記住:

當您的應用程序不在使用攝像頭的時候,要通過Camera.release()方法釋放相機對象。如果你的應用程序沒有正確地釋放相機,所有隨後嘗試訪問相機的應用程序,包括那些你自己的應用程序,都將失敗,可能會導致你或其他應用程序被關閉。

Camera.release():斷開並釋放相機的對象資源。只要不使用相機對象,就要釋放。

 

 

1.檢測攝像機硬件

如果你的應用程序沒有特別要求使用攝像頭(在清單文件中聲明Camera),你應該檢查一下是否有一個攝像頭在運行時可用。要執行此檢查,使用PackageManager.hasSystemFeature()方法,如下面的代碼示例所示:

/**Check if this device has a camera */

private boolean checkCameraHardware(Contextcontext) {

if(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){

// this device has a camera

return true;

}else {

// no camera on this device

return false;

}

}

 

安卓設備可以有多個攝像頭,例如一個背對著相機的攝影和一個前置攝像頭進行錄像。Android 2.3(API Level 9)以及後面的版本允許你檢查在設備上可使用的攝像頭的個數。通過Camera.getNumberOfCameras()方法。

public static int getNumberOfCameras():返回此設備上可用的物理攝像頭的數量。

 

2.連接相機

如果你已經確定了你的應用程序運行的設備上有一個攝像頭,你必須通過獲取一個相機的實例來請求獲得它(除非你使用的意圖訪問相機)。

 

為了訪問私有攝像頭,要使用Camera.open() 【open(int)】方法,並且try catch所有異常,下面代碼所示:

public static Cameraopen ():創建一個新的相機對象(連接第一個後置攝像頭),如果設備沒有一個後置攝像頭,返回null。

 

/** A safe way to get an instance of theCamera object. */

public static Camera getCameraInstance(){

Camera c = null;

try {

c = Camera.open(); // attempt to get a Camera instance

}

catch (Exception e){

// Camera is not available (in use or does not exist)

}

return c; // returns null if camera is unavailable

}

記住:在使用Camera.open()方法的時候,要檢查異常,如果不檢查異常:當攝像頭正被其他應用使用,或者不存在攝像頭,會導致你的應用程序shut down。在Android2.3(API Level9)的設備上或更高的版本上,你可以使用通過Camera.open(int)方法連接特定的攝像頭。上面的代碼返回第一個,後置攝像頭(該設備上的,即使有多個攝像頭)。

public static Cameraopen (int cameraId):創建一個新的相機對象訪問特定的硬件相機。如果這個攝像頭被其他應用程序打開,將會拋出一個運行時異常。參數:between 0 and getNumberOfCameras()-1。

 

u 使用完攝像頭後必須調用release()方法釋放相機,否則這個攝像頭仍然被鎖定,是無法被其他應用程序訪問的。

 

u 對於一個特定的攝像頭硬件,你的應用程序應該在同一時間只有一個活動著的攝像頭對象。

 

u 其他方法的回調被傳遞給了thread(線程)的event loop(事件輪詢器, 消息循環,使線程隨時處理事件,但不退出)叫做open()。如果這個線程沒有event loop,回調會被傳遞給主線程的event loop。如果沒有主線程的event loop,回調不會傳遞。

 

警告:在某些設備上,這種方法可能需要很長的時間才能完成。最好是從一個工作者線程調用這個方法(可能使用AsyncTask)以避免阻塞UI線程的主要應用。(此方法最好在子線程中調用)。

 

攝像頭禁用?

public boolean getCameraDisabled (ComponentNameadmin);

確定是否設備的攝像頭已被禁用或被當前的管理員,如果指定,或所有管理員。

Admin:管理組件的名稱。

 

3.檢查相機功能

一旦你獲得一個攝像頭,你可以得到進一步得到更多攝像頭的信息,通過Camera.getParameters()方法可以得到攝像頭的功能信息,檢查返回的相機所支持功能的參數對象。使用API級別9或更高的時候,可以通過Camera.getCameraInfo()方法確定一個攝像頭是否在設備的正面或背面,以及圖像的方向。

publicCamera.Parameters getParameters ():返回此相機的當前設置。如果要修改返回的相機配置參數,就必須通過setParameters(Camera.Parameters)使新的相機參數生效。

 

public void setParameters (Camera.Parameters params):修改相機的設置。

Camera.Parameters params:相機參數。

 

2 Camera.Parameters params類(以後補充)

 

4.創建預覽類

為了使用戶可以拍攝照片和視頻,要使他們必須能夠看到設備攝像頭看到的場景。相機預覽類是一個圖形,可以實時顯示來自攝像頭的數據,所以用戶可以組織和拍攝照片或視頻。

下面的示例代碼演示了如何創建一個基本的相機預覽類,它包含了一個視圖布局。這個類實現了SurfaceHolder.Callback接口。用於捕捉事件回調(創建和銷毀界面),需要指定攝像預覽輸入。

 

 

/** A basic Camerapreview class */

public class CameraPreview extendsSurfaceView implements SurfaceHolder.Callback {

private SurfaceHolder mHolder;

private Camera mCamera;

public CameraPreview(Context context, Camera camera) {

super(context);

mCamera = camera;

//Install a SurfaceHolder.Callback so we get notified when the

// underlying surface is created and destroyed.

mHolder = getHolder();

mHolder.addCallback(this);

// deprecated setting, but required on Android versions prior to 3.0

//推薦設置。

mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

}

public void surfaceCreated(SurfaceHolder holder) {

//The Surface has been created, now tell the camera where to draw the preview.

try {

mCamera.setPreviewDisplay(holder);

mCamera.startPreview();

} catch (IOException e) {

Log.d(TAG, "Error setting camera preview: " + e.getMessage());

}

}

public void surfaceDestroyed(SurfaceHolder holder) {

//empty. Take care of releasing the Camera preview in your activity.

}

 

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
//在改變surface前要先停止預覽。
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
//停止一個不存在的預覽。
}
// set preview size and make any resize, rotate or
// reformatting changes here設置預覽大小和作任何調整大小,旋轉或重新設置格式修改
// start preview with new settings
//開始預覽新設置。
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}


SurfaceHolder.Callback:
一個客戶端可以實現這個接口從而接收到Surface的信息變化。
想讓SurfaceHolder.CallBack生效,必須執行SurfaceHolder.addCallBack(SurfaceHolder.CallBack)方法。當使用一個SurfaceView,這個Surface只在surfaceCreated(SurfaceHolder)和surfaceDestroyed(SurfaceHolder)之間可獲得。這個Callback在 SurfaceHolder.addCallback方法中設置。


abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height): 一旦Surface的結構變化(格式或者尺寸大小的變化)都會導致這個方法的調用。你應該此時更新Surface表面的圖像。這個方法在surfaceCreated(SurfaceHolder)後至少調用一次。


Holder: 該SurfaceHolder的surface發生改變了。
Format: Surface的新的像素格式。
Width:Surface的新寬度。
Height:Surface的新高度。


abstract void surfaceCreated(SurfaceHolder holder):Surface第一次創建後立即調用。在這個方法中寫希望渲染surface的代碼。注意:只有一個線程可以在一個Surface上draw(),所以你不應該在Surface上draw(),如果此時該Surface在另一個線程中渲染。
Holder:該SurfaceHolder的surface正在被創建。


abstract void surfaceDestroyed(SurfaceHolder holder):Surface馬上被銷毀前調用。執行這個回調後,你不應該嘗試去訪問這個surface。在執行這個方法後,如果你有一個渲染線程直接訪問這個surface,你必須確保這個線程不再接觸這個Surface。


public abstract void addCallback (SurfaceHolder.Callback callback):為該holder添加一個Callback回調,這個holder可以關聯多個Callback.


如果你想為你的相機預覽界面設置特定的大小,就在surfacechanged()方法中設置。設置預覽大小的時候,你必須使用從getsupportedpreviewsizes()方法中獲取的值。不要在setpreviewsize()設定任意的值。
public List getSupportedPreviewSizes ():獲取所支持預覽的大小。返回一個相機所支持的場景模式的集合,返回null表示不支持場景模式設置。


Camera.Size
相機尺寸
總結:
屬性:public int height 圖片的高度
Public int width 圖片的寬度
構造方法:
Camera.Size(int w, int h)


public void setPreviewSize (int width, int height):設置預覽圖片的尺寸。如果預覽已經開始(啟動了),應用程序應該在更改預覽大小之前先停止預覽。寬度和高度的邊是基於攝像機的方向。也就是說,預覽大小是在它被顯示方向旋轉之前的大小。因此,應用程序需要考慮的顯示方向,同時設置預覽大小。(這兩個一起設置的時候要注意)例如,假設相機支持320x480 480x320的預覽圖片。應用程序需要一個3:2的預覽比例。如果顯示方向設置為0或180,預覽大小應設置為480x320。如果顯示方向設置為90或270,預覽大小應設置為320x480。在設置圖片大小和縮略圖大小時,還應考慮顯示方向。
Width: 圖片的寬度,以像素為單位。
Height: 圖片的寬度,以像素為單位。


public final void setDisplayOrientation (int degrees):設置順時針旋轉的預覽顯示的度數。這影響到預覽框和snapshot後顯示的圖片。這個方法對垂直模式的應用有效。請注意:前置攝像頭的預覽顯示在旋轉之前水平翻轉了,即
圖像是反映沿攝像頭傳感器的中心垂直軸。所以用戶可以看到自己像鏡子一樣。


 這個方法不允許在攝像頭預覽的時候調用(setDisplayOrientation)


如果你想讓相機的圖像顯示在同一個方向上顯示,可以使用以下代碼:
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}


int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}


從API14開始,這種方法可以在預覽時主動被調用。
int degrees:圖片將順時針旋轉的角度。有效值分別為0、90、180和270。起始位置為0(景觀)




public final void setPreviewDisplay (SurfaceHolder holder):用於設置實時預覽的界面。
不論是surface還是surface texture 必須能夠預覽,並且預覽界面可以用來拍照。同一個surface界面可以重新設定而不會損害surface。設置一個預覽界面會重置所有預覽界面的結構,通過setPreviewTexture(SurfaceTexture)方法實現。
當這個方法調用的時候,SurfaceHolder必須已經包含了一個surface。如果你使用SurfaceView,在調用setPreviewDisplay()或starting preview()之前,你必須先注冊一個SurfaceHolder.Callback接口和調用addCallback(SurfaceHolder.Callback)方法,並且等待surfaceCreated(SurfaceHolder)的執行。
這個方法必須在startPreview()方法之前調用。唯一的例外是,在調用startPreview()方法之前,如果沒有設置預覽界面,(或者預覽界面設置為空),這個方法會調用一次(傳入一個非空參數)來設置預覽界面。預覽界面正在運行的時候,預覽界面無法做改動。




Camera布局:
例如前面舉的一個例子(相機預覽類)。需要一個布局,來進行拍照和錄像。本節將向您展示如何構建預覽的基本布局和Activity。下面的布局代碼提供了一個非常基本的視圖,可以用來顯示一個攝像頭預覽。在這個例子中,FrameLayout元素是相機預覽類的容器。這個布局用來在相機預覽圖像上疊加額外的圖片信息和控件。



android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<framelayout android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
/>


android:id="@+id/button_capture"
android:text="Capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</framelayout



在大多數設備,相機預覽的默認方向是垂直的。此示例布局指定一個橫向(縱向)的布局,下面的代碼應用程序landscape的方向性。要使攝像頭的預覽方向為垂直方向,你應該在清單文件中增加以下代碼。
android:label="@string/app_name"
android:screenOrientation="landscape">








注意:一個攝像頭預覽模式不必一定是垂直模式。在Android 2.2(API Level 8開始),你可以使用setDisplayOrientation()方法設置預覽圖像旋轉。要想改變預覽方向。在預覽類的surfaceChanged()方法中,首先要通過Camera.stopPreview()方法停止預覽。然後通過Camera.startPreview()方法重啟預覽。把你自定義的預覽類添加到上面FrameLayout元素中。你的相機Activity必須確定當Activity執行pause(暫停)和shut down(關閉)的時候,它已經釋放了camera。


public class CameraActivity extends Activity {
private Camera mCamera;
private CameraPreview mPreview;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Create an instance of Camera
mCamera = getCameraInstance();


// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
}
在上面的例子中的getCameraInstance()方法指的是在Accessing cameras中。




捕獲圖片:
一旦你已經建立了一個預覽類和一個視圖布局來顯示它,你准備開始用您的應用程序捕捉圖像。在您的應用程序代碼中,您必須為用戶界面控件設置偵聽器來回應用戶的拍照操作。要獲取一張圖片,使用Camera.takePicture()方法類獲取圖片。該方法需要三個參數,從而接收來自攝像機的數據。為了接收以JPEG格式的數據,你必須實現Camera.PictureCallback接口,來接收圖片信息和把信息寫到文件中去。以下的代碼展示了實現了一個 Camera.PictureCallback接口來保存圖片(從相機中獲取的)。


private PictureCallback mPicture = new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null){
Log.d(TAG, "Error creating media file, check storage permissions: " +
e.getMessage());
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
觸發調用攝像頭捕獲圖像的方法 Camera.takePicture() 。下面的代碼展示了如何在Button的View.OnClickListener方法中調用 Camera.takePicture() 方法。
// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// get an image from the camera
mCamera.takePicture(null, null, mPicture);
}
}
);


注意:在應用程序使用完攝像頭後要及時通過Camera.release()方法釋放攝像頭。


捕獲錄像
使用Android框架的視頻采集需要對相機對象和MediaRecorder類兩者進行精心的協調管理。當使用攝像頭錄像,你必須管理Camera.lock()和Camera.unlock()方法讓MediaRecorder可以訪問相機硬件。除了 Camera.open() 和 Camera.release()需要調用之外。


public final void lock ():重新鎖上的攝像頭,以防止其他進程訪問它。相機對象默認是鎖定的,除非調用unlock()方法。通常用reconnect()代替。自從API級別14,相機會自動鎖定在應用程序的start()中。應用程序可以使用相機,當啟動後(start)。記錄啟動或停止後無需再調用此方法。如果你不錄制視頻,你可能不需要這種方法。


public final void unlock ():打開相機允許另一個進程訪問它。通常,相機被鎖定在的活動著的攝像機對象,直到release()被調用。允許進程間快速切換,你可以調用這個方法來暫時釋放相機,讓另一個應用來使用;一旦其他進程完成後你可以調用reconnect()方法取回相機。
這個方法必須在setCamera(Camera)方法之前調用。執行start()方法之後就不能調用了。
如果你不錄制視頻,你可能不需要這種方法。


注意:從安卓4.0(API Level 14)之後,Camera.lock()和Camera.unlock()由你自己管理。


不像使用攝像頭來拍照,錄像需要一個非常特別的調用順序。你必須遵循一個特定的執行順序,為了使您的應用程序成功地准備和捕捉視頻。正如下面所說。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved