android--相機開發----我的相機我做主

一口三個漢堡發表於2020-04-04

因為要模仿一個iphone的遊戲,昨天開始學習android的相機應用。


在android中應用相機功能,一般有兩種:一種是直接呼叫系統相機,一種自己寫的相機。

我將分別演示兩種方式的使用:


第一種:是使用Intent跳轉到系統相機,action為:android.media.action.STILL_IMAGE_CAMERA

關鍵程式碼:

    

例子:CameraTest_2.java

 

想要測試的,可以直接新建一個專案,並且把主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

 

CameraTest_3.java

 

 

 

 

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

(5)能夠預覽了,接下來就是拍照了,拍照用到了一個camera.tackPiture()這個方法,這個方法,有三個引數分別是

ShutterCallBack shutter,PictureCallBack raw,PictureCallBack jpeg.

下面是對他們的實現

 

當開始拍照時,會依次呼叫shutter的onShutter()方法,raw的onPictureTaken方法,jpeg的onPictureTaken方法.

三個引數的作用是shutter--拍照瞬間呼叫,raw--獲得沒有壓縮過的圖片資料,jpeg---返回jpeg的圖片資料

當你不需要對照片進行處理,可以直接用null代替.

 

注意,當呼叫camera.takePiture方法後,camera關閉了預覽,這時需要呼叫startPreview()來重新開啟預覽。


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

MySurfaceView.java

 

CameraTest_3.java

 

 

 

這樣就是實現了拍照的功能,那麼怎樣要圖片儲存呢?那麼這是就需要在那個引數中的jpeg的

方法裡面進行處理了,那個方法的data引數,就是相片的資料。

我們通過BitmapFactory.decodeByteArray(data, 0, data.length)來獲得圖片並通過io處理,將圖片儲存到想要儲存的位置

下面這段程式碼,是將照片儲存到/sdcard/wjh.jpg;並把一些沒有用到的程式碼全部刪掉,剩下一些必須的程式碼

MySurfaceView.java

 

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

<textarea cols="50" rows="15" name="code" class="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); } } }</textarea> 

main.xml

 

AndroidManifest.xml

<textarea cols="50" rows="15" name="code" class="xhtml"><?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></textarea> 

相關文章