http://blog.csdn.net/lhbtm/article/details/55505668
最近項目需要實現身份證拍照的功能,系統的相機並不能滿足需求,故需要自定義相機,先上效果圖,使用了Camera+ SurfaceView;
布局文件
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.administrator.camerasurfaceview.CameraSurfaceView android:id="@+id/cameraSurfaceView" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.example.administrator.camerasurfaceview.CameraTopRectView android:id="@+id/rectOnCamera" android:layout_width="match_parent" android:layout_height="match_parent" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="20px" android:id="@+id/takePic" android:layout_width="80dp" android:layout_height="50dp" android:background="#88427ac7" android:text="拍照" android:textColor="#aaa" /> </RelativeLayout> </FrameLayout>
MainActivity
package com.example.administrator.camerasurfaceview; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { private Button button; private CameraSurfaceView mCameraSurfaceView; private RectOnCamera rectOnCamera; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // requestWindowFeature(Window.FEATURE_NO_TITLE); // // 全屏顯示 // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_main); mCameraSurfaceView = (CameraSurfaceView) findViewById(R.id.cameraSurfaceView); button = (Button) findViewById(R.id.takePic); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mCameraSurfaceView.takePicture(); } }); } // public void autoFocus() { // mCameraSurfaceView.setAutoFocus(); // } }
自定義相機類
package com.example.administrator.camerasurfaceview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.hardware.Camera; import android.os.Environment; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.Toast; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; /** * Created by Administrator on 2017/2/15 0015.//自定義相機 */ public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback { private static final String TAG = "CameraSurfaceView"; private Context mContext; private SurfaceHolder holder; private Camera mCamera; private int mScreenWidth; private int mScreenHeight; private CameraTopRectView topView; public CameraSurfaceView(Context context) { this(context, null); } public CameraSurfaceView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CameraSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; getScreenMetrix(context); topView = new CameraTopRectView(context,attrs); initView(); } //拿到手機屏幕大小 private void getScreenMetrix(Context context) { WindowManager WM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); WM.getDefaultDisplay().getMetrics(outMetrics); mScreenWidth = outMetrics.widthPixels; mScreenHeight = outMetrics.heightPixels; } private void initView() { holder = getHolder();//獲得surfaceHolder引用 holder.addCallback(this); // holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//設置類型 } @Override public void surfaceCreated(SurfaceHolder holder) { Log.i(TAG, "surfaceCreated"); if (mCamera == null) { mCamera = Camera.open();//開啟相機 try { mCamera.setPreviewDisplay(holder);//攝像頭畫面顯示在Surface上 } catch (IOException e) { e.printStackTrace(); } } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i(TAG, "surfaceChanged"); setCameraParams(mCamera, mScreenWidth, mScreenHeight); mCamera.startPreview(); // mCamera.takePicture(null, null, jpeg); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i(TAG, "surfaceDestroyed"); mCamera.stopPreview();//停止預覽 mCamera.release();//釋放相機資源 mCamera = null; holder = null; } @Override public void onAutoFocus(boolean success, Camera Camera) { if (success) { Log.i(TAG, "onAutoFocus success="+success); System.out.println(success); } } private void setCameraParams(Camera camera, 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 = getProperSize(pictureSizeList, ((float) height / width)); if (null == picSize) { Log.i(TAG, "null == picSize"); picSize = parameters.getPictureSize(); } 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); this.setLayoutParams(new FrameLayout.LayoutParams((int) (height*(h/w)), height)); // 獲取攝像頭支持的PreviewSize列表 List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes(); for (Camera.Size size : previewSizeList) { Log.i(TAG, "previewSizeList size.width=" + size.width + " size.height=" + size.height); } Camera.Size preSize = getProperSize(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();//自動對焦。 mCamera.setDisplayOrientation(90);// 設置PreviewDisplay的方向,效果就是將捕獲的畫面旋轉多少度顯示 mCamera.setParameters(parameters); } /** * 從列表中選取合適的分辨率 * 默認w:h = 4:3 * <p>注意:這里的w對應屏幕的height * h對應屏幕的width<p/> */ private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) { Log.i(TAG, "screenRatio=" + 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:3 result = size; break; } } } return result; } // 拍照瞬間調用 private Camera.ShutterCallback shutter = new Camera.ShutterCallback() { @Override public void onShutter() { Log.i(TAG,"shutter"); System.out.println("執行了嗎+1"); } }; // 獲得沒有壓縮過的圖片數據 private Camera.PictureCallback raw = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera Camera) { Log.i(TAG, "raw"); System.out.println("執行了嗎+2"); } }; //創建jpeg圖片回調數據對象 private Camera.PictureCallback jpeg = new Camera.PictureCallback() { private Bitmap bitmap; @Override public void onPictureTaken(byte[] data, Camera Camera) { topView.draw(new Canvas()); BufferedOutputStream bos = null; Bitmap bm = null; if(data != null){ } try { // 獲得圖片 bm = BitmapFactory.decodeByteArray(data, 0, data.length); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { String filePath = "/sdcard/dyk"+System.currentTimeMillis()+".JPEG";//照片保存路徑 // //圖片存儲前旋轉 Matrix m = new Matrix(); int height = bm.getHeight(); int width = bm.getWidth(); m.setRotate(90); //旋轉后的圖片 bitmap = Bitmap.createBitmap(bm, 0, 0, width, height, m, true); System.out.println("執行了嗎+3"); File file = new File(filePath); if (!file.exists()){ file.createNewFile(); } bos = new BufferedOutputStream(new FileOutputStream(file)); // Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, // data.length); Bitmap sizeBitmap = Bitmap.createScaledBitmap(bitmap, topView.getViewWidth(), topView.getViewHeight(), true); bm = Bitmap.createBitmap(sizeBitmap, topView.getRectLeft(), topView.getRectTop(), topView.getRectRight() - topView.getRectLeft(), topView.getRectBottom() - topView.getRectTop());// 截取 bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);//將圖片壓縮到流中 }else{ Toast.makeText(mContext,"沒有檢測到內存卡", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); } finally { try { bos.flush();//輸出 bos.close();//關閉 bm.recycle();// 回收bitmap空間 mCamera.stopPreview();// 關閉預覽 mCamera.startPreview();// 開啟預覽 } catch (IOException e) { e.printStackTrace(); } } } }; public void takePicture(){ //設置參數,並拍照 setCameraParams(mCamera, mScreenWidth, mScreenHeight); // 當調用camera.takePiture方法后,camera關閉了預覽,這時需要調用startPreview()來重新開啟預覽 mCamera.takePicture(null,null, jpeg); } // public void setAutoFocus(){ // mCamera.autoFocus(this); // } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
自定義身份證方框
package com.example.administrator.camerasurfaceview; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.FontMetricsInt; import android.graphics.Paint.Style; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.WindowManager; //import cn.jiazhengye.panda_home.application.BaseApplication; //import cn.jiazhengye.panda_home.common.Constant; //import cn.jiazhengye.panda_home.utils.DisplayUtil; //import cn.jiazhengye.panda_home.utils.LoggerUtil; class CameraTopRectView extends View { private int panelWidth; private int panelHeght; private int viewWidth; private int viewHeight; public int rectWidth; public int rectHeght; private int rectTop; private int rectLeft; private int rectRight; private int rectBottom; private int lineLen; private int lineWidht; private static final int LINE_WIDTH = 5; private static final int TOP_BAR_HEIGHT = 50; private static final int BOTTOM_BTN_HEIGHT = 66; // private static final int TOP_BAR_HEIGHT = Constant.RECT_VIEW_TOP; // private static final int BOTTOM_BTN_HEIGHT = Constant.RECT_VIEW_BOTTOM; private static final int LEFT_PADDING = 10; private static final int RIGHT_PADDING = 10; private static final String TIPS = "請將身份證放入到方框中"; private Paint linePaint; private Paint wordPaint; private Rect rect; private int baseline; public CameraTopRectView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub Activity activity = (Activity) context; WindowManager wm = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); panelWidth = wm.getDefaultDisplay().getWidth();//拿到屏幕的寬 panelHeght = wm.getDefaultDisplay().getHeight();//拿到屏幕的高 //高度不需要dp轉換px,不然整體相機會向上移動一小節 // viewHeight = panelHeght - (int) DisplayUtil.dp2px(activity,TOP_BAR_HEIGHT + BOTTOM_BTN_HEIGHT); viewHeight = panelHeght; //viewHeight,界面的高,viewWidth,界面的寬 viewWidth = panelWidth; /*rectWidth = panelWidth - UnitUtils.getInstance(activity).dip2px( LEFT_PADDING + RIGHT_PADDING);*/ rectWidth = panelWidth - (int) DisplayUtil.dp2px(activity,LEFT_PADDING + RIGHT_PADDING); rectHeght = (int) (rectWidth * 54 / 85.6); // 相對於此view rectTop = (viewHeight - rectHeght) / 2; rectLeft = (viewWidth - rectWidth) / 2; rectBottom = rectTop + rectHeght; rectRight = rectLeft + rectWidth; lineLen = panelWidth / 8; linePaint = new Paint(); linePaint.setAntiAlias(true); linePaint.setColor(Color.rgb(0xdd, 0x42, 0x2f)); linePaint.setStyle(Style.STROKE); linePaint.setStrokeWidth(LINE_WIDTH);// 設置線寬 linePaint.setAlpha(255); wordPaint = new Paint(); wordPaint.setAntiAlias(true); wordPaint = new Paint(Paint.ANTI_ALIAS_FLAG); wordPaint.setStrokeWidth(3); wordPaint.setTextSize(35); rect = new Rect(rectLeft, rectTop - 80, rectRight, rectTop - 10); FontMetricsInt fontMetrics = wordPaint.getFontMetricsInt(); baseline = rect.top + (rect.bottom - rect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top; wordPaint.setTextAlign(Paint.Align.CENTER); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); wordPaint.setColor(Color.TRANSPARENT); canvas.drawRect(rect, wordPaint); //畫蒙層 wordPaint.setColor(0xa0000000); rect = new Rect(0, viewHeight/2+rectHeght/2, viewWidth, viewHeight); canvas.drawRect(rect, wordPaint); rect = new Rect(0, 0, viewWidth, viewHeight/2-rectHeght/2); canvas.drawRect(rect, wordPaint); rect = new Rect(0, viewHeight/2-rectHeght/2, (viewWidth-rectWidth)/2, viewHeight/2+rectHeght/2); canvas.drawRect(rect, wordPaint); rect = new Rect(viewWidth-(viewWidth-rectWidth)/2, viewHeight/2-rectHeght/2, viewWidth, viewHeight/2+rectHeght/2); canvas.drawRect(rect, wordPaint); //重制rect 並畫文字 吧文字置於rect中間 rect = new Rect(rectLeft, rectTop - 80, rectRight, rectTop - 10); wordPaint.setColor(Color.WHITE); canvas.drawText(TIPS, rect.centerX(), baseline, wordPaint); canvas.drawLine(rectLeft, rectTop, rectLeft + lineLen, rectTop, linePaint); canvas.drawLine(rectRight - lineLen, rectTop, rectRight, rectTop, linePaint); canvas.drawLine(rectLeft, rectTop, rectLeft, rectTop + lineLen, linePaint); canvas.drawLine(rectRight, rectTop, rectRight, rectTop + lineLen, linePaint); canvas.drawLine(rectLeft, rectBottom, rectLeft + lineLen, rectBottom, linePaint); canvas.drawLine(rectRight - lineLen, rectBottom, rectRight, rectBottom, linePaint); canvas.drawLine(rectLeft, rectBottom - lineLen, rectLeft, rectBottom, linePaint); canvas.drawLine(rectRight, rectBottom - lineLen, rectRight, rectBottom, linePaint); } public int getRectLeft() { return rectLeft; } public int getRectTop() { return rectTop; } public int getRectRight() { return rectRight; } public int getRectBottom() { return rectBottom; } public int getViewWidth() { return viewWidth; } public int getViewHeight() { return viewHeight; } }
DisplayUtil工具類
package com.example.administrator.camerasurfaceview; import android.content.Context; /** * Created by Administrator on 2017/2/16 0016. */ public class DisplayUtil { /** * 將px裝換成dp,保證尺寸不變 * @param context * @param pxValue * @return */ public static int px2dp(Context context, float pxValue){ float density = context.getResources().getDisplayMetrics().density;//得到設備的密度 return (int) (pxValue/density+0.5f); } public static int dp2px(Context context, float dpValue){ float density = context.getResources().getDisplayMetrics().density; return (int) (dpValue*density+0.5f); } public static int px2sp(Context context, float pxValue){ float scaleDensity = context.getResources().getDisplayMetrics().scaledDensity;//縮放密度 return (int) (pxValue/scaleDensity+0.5f); } public static int sp2px(Context context, float spValue) { float scaleDensity = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue*scaleDensity+0.5f); } }