Android Camera探究之路——起步,androidcamera
Android Camera探究之路——起步
Camera在手機中有著舉足輕重的地位,不管是二維碼還是照片、識別,都離不開網路攝影機,本文將對Android中的Camera進行全面解析。
許可權鎮樓:
<uses-permission android:name="android.permission.CAMERA"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-feature android:name="android.hardware.camera"/>
調用系統Camera
通過系統定義的Intent Action,我們可以很方便的使用所有實現了Camera功能的App。
ACTION_IMAGE_CAPTURE
這個action是最常用的一個調用系統Camera的action。
使用方式如下:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
通過這樣一個Action,我們就可以調用所有聲明了Camera的App。
那麼如何收到拍攝的圖片呢?我們自然是需要使用startActivityForResult方法。
這裡我們先來看最簡單的:
我們在:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); }
onActivityResult方法中,通過data參數來擷取映像:
/** * 通過data取得圖片 */Bundle extras = data.getExtras();Bitmap bitmap = (Bitmap) extras.get("data");mImageViewShow.setImageBitmap(bitmap);
但是,現在手機像素這麼高,萬一圖片特別大呢,會不會data過大而FC呢?放心,Android早就考慮到了,所以,data裡面壓根就不是完整的圖片,它只是一張縮圖,對,真的是縮圖。所以,我們需要擷取到拍攝的原圖,就不能使用這種方法。但是我們可以這樣做,我們可以指定MediaStore類的一個EXTRA_OUTPUT來指定拍攝映像儲存的位置,相當於建立一個臨時檔案。在onActivityResult中,我們不使用data來擷取映像,而是直接去讀這個臨時檔案即可。
指定EXTRA_OUTPUT:
String tempPath = Environment.getExternalStorageDirectory().getPath();mFilePath = tempPath + "/" + "test1.png";
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);Uri photoUri = Uri.fromFile(new File(mFilePath));intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);startActivityForResult(intent, CAMERA_CODE1);
onActivityResult:
/** * 通過暫存路徑取得圖片 */FileInputStream fis = null;Bitmap bitmap = null;try { fis = new FileInputStream(mFilePath); bitmap = BitmapFactory.decodeStream(fis);} catch (FileNotFoundException e) { e.printStackTrace();} finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } }}
這樣我們就可以擷取到完整的拍攝圖片了。後面你可以讓映像顯示出來,顯示的時候,同樣需要考慮大圖的處理,避免映像尺寸帶來的問題,這些東西,請參考這裡:
http://blog.csdn.net/eclipsexys/article/details/44459771
這裡就不贅述了。如果你的App僅僅是需要非常簡單的拍攝功能,那麼通過調用系統Intent就足夠了,但是大部分時候,這都是不可能的,所以下面我們來看看如何自訂Camera。
自訂Camera
根據Google Android Doc,自訂一個Camera需要如下幾個步驟:
1.檢查Camera是否存在,並在AndroidManifest.xml中賦予相關的許可權;
2.建立一個繼承於SurfaceView並實現SurfaceHolder介面的Camera Preview類;
3.建立一個Camera Preview布局檔案;
4.設定一個拍照的監聽事件,例如單擊按鈕事件等;
5.實現拍照,並儲存拍照後的圖片到裝置;
6.釋放Camera。
看上去還是比較複雜的。所以我們一步步來。
首先,我們建立預覽Camera的介面:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/ll" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:orientation="horizontal"> <Button android:id="@+id/btn_switch_camera" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:onClick="switchCamera" android:text="切換網路攝影機"/> <Button android:id="@+id/btn_capture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:onClick="capture" android:text="拍照"/> </LinearLayout> <SurfaceView android:id="@+id/sv_camera" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/ll" android:text="camera"/></RelativeLayout>
非常簡單,兩個button下面一個surfaceview:
然後,我們建立一個Activity,用來展示Camera的預覽:
這個Activity裡面肯定有SurfaceView,所以,SurfaceView的那一套東西,自然是少不了,不懂的請自行腦補。
那麼在這個Activity裡面,我們需要做什麼呢?兩件事情:
Android的Camera是獨享的,如果多處調用,就會拋出異常,所以,我們需要將Camera的生命週期與Activity的生命週期綁定:
初始化相機非常簡單:
/** * 初始化相機 * * @return camera */private Camera getCamera() { Camera camera; try { camera = Camera.open(); } catch (Exception e) { camera = null; } return camera;}
釋放相機也非常簡單:
/** * 釋放相機資源 */private void releaseCamera() { if (mCamera != null) { mCamera.setPreviewCallback(null); mCamera.stopPreview(); mCamera.release(); mCamera = null; }}
那麼下面我們再來看如何把相機映像設定到SurfaceView中進行預覽:
/** * 在SurfaceView中預覽相機內容 * * @param camera camera * @param holder SurfaceHolder */private void setStartPreview(Camera camera, SurfaceHolder holder) { try { camera.setPreviewDisplay(holder); camera.setDisplayOrientation(90); camera.startPreview(); } catch (IOException e) { e.printStackTrace(); }}
尼瑪,是不是也非常簡單,camera的一個方法已經幫我們自動關聯了SurfaceView。
PS 這裡需要注意下這個方法camera.setDisplayOrientation(90),通過這個方法,我們可以調整網路攝影機的角度,不然預設是橫屏,映像會顯示的比較奇怪。當然,即使你設定的90,映像也有可能比較奇怪,這是因為你沒有對映像進行正確的縮放,比例不對。
通過上面的設定,我們已經可以正常預覽網路攝影機的映像內容了,下面我們就可以拍照了。
唉,拍照真的也非常簡單,就一句話:
mCamera.takePicture(null, null, mPictureCallback);
當然,為了配合拍照,我們需要做一些設定,設定拍照的參數,並且給拍照之後的動作設定一個回調:
參數:
Camera.Parameters params = mCamera.getParameters();params.setPictureFormat(ImageFormat.JPEG);params.setPreviewSize(800, 400);params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);mCamera.setParameters(params);// 使用自動對焦功能mCamera.autoFocus(new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { mCamera.takePicture(null, null, mPictureCallback); }});
回調:
/** * Camera回調,通過data[]保持圖片資料資訊 */Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { File pictureFile = getOutputMediaFile(); if (pictureFile == null) { return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); Intent intent = new Intent(CustomCamera.this, CameraResult.class); intent.putExtra("picPath", pictureFile.getAbsolutePath()); startActivity(intent); CustomCamera.this.finish(); } catch (IOException e) { e.printStackTrace(); } }};
在回調中,我們將拍攝好的圖片地址傳遞給用於展示的ImageView。這樣就完成了相機的拍攝與圖片的展示。
處理映像變形
由於我們自己在布局中建立了一個SurfaceView,而且我們之間讓他match_parent了,所以,映像在preview的時候,肯定是會有展開的。那麼如何處理這些變形呢?
我們可以通過改變SurfaceView大小的方式來實現,在Android API Demo中,Google也給我們提供了這樣一個執行個體:
路徑如下:
android-22/legacy/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.java
Google就是通過設定新的大小來適應預覽地區大小的方式來解決變形問題的,所以說,內事不懂看源碼,外事不懂看Demo。
自訂取景畫面
聽上去非常高大上,其實,真的非常簡單,你只需要用一個FrameLayout把用來Preview的SurfaceView包起來就OK了,下面你想加什麼,就直接在FrameLayout中加吧,like this:
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/ll"> <SurfaceView android:id="@+id/sv_camera" android:layout_width="match_parent" android:layout_height="match_parent" android:text="拍照地區"/> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="center" android:src="@drawable/demo"/></FrameLayout>
不光了ImageView,ViewPager也可以,這樣甚至可以做一個可切換的浮水印相機了。是不是非常簡單,而且加入的一切都是可操作的,加動效、顏色,分分鐘搞定。
以上。
起步之後,我們要開始跑了。
代碼下載,請移步全球最大同性程式猿交友社區:
https://github.com/xuyisheng/CameraGuide
後續篇章也會在此repo中更新。