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);
}
}
