標籤:android
項目中最近需要用到攝像機拍照,起初的時候肯定怎麼省事怎麼來,直接就是調用系統的攝像機了,那麼問題來了,調用系統攝像機的時候,發現不同的手機拍攝出的照片的旋轉角度不一樣。舉個例子來說,小米手機拍攝出的照片旋轉角度為0度(照片是正的),三星手機拍攝出的照片,照片用同樣的方法載入出來,而照片旋轉了90度,(照片是反的),shit,這是為什嗎?
剛開始的時候肯定將照片旋轉一定的角度來解決,但是發現這樣有一點low啊,並且設計上要求在拍照介面中要自己繪製一個框框,使用者看到這個框框後拍照,拍照後自動截取圖片,並壓縮到640*640,shit,這樣以來調用系統的照相機肯定就是不行了啊。。。。。
沒有辦法了,只能到網上去搜尋相關的解決方案了,但是搜尋了好久基本上就是簡單的預覽,哎,最後到google官方的例子裡面發現了一個比較好的SurfaceView,這個SurfaceView起碼根據你的手機計算出了最合適的拍照比例,好吧。這還是不夠的,還是太簡單了。最後通過多方途徑(參考以前項目的實現,問一個大哥),大概明白了攝像機的基本原理。
下面簡單的大致描述一下攝像機:
1:我們要想拍照正確,自訂的拍照介面的螢幕現實方式要是橫屏拍攝,最好設定為全屏並且沒有標題,這樣拍攝出的照片才是正確,否則你就哭去吧,這也是血淚摸索出來的,下面簡單粘貼一個設定檔:
<activity android:name="com.example.AndroidCaptureCropTags.camera.ActivityCapture" android:configChanges="keyboardHidden|orientation|screenSize" android:screenOrientation="landscape" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="adjustResize|stateHidden"> </activity>
2:其實我們拍照預覽的時候,google把網路攝影機封裝了一系列的API,主要是再預覽之前計算最佳的預覽比例,防止預覽的時候變形。並計算最佳的拍攝出的圖片的現實比例,這樣拍攝出的圖片也不會變形。
3:接下來就是攝像機介面白色框框的繪製,就是自訂了一個View而已。
4:最後拍攝出來照片後根據螢幕中顯示的白色的框框來截取圖片(這個地方也挺蛋疼的。。。);
pictureCallBack = new PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {_isCapturing = false;Bitmap bitmap = null;try {BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeByteArray(data, 0, data.length, options);//Debug.debug("width--:" + options.outWidth + " height--:" + options.outHeight);options.inJustDecodeBounds = false;options.inPreferredConfig = Bitmap.Config.ARGB_8888;//此處就把圖片壓縮了options.inSampleSize = Math.max(options.outWidth/ kPhotoMaxSaveSideLen, options.outHeight/ kPhotoMaxSaveSideLen);bitmap = BitmapUtil.decodeByteArrayUnthrow(data, options);if (null == bitmap) {options.inSampleSize = Math.max(2, options.inSampleSize * 2);bitmap = BitmapUtil.decodeByteArrayUnthrow(data, options);}} catch (Throwable e) {}if (null == bitmap) {Toast.makeText(ActivityCapture.this, "記憶體不足,儲存照片失敗!", Toast.LENGTH_SHORT).show();return;}//long start = System.currentTimeMillis();Bitmap addBitmap = BitmapUtil.rotateAndScale(bitmap, _rotation, kPhotoMaxSaveSideLen, true);Bitmap finalBitmap = cropPhotoImage(addBitmap);File photoFile = PathManager.getCropPhotoPath();boolean successful = BitmapUtil.saveBitmap2file(finalBitmap, photoFile, Bitmap.CompressFormat.JPEG, 100);while (!successful) {successful = BitmapUtil.saveBitmap2file(finalBitmap, photoFile, Bitmap.CompressFormat.JPEG, 100);}if (finalBitmap != null && !finalBitmap.isRecycled()) {addBitmap.recycle();}Intent intent = new Intent();intent.putExtra(kPhotoPath, photoFile.getAbsolutePath());ActivityCapture.this.setResult(RESULT_OK, intent);ActivityCapture.this.finish();}};上面代碼的邏輯是:拍攝後系統會將圖片的資料以byte[]的形式傳遞給我們,options.inJustDecodeBounds = true;先用這種形式避免直接將圖片載入進記憶體中,得到了圖片的寬度和高度後,然後計算圖片的inSampleSize,這裡自己寫了一個最大的邊長1600,通過實驗發現定義成這個邊長後,拍攝出的照片無論是手機上看還是電腦上看,體驗效果都是不錯的,這也是通過詢問以前的大哥才瞭解的,(我想了半天也不知道為什麼非要定義成1600.。。。)。下面就是將data載入成一個Bitmap,注意此時的Bitmap可能是旋轉的,還要調用這個方法Bitmap addBitmap = BitmapUtil.rotateAndScale(bitmap, _rotation, kPhotoMaxSaveSideLen, true);來將圖片擺正,其中的_rotation這個角度是我們在拍攝的過程中不斷的計算的(這個有些是從網上找的,正在消化中。。。。,自己不是太明白的就不說了,免的說錯了誤人子弟)。將圖片旋轉正確後下面就是所見即所得 (WYSIWYG)了,。。。。。。。
//根據拍照的圖片來剪裁private Bitmap cropPhotoImage(Bitmap bmp) {int dw = bmp.getWidth();int dh = bmp.getHeight();int height;int width;if (dh > dw) {//圖片豎直方向//切圖片時按照豎屏來計算height = getWindowManager().getDefaultDisplay().getWidth();width = getWindowManager().getDefaultDisplay().getHeight();} else {//圖片是水平方向//切圖片時按照橫屏來計算width = getWindowManager().getDefaultDisplay().getWidth();height = getWindowManager().getDefaultDisplay().getHeight();}Rect rect = new Rect();int left = (width - cropBorderView.getRect().width()) / 2;int top = (height - cropBorderView.getRect().height()) / 2;int right = left + cropBorderView.getRect().width();int bottom = top + cropBorderView.getRect().height();rect.set(left, top, right, bottom);float scale = 1.0f;// 如果圖片的寬或者高大於螢幕,則縮放至螢幕的寬或者高if (dw > width && dh <= height) {scale = width * 1.0f / dw;}if (dh > height && dw <= width) {scale = height * 1.0f / dh;}// 如果寬和高都大於螢幕,則讓其按按比例適應螢幕大小if (dw > width && dh > height) {scale = Math.max(width * 1.0f / dw, height * 1.0f / dh);}//如果圖片的寬度和高度都小於螢幕的寬度和高度,則放大至螢幕大小if (dw < width && dh < height) {scale = width * 1.0f / dw;}Matrix matrix = new Matrix();matrix.postScale(scale, scale);try {Bitmap b2 = Bitmap.createBitmap(bmp, 0, 0, dw, dh, matrix, true);if (null != b2 && bmp != b2) {bmp.recycle();bmp = b2;}} catch (OutOfMemoryError e) {e.printStackTrace();}try {Bitmap b3 = Bitmap.createBitmap(bmp, rect.left, rect.top, rect.width(), rect.height());if (null != b3 && bmp != b3) {bmp.recycle();bmp = b3;}} catch (OutOfMemoryError e) {e.printStackTrace();}//將圖片壓縮至640*640try {Bitmap b4 = Bitmap.createScaledBitmap(bmp, 640, 640, false);if (null != b4 && bmp != b4) {bmp.recycle();bmp = b4;}} catch (OutOfMemoryError e) {e.printStackTrace();}return bmp;}
經過測試發現,拍攝出的照片並不是你在螢幕中看見是豎直方向就是豎直方向,可能拍攝出的照片是豎直方向也可能是水平方向(即照片是豎的還是橫的)。這是兩種情況要分別來進行處理的。。。下面簡單用文字描述一下這兩種情況。
1:照片是豎的,而我們的Activity是橫屏的。。。所以計算截取框的時候我們要按照螢幕是豎的來計算,才可以確定截取框的正確的位置。
2:照片是橫的,我們的Activity也是橫屏的。。。所以正常計算就ok。
3:不要想著我們螢幕那麼大,拍攝出的照片就是螢幕那麼大,而是比螢幕大得多。。。。,這就需要我們縮放到螢幕的大小,這樣才可以做到所見即所得 (WYSIWYG)嘛,你說是不是?具體的縮放無非就是圖片等比例縮放。圖片是等比例縮放了,但是可能擔心這種情況,要是縮放後比螢幕小一點或者大一點那不就不準確了嘛?這點我剛開始的時候也是擔心的,但是我們在代碼中計算了拍照的最合適的預覽比例,最合適的照片比例,經過很多的手機測試發現都是沒有問題的,他們的比例是保持一致的。
4:接下來就是煩人的了,最後將圖片壓縮到640*640,就ok了。
5:明白一點不要重複的造車子,你的心裏面就舒服多了。
完了就儲存到本地的檔案裡面就ok了。下面就是給圖片添加一個Tag什麼的,Tag添加點動畫,都是一點點調出來的,沒什麼技術含量,包括正方形的控制項的自訂也很簡單,直接看看代碼就ok了。。。。。
草,我發現我寫部落格寫不了那麼的詳細。。。shit
Android 自訂照相機拍照並仿照nice添加標籤