標籤:無預覽拍照 no-preview take-photo
副標題:Take Picture without preview Android
Google出於對隱私的保護,制定了一條門檻,即在Android應用開發中編寫拍照程式是必需要有映像預覽的。這會對那些惡意程式比如Android中泛濫的Service在後台偷偷記錄手機使用者的行為與周邊資訊。這樣的門檻還包括手機廠商內建的相機軟體在拍照時必須是有聲音,這樣要避免一些偷拍的情況。
處於技術調研與一些特殊無害情境的使用,我們要用到不用預覽的拍照。此文就是以此為背景,做的一些調研。只是用不多與五款手機測試,不保證所有的裝置都無問題。
免責聲明:本文純屬技術研究,請勿用於有害情境!方案
既然Google在技術上做出了特定要求,網友們想出各種workaround的方法,比如用一個空的SurfaceView或將此preview設成透明。
但這兩個辦法我都失敗了。而網友Sam給出的將preview設定成1x1大小的方案起了作用。下面的demo就是示範此方案:
介面甚是簡單,一個按鈕用以拍照,onClick事件方法名為onTakePhotoClicked。
public void onTakePhotoClicked(View view) { final SurfaceView preview = new SurfaceView(this); SurfaceHolder holder = preview.getHolder(); // deprecated setting, but required on Android versions prior to 3.0 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.addCallback(new SurfaceHolder.Callback() { @Override //The preview must happen at or after this point or takePicture fails public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "Surface created"); Camera camera = null; try { camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); Log.d(TAG, "Opened camera"); try { camera.setPreviewDisplay(holder); } catch (IOException e) { throw new RuntimeException(e); } camera.startPreview(); Log.d(TAG, "Started preview"); camera.takePicture(null, null, pictureCallback); } catch (Exception e) { if (camera != null) camera.release(); throw new RuntimeException(e); } } @Override public void surfaceDestroyed(SurfaceHolder holder) {} @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} }); WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE); WindowManager.LayoutParams params = new WindowManager.LayoutParams( 1, 1, //Must be at least 1x1 WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 0, //Don‘t know if this is a safe default PixelFormat.UNKNOWN); //Don‘t set the preview visibility to GONE or INVISIBLE wm.addView(preview, params); }
圖片縮放與裁剪
現在幾乎就可以了,pictureCallback是拍照成功後的回調,我們將在此回調中做一些對圖片資料的處理工作。
首先,圖片資料以位元組數組的形式返回的,原型如下:
@Override public void onPictureTaken(byte[] data, Camera camera) {}
我們要將數群組轉換成bitmap,用BitmapFactory.decodeByteArray方法即可。
但你會發現,圖片是向左倒著顯示的,不要急,我們要把它正過來。此時我們要用matrix.postRotate方法配上Bitmap.createBitmap建立新的bitmap。
談談縮放吧,參考Bitmap.createScaledBitmap方法即可。
那麼裁剪呢?建立一個Rect,注意它要在整個bitmap中,比如程式中我將進行距離原圖1/4處裁剪。
這還沒完,做完這些工作,我會將這三個bitmap存成三張圖片。注意在做bitmap的compress操作時,第二個參數quality很重要,圖片的品質越高,佔用的空間越大。
好了,下面是代碼,請參考:
private Camera.PictureCallback pictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { Log.d(TAG, "onPictureTaken"); if(null == data){ return; } Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); camera.stopPreview(); Matrix matrix = new Matrix(); matrix.postRotate((float) 90.0); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); Log.d(TAG, "original bitmap width: " + bitmap.getWidth() + " height: " + bitmap.getHeight()); Bitmap sizeBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth()/3, bitmap.getHeight()/3, true); Log.d(TAG,"size bitmap width "+sizeBitmap.getWidth()+" height "+sizeBitmap.getHeight()); //裁剪bitmap int leftOffset = (int)(sizeBitmap.getWidth() * 0.25); int topOffset = (int)(sizeBitmap.getHeight() * 0.25); Rect rect = new Rect(leftOffset, topOffset, sizeBitmap.getWidth() - leftOffset, sizeBitmap.getHeight() - topOffset); Bitmap rectBitmap = Bitmap.createBitmap(sizeBitmap, rect.left, rect.top, rect.width(), rect.height()); try { FileOutputStream outputStream = new FileOutputStream(Environment .getExternalStorageDirectory().toString()+"/photoResize.jpg"); sizeBitmap.compress(Bitmap.CompressFormat.JPEG, 30, outputStream); outputStream.close(); FileOutputStream outputStreamOriginal = new FileOutputStream(Environment .getExternalStorageDirectory().toString()+"/photoOriginal.jpg"); bitmap.compress(Bitmap.CompressFormat.JPEG, 20, outputStreamOriginal); outputStreamOriginal.close(); FileOutputStream outputStreamCut = new FileOutputStream(Environment .getExternalStorageDirectory().toString()+"/photoCut.jpg"); rectBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStreamCut); outputStreamCut.close(); Log.d(TAG,"picture saved!"); } catch(Exception e) { e.printStackTrace(); } } };
你需要的許可權:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" />
參考:
http://stackoverflow.com/questions/2386025/taking-picture-from-camera-without-preview
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Android實戰技巧之四十七:不用預覽拍照與圖片縮放剪裁