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