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

Android实现身份证拍摄框的更多相关文章

  1. Android弹出选项框及指示箭头动画选择

     Android弹出选项框及指示箭头动画选择 Android原生的Spinner提供了下拉列表选项框,但在一些流行的APP中,原生的Spinner似乎不太受待见,而通常会有下图所示的下拉列表选项框 ...

  2. Cocos2d-x C++调用Android弹出提示框

    转载请注明地址,谢谢.. Cocos2d-x中提供了一个JniHelper类来让我们对Jni进行操作. (PS:弄了一天想自己写代码操作Jni的,但是总是出错,技术差不得不使用Cocos2d-x现成的 ...

  3. Android 讲述Help提示框

    Android 讲述Help提示框 XML/HTML代码 <stringname="help_dialog_text"> <i>Author:fonter. ...

  4. Android安卓身份证识别SDK

    一.Android安卓身份证识别SDK应用背景 这些年,随着互联网金融的极速发展,第三方支付.理财.P2P网贷.征信等APP应用成爆发式的增长,在众多APP中都涉及到对身份证信息的录入,如第三方支付. ...

  5. Android Spinner 下拉框简单应用 详细注解

    目录 Android Spinner 代码部分 Spinner代码介绍 核心代码 说在最后 @ Android Spinner Spinner 提供下拉列表式的输入方式,该方法可以有效节省手机屏幕上的 ...

  6. Android仿微信拍摄短视频

    近期做项目需要添加上传短视频功能,功能设置为类似于微信,点击开始拍摄,设置最长拍摄时间,经过研究最终实现了这个功能,下面就和大家分享一下,希望对你有帮助. 1.视频录制自定义控件: /** * 视频播 ...

  7. 详细解读Android中的搜索框—— SearchView

    以前总是自己写的 今天看看别人做的 本篇讲的是如何用searchView实现搜索框,其实原理和之前的没啥差别,也算是个复习吧. 一.Manifest.xml 这里我用一个activity进行信息的输入 ...

  8. Android 下拉列表框、文本框、菜单

    1.下拉列表框(Spinner) 项目布局 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/andr ...

  9. android自定义弹出框样式实现

    前言: 做项目时,感觉Android自带的弹出框样式比较丑,很多应用都是自己做的弹出框,这里也试着自己做了一个. 废话不说先上图片: 实现机制 1.先自定义一个弹出框的样式 2.自己实现CustomD ...

随机推荐

  1. Map集合的四种遍历方式(转载)

    import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class TestMap { pu ...

  2. java反射机制_读取properties

    代码: import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 反射特点: ...

  3. encodeURI、encodeURIComponent、btoa及其应用场景

    escape不编码字符有69个:*,+,-,.,/,@,_,0-9,a-z,A-Z encodeURI不编码字符有82个:!,#,$,&,’,(,),*,+,,,-,.,/,:,;,=,?,@ ...

  4. python-FTP模块

    #!/user/bin/python #coding=utf-8 import ftplib import os import socket HOST = 'ftp.kernel.org' DIRN ...

  5. gcc对open(2)支持重载吗

    在Linux中,如果man -s2 open, 我们看到两种不同的函数原型声明: $ man -s2 open NAME open, creat - open and possibly create ...

  6. js if和switch,==和===

    今天改插件BoxScroll的时候,因为if里面的条件判断多于两个,于是立马想着改写switch.改到一半,忽然记起来JSHint等代码质量检测工具中的一个要求,用===替换==,不用不可靠的强制转型 ...

  7. android studio的jni和so

    1. android studio自己添加代码生成so 代码地址:https://github.com/maogefff/Android-Test-Sample/tree/master/MyJni 参 ...

  8. C# 核心语法-反射(反射类型、方法、构造函数、属性,实现可配置可扩展,完成数据库访问类反射封装)

    反射是.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类.结构.委托.接口和枚举等)的成员和成员的信息.有了反射,即可对每一个类型了如指掌.另外我还可以直接创建对象,即使 ...

  9. Redis常用命令整理

    doc 环境下使用命令:       keys 命令         ?    匹配一个字符         *    匹配任意个(包括0个)字符         []    匹配括号间的任一个字符, ...

  10. “PPT中如何插入和提取swf文件”的解决方案

    解决方案: 如何在PPT中插入swf文件: 1.依次单击Office按钮,Powerpoint选项,勾选“在功能区显示‘开发工具’选项卡”后,确定: 2.单击“开发工具”选项卡中的“其他控件”按钮,然 ...