10.3 控制攝像頭拍照
現在的智能手機和平板電腦一般都會提供攝像頭拍照功能。在Android中提供了專門用於處理攝像頭相關事件的類,即android.hardware包中的Camera類。Camera類沒有構造方法,可以通過其提供的open()方法打開攝像頭。打開攝像頭後,可以通過Camera.Parameters類處理攝像頭的拍照參數。拍照參數設置完成後,可以調用startPreview()方法預覽拍照畫面,也可以調用takePicture()方法進行拍照。結束程序時,可以調用Camera類的stopPreview()方法結束預覽,並調用release()方法釋放攝像頭資源。Camera類常用的方法如表10.9所示。
表10.9 Camera類常用的方法
下面通過一個實例來說明控制攝像頭拍照的具體過程。
實例 實現控制攝像頭拍照功能 實例位置:
在Android Studio中創建Module,名稱爲“Camera”,在該Module中實現本實例,具體步驟如下。
(1)修改佈局文件activity_main.xml,首先將默認添加的佈局管理器修改爲幀佈局管理器,然後將TextView組件刪除,再添加一個SurfaceView組件(用於顯示攝像頭預覽畫面),最後添加一個預覽按鈕和一個拍照按鈕。具體代碼請參見光盤。
(2)打開MainActivity類,該類繼承Activity,然後在該類中,定義所需的成員變量,關鍵代碼如下:
01 private Camera camera; //定義相機對象
02 private boolean isPreview = false; //定義非預覽狀態
(3)在MainActivity類的onCreate()方法中,首先設置全屏顯示,然後判斷手機是否安裝SD卡,關鍵代碼如下:
01 //設置全屏顯示
02 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
03 WindowManager.LayoutParams.FLAG_FULLSCREEN);
04 if (!Environment.getExternalStorageState().equals( //判斷手機是否安裝SD卡
05 Environment.MEDIA_MOUNTED)) {
06 Toast.makeText(this, "請安裝SD卡!", Toast.LENGTH_SHORT).show(); // 提示安裝SD卡
07 }
(4)獲取SurfaceView組件與SurfaceHolder對象,用於顯示攝像頭預覽,關鍵代碼如下:
01 //獲取SurfaceView組件,用於顯示攝像頭預覽
02 SurfaceView sv = (SurfaceView) findViewById(R.id.surfaceView);
03 final SurfaceHolder sh = sv.getHolder(); //獲取SurfaceHolder對象
04 //設置該SurfaceHolder自己不維護緩衝
05 sh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
06 ImageButton preview = (ImageButton) findViewById(R.id.preview); //獲取“預覽”按鈕
07 ImageButton takePicture = (ImageButton) findViewById(R.id.takephoto); //獲取“拍照”按鈕
(5)爲預覽按鈕添加單擊事件監聽器,實現攝像頭的預覽功能,關鍵代碼如下:
01 preview.setOnClickListener(new View.OnClickListener() { //實現攝像頭預覽功能
02 @Override
03 public void onClick(View v) {
04 // 如果攝像頭爲非預覽模式,則打開相機
05 if (!isPreview) {
06 camera = Camera.open(); /打開相機
07 isPreview = true; //設置爲預覽狀態
08 }
09 try {
10 camera.setPreviewDisplay(sh); //設置用於顯示預覽的SurfaceView
11 Camera.Parameters parameters = camera.getParameters(); //獲取相機參數
12 parameters.setPictureFormat(PixelFormat.JPEG); //指定圖片爲JPEG圖片
13 parameters.set("jpeg-quality", 80); //設置圖片的質量
14 camera.setParameters(parameters); //重新設置相機參數
15 camera.startPreview(); //開始預覽
16 camera.autoFocus(null); //設置自動對焦
17 } catch (IOException e) { //輸出異常信息
18 e.printStackTrace();
19 }
20 }
21 });
(6)在MainActivity中,創建實現重新預覽的方法resetCamera(),在該方法中,當isPreview變量的值爲真時,調用攝像頭的startPreview()方法開啓預覽,具體代碼如下:
01 private void resetCamera() { //創建resetCamera()方法,實現重新預覽功能
02 if (!isPreview) { //如果爲非預覽模式
03 camera.startPreview(); //開啓預覽
04 isPreview = true;
05 }
06 }
(8)實現拍照的回調接口,在重寫的onPictureTaken()方法中,首先根據拍照所得的數據創建位圖,然後保存所拍攝的圖片,再把保存的圖片文件插入到系統圖庫,最後通知圖庫更新,具體代碼如下:
01
02 //實現將照片保存到系統圖庫中
03 final Camera.PictureCallback jpeg = new Camera.PictureCallback() { //照片回調函數
04 @Override
05 public void onPictureTaken(byte[] data, Camera camera) {
06 // 根據拍照所得的數據創建位圖
07 final Bitmap bm = BitmapFactory.decodeByteArray(data, 0,
08 data.length);
09 camera.stopPreview(); //停止預覽
10 isPreview = false; //設置爲非預覽狀態
11 //獲取sd卡根目錄
12 File appDir = new File(Environment.getExternalStorageDirectory(), "/DCIM/Camera/");
13 if (!appDir.exists()) { //如果該目錄不存在
14 appDir.mkdir(); //創建該目錄
15 }
16 //將獲取的當前系統時間設置爲照片名稱
17 String fileName = System.currentTimeMillis() + ".jpg";
18 File file = new File(appDir, fileName); //創建文件對象
19 try { //保存拍到的圖片
20 FileOutputStream fos = new FileOutputStream(file); //創建一個文件輸出流對象
21 //將圖片內容壓縮爲JPEG格式輸出到輸出流對象中
22 bm.compress(Bitmap.CompressFormat.JPEG, 100, fos);
23 //將緩衝區中的數據全部寫出到輸出流中
24 fos.flush();
25 fos.close(); //關閉文件輸出流對象
26 } catch (FileNotFoundException e) {
27 e.printStackTrace();
28 } catch (IOException e) {
29 e.printStackTrace();
30 }
31 //將照片插入到系統圖庫
32 try {
33 MediaStore.Images.Media.insertImage(MainActivity.this.getContentResolver(),
34 file.getAbsolutePath(), fileName, null);
35 } catch (FileNotFoundException e) {
36 e.printStackTrace();
37 }
38 //最後通知圖庫更新
39 Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
40 Uri uri = Uri.fromFile(file);
41 intent.setData(uri);
42 MainActivity.this.sendBroadcast(intent); //這個廣播的目的就是更新圖庫
43 Toast.makeText(MainActivity.this, "照片保存至:" + file, Toast.LENGTH_LONG).show();
44 resetCamera(); //調用重新預覽resetCamera()方法
45 }
46 };
(9)在onCreate()方法中,爲拍照按鈕添加單擊事件監聽器,實現攝像頭的拍照功能,關鍵代碼如下:
01 takePicture.setOnClickListener(new View.OnClickListener() {
02 @Override
03 public void onClick(View v) {
04 if (camera != null) { //相機不爲空
05 camera.takePicture(null, null, jpeg); //進行拍照
06 }
07 }
08 });
(10)重寫Activity的onPause()方法,用於當暫停Activity時,停止預覽並釋放攝像頭資源,具體代碼如下:
01 @Override
02 protected void onPause() {
03 if (camera != null) { //如果攝像頭不爲空
04 camera.stopPreview(); //停止預覽
05 camera.release(); //釋放資源
06 }
07 super.onPause();
08 }
(11)由於本程序需要訪問SD卡和控制攝像頭,所以需要在AndroidManifest.xml文件中賦予程序訪問SD卡和控制攝像頭的權限,關鍵代碼如下:
01 <!-- 授予程序可以向SD卡中保存文件的權限 -->
02 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
03 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
04 <!-- 授予程序使用攝像頭的權限 -->
05 <uses-permission android:name="android.permission.CAMERA"/>
06 <uses-feature android:name="android.hardware.camera.autofocus"/>
07 <uses-feature android:name="android.hardware.camera"/>
(12)在AndroidManifest.xml文件的<activity>標記中添加screenOrientation屬性,設置其橫屏顯示,關鍵代碼如下:
android:screenOrientation="landscape"
(13)在工具欄中找到 下拉列表框,選擇要運行的應用(這裏爲Camera),再單擊右側的 按鈕,在顯示的界面中,單擊預覽按鈕,啓動攝像頭,單擊拍照按鈕進行拍照,如圖10.9所示。
圖10.9 預覽與拍照
說明:本實例需要攝像頭硬件的支持,這裏我們使用真機測試。讀者需要在手機中手動開啓攝像頭權限與sd卡讀寫權限。