自定义带取景框的camera
前言:公司項目需求,在圖像信息采集時只采集肩部以上部位的圖片(和我們平時的一寸證件照很像),首先想到的是用第三方的圖片選擇器,他們都自帶裁剪功能,不過每次拍完照后的手動裁剪,結果老大說簡化業務人員的操作,不過這也難不倒無所不能的程序猿,沒有咱們可以new一個(女朋友)。言歸正傳,開啟我們的自定義帶取景框的camera...
轉載鏈接:https://blog.csdn.net/ruancw/article/details/79907677
效果圖:
技術實現:(Activity中實現)
1.SurfaceView
2.Camera
3.自定義矩形取景框view
SurfaceView
介紹:從API中可以看出SurfaceView屬于View的子類,它的功能很強大,它支持OpenGL ES庫,2D和3D的效果,可以制作游戲、視頻等,這里我們用surfaceview和camera實現相機的拍照取景功能。首先,讓我們創建了SurfaceView的類實現SurfaceHolder的CallBack接口,重寫CallBack的3個方法用于監聽SurfaceView的創建、改變、銷毀狀態。
//SurfaceHolder的callback接口 public interface Callback {//對surfaceView的創建狀態的監聽void surfaceCreated(SurfaceHolder var1);//對SurfaceView的狀態改變進行監聽void surfaceChanged(SurfaceHolder var1, int var2, int var3, int var4);//對SurfaceView的銷毀進行監聽void surfaceDestroyed(SurfaceHolder var1); }1.創建SurfaceView
這里我們使用Api自帶的SurfaceView進行相機的預覽(當然你也可以自定義surfaceView),在Activity的onCreate方法中進行初始化SurfaceView。
?
mCameraSurfaceView = (SurfaceView) findViewById(R.id.cameraSurfaceView);2.使用SurfaceView
?
//根據layoutParams設置surfaceView的大小 mCameraSurfaceView.setLayoutParams(new FrameLayout.LayoutParams((int) (height * (h / w)), height));3.獲取SrfaceView的SurfaceHoler
?
mHolder = mCameraSurfaceView.getHolder(); //添加監聽 mHolder.addCallback(this); //設置類型 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);然后就是在三個監聽方法中進行camera的相關的操作。
自定義view(矩形取景框)
剛才已經介紹了顯示camera預覽的surfaceView,那么surfaceView上的取景框該如何實現呢?canvas and paint,沒錯,今天我們就用paint和canvas畫出效果圖的矩形取景框,接下來我們來自定義view。
模塊實現:
a.頂部文字
b.陰影區域
c.矩形取景框
d.四角紅色短線
1.自定義View,繼承自imageView
構造方法:
?
public OverLayerTopView(Context context) {this(context,null,0); }public OverLayerTopView(Context context, AttributeSet attrs) {this(context, attrs,0); }public OverLayerTopView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context); }2.初始化view,實現四個模塊
(1)頂部文字實現
a.初始化畫筆
?
//頂部文字提示信息 wordPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗鋸齒 wordPaint.setColor(Color.WHITE);//字體顏色 wordPaint.setTextAlign(Paint.Align.CENTER);//居中顯示 wordPaint.setStrokeWidth(3f);//畫筆的寬度 wordPaint.setTextSize(45);//字體大小b.onDraw方法中繪畫
注:必須在super.onDraw之前調用
?
/*** 畫文字提示* @param canvas 畫布*/ private void drawTipText(Canvas canvas) {canvas.drawText(TIPS, mCenterRect.centerX(), mCenterRect.top-50, wordPaint); }(2)矩形取景框實現
?
a.初始化畫筆
//中間矩形取景框的邊界 mRectBorderPaint=new Paint(Paint.ANTI_ALIAS_FLAG); mRectBorderPaint.setColor(Color.RED); mRectBorderPaint.setStyle(Paint.Style.STROKE); mRectBorderPaint.setStrokeWidth(5f); mRectBorderPaint.setAlpha(0);//透明度注:setAlpha(int value),value的值越小,透明度越高
b.onDraw方法中繪畫
注:必須在super.onDraw之前調用
//判斷取景框矩形是否為空 if (mCenterRect==null) return; //繪制中間矩形取景框 canvas.drawRect(mCenterRect,mRectBorderPaint);?
(3)陰影區域實現
a.初始化畫筆
//陰影區域的畫筆 mShadePaint=new Paint(Paint.ANTI_ALIAS_FLAG); mShadePaint.setColor(Color.GRAY); mShadePaint.setStyle(Paint.Style.FILL); mShadePaint.setAlpha(100);b.onDraw方法中繪畫
注:必須在super.onDraw之前調用
?
canvas.drawRect(0,0,screenWidth,mCenterRect.top-2,mShadePaint);//頂部 canvas.drawRect(0,mCenterRect.bottom+2,screenWidth,screenHeight,mShadePaint);//左側 canvas.drawRect(0,mCenterRect.top-2,mCenterRect.left-2,mCenterRect.bottom+2,mShadePaint);//下部 canvas.drawRect(mCenterRect.right+2,mCenterRect.top-2,screenWidth,mCenterRect.bottom+2,mShadePaint);//右側(4)四角紅色短線實現
a.初始化畫筆
?
//矩形四角的短線 mLinePaint=new Paint(); mLinePaint.setColor(Color.RED); mLinePaint.setAlpha(150);b.onDraw方法中繪畫
注:必須在super.onDraw之前調用
?
//左下 canvas.drawRect(mCenterRect.left-2,mCenterRect.bottom,mCenterRect.left+50,mCenterRect.bottom+2,mLinePaint);//底部 canvas.drawRect(mCenterRect.left-2,mCenterRect.bottom-50,mCenterRect.left,mCenterRect.bottom,mLinePaint);//左側 //左上 canvas.drawRect(mCenterRect.left-2,mCenterRect.top-2,mCenterRect.left+50,mCenterRect.top,mLinePaint);//頂部 canvas.drawRect(mCenterRect.left-2,mCenterRect.top,mCenterRect.left,mCenterRect.top+50,mLinePaint);//左側 //右上 canvas.drawRect(mCenterRect.right-50,mCenterRect.top-2,mCenterRect.right+2,mCenterRect.top,mLinePaint);//頂部 canvas.drawRect(mCenterRect.right,mCenterRect.top,mCenterRect.right+2,mCenterRect.top+50,mLinePaint);//右側 //右下 canvas.drawRect(mCenterRect.right-50,mCenterRect.bottom,mCenterRect.right+2,mCenterRect.bottom+2,mLinePaint);//右側 canvas.drawRect(mCenterRect.right,mCenterRect.bottom-50,mCenterRect.right+2,mCenterRect.bottom,mLinePaint);//底部3.設置矩形框的大小
?
/*** 設置取景框的矩形區域大小* @param mCenterRect 取景框矩形*/ public void setCenterRect(Rect mCenterRect){this.mCenterRect=mCenterRect;//postInvalidate(); }4.OverLayerTopView的使用(Activity中)
?
// 設置取景框的margin; 距 左 、上 、右、下的 距離 單位是dp mCenterRect = DisplayUtils.createCenterRect(this, new Rect(120, 180, 120, 300)); mOverLayerView.setCenterRect(mCenterRect);這樣我們就實現了取景框的繪制,并能設置你想要的取景框的大小。
!!!重點就是下面的camera的實現了,讓我們繼續吧
Camera
介紹:android framework包括對設備上可用的各種相機及相機功能的支持,在應用中實現拍照和錄制視頻,不過,API 21(Android5.0)中將原來的camera API棄用轉而推薦使用新增的camera 2 API,這是google對camera架構的一個大動作,因為API換了新架構,讓開發者用起來有些難度,本文不對camera 2進行研究。這里使用的還是之前的camera API進行相機拍照實現。接下來我們將在surfaceView的接口方法中對camera進行初始化、設置參數以及資源釋放等。
1.camera的初始化(surfaceCreated的方法中)
?
@Override public void surfaceCreated(SurfaceHolder holder) {openCamera(); }openCamera方法
?
@TargetApi(Build.VERSION_CODES.GINGERBREAD) private void openCamera() {if (!isFrontCamera) {//是否是后置攝像頭//打開相機mCamera = Camera.open();try {//攝像頭畫面顯示在Surface上mCamera.setPreviewDisplay(mHolder);} catch (IOException e) {e.printStackTrace();}} else {//前置攝像頭的操作//獲取攝像頭信息Camera.CameraInfo cameraInfo = new Camera.CameraInfo();//遍歷所有攝像頭信息,查找前置攝像頭for (int i = 0; i < Camera.getNumberOfCameras(); i++) {Camera.getCameraInfo(i, cameraInfo);{//判斷是否是前置攝像頭if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//打開前置攝像頭mCamera = Camera.open(i);isFrontCamera = true;}}}} }2.camera參數設置(surfaceChanged方法中)
?
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {initCamera(); }initCamera方法
?
/*** 照相機參數設置*/ public void initCamera() {if (mCamera != null && !isPreview) {//獲取相機參數Camera.Parameters parameters = mCamera.getParameters();// 設置閃光燈為自動 前置攝像頭時 不能設置if (!isFrontCamera) {parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);}//設置相機預覽及圖片參數setCameraParams(mPoint.x, mPoint.y);//開啟預覽mCamera.startPreview();isPreview = true;}}setCameraparams方法
?
/*** 設置預覽圖片和裁剪圖片的大小* @param width 屏幕寬度* @param height 屏幕高度*/ private void setCameraParams(int width, int height) {Log.i(TAG, "setCameraParams width=" + width + " height=" + height);Camera.Parameters parameters = mCamera.getParameters();// 獲取攝像頭支持的PictureSize列表List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();for (Camera.Size size : pictureSizeList) {Log.i(TAG, "pictureSizeList size.width=" + size.width + " size.height=" + size.height);}//從列表中選取合適的分辨率Camera.Size picSize = getPreviewSize(pictureSizeList, ((float) height / width));Log.i(TAG, "picSize.width=" + picSize.width + " picSize.height=" + picSize.height);// 根據選出的PictureSize重新設置SurfaceView大小float w = picSize.width;float h = picSize.height;parameters.setPictureSize(picSize.width, picSize.height);//根據layoutParams設置surfaceView的大小mCameraSurfaceView.setLayoutParams(new FrameLayout.LayoutParams((int) (height * (h / w)), height));// 獲取攝像頭支持的PreviewSize列表List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();//獲取預覽圖片的大小Camera.Size preSize = getPreviewSize(previewSizeList, ((float) height) / width);if (null != preSize) {Log.i(TAG, "preSize.width=" + preSize.width + " preSize.height=" + preSize.height);parameters.setPreviewSize(preSize.width, preSize.height);}// 設置照片質量parameters.setJpegQuality(100);if (parameters.getSupportedFocusModes().contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 連續對焦模式}//自動對焦mCamera.cancelAutoFocus();// 設置PreviewDisplay的方向,效果就是將捕獲的畫面旋轉多少度顯示mCamera.setDisplayOrientation(90);//設置參數(不設置不會有效果)mCamera.setParameters(parameters);}getPreviewSize方法:
?
/*** 從列表中選取合適的分辨率* 默認w:h = 4:3*/ private Camera.Size getPreviewSize(List<Camera.Size> pictureSizeList, float screenRatio) {Camera.Size result = null;for (Camera.Size size : pictureSizeList) {float currentRatio = ((float) size.width) / size.height;if (currentRatio - screenRatio == 0) {result = size;break;}}if (null == result) {for (Camera.Size size : pictureSizeList) {float curRatio = ((float) size.width) / size.height;if (curRatio == 4f / 3) {// 默認w:h = 4:3result = size;break;}}}return result; }3.釋放camera(surfaceDestoryed方法中)
?
@Override public void surfaceDestroyed(SurfaceHolder holder) {// 當holder被回收時 釋放硬件releaseCamera(); }releaseCamera方法:
?
/*** 釋放camera資源*/ private void releaseCamera() {if (mCamera != null) {if (isPreview) {//停止camera的預覽mCamera.stopPreview();}//釋放相機資源mCamera.release();//相機設置為nullmCamera = null;}isPreview = false; }重點來啦,讓我們來拍照吧
4.camera拍照
(1)設置camera的回調接口:Camera.pictureCallBack
?
/*** 相機拍照的返回接口*/ private Camera.PictureCallback jpeg = new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {isTake = false;if (data == null) return;// 獲取拍照回調的圖片數據。Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opt);Bitmap bm;//獲取相機的方向(橫向或縱向)if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {//矩陣轉換Matrix matrix = new Matrix();matrix.setRotate(90, 0.1f, 0.1f);bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, false);if (isFrontCamera) {//前置攝像頭旋轉圖片270度。matrix.setRotate(270);bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);}} else {bm = bitmap;}//判斷矩形取景框是否為空if (mCenterRect != null) {//獲取取景框大小的bitmapbitmap = BitmapUtils.getRectBitmap(mCenterRect, bm, mPoint);}//圖片縮放到341x481大小Bitmap scaleBitmap=Bitmap.createScaledBitmap(bitmap,341,481,false);//將以矩形取景框大小的圖片保存到sd卡if (SdcardUtils.existSdcard()) {SdcardUtils.saveBitmap2SD(scaleBitmap, filesDir, imageName);//SdcardUtils.saveBitmap2SD(bitmap, filesDir, imageName);} else ToastUtil.showT(CameraActivity.this, "未檢測到SD卡");//釋放圖片資源,防止OOMBitmapUtils.recycleBitmap(bm);//顯示預覽圖ivPreview.setImageBitmap(bitmap);//釋放相機資源if (mCamera != null) {mCamera.stopPreview();mCamera.startPreview();isPreview = true;}//拍照成功返回setResult(-1);finish();} };(2)點擊拍照
注:camera調用takePicture方法前要設置相機參數,不然拍照次數多了會出現閃退
?
// 設置相機參數 setCameraParams(mPoint.x,mPoint.y); //拍照 mCamera.takePicture(null, null, jpeg);jpeg就是我們剛才設置的接口回調名稱
注:文中之前設置的自動對焦在華為等部分機型上會出現閃退,因為文中是在預覽之前就讓其對焦了,源碼中已經更改了自動對焦的位置在預覽顯示之后。
源碼地址:https://github.com/ruancw/CustomCamerDemo
完結!!!
總結
以上是生活随笔為你收集整理的自定义带取景框的camera的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java socket编程 CPU占用率
- 下一篇: 图像处理:Tiler制作你的专属卡通头像