Android实现身份证拍摄框
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实现身份证拍摄框的更多相关文章
- Android弹出选项框及指示箭头动画选择
Android弹出选项框及指示箭头动画选择 Android原生的Spinner提供了下拉列表选项框,但在一些流行的APP中,原生的Spinner似乎不太受待见,而通常会有下图所示的下拉列表选项框 ...
- Cocos2d-x C++调用Android弹出提示框
转载请注明地址,谢谢.. Cocos2d-x中提供了一个JniHelper类来让我们对Jni进行操作. (PS:弄了一天想自己写代码操作Jni的,但是总是出错,技术差不得不使用Cocos2d-x现成的 ...
- Android 讲述Help提示框
Android 讲述Help提示框 XML/HTML代码 <stringname="help_dialog_text"> <i>Author:fonter. ...
- Android安卓身份证识别SDK
一.Android安卓身份证识别SDK应用背景 这些年,随着互联网金融的极速发展,第三方支付.理财.P2P网贷.征信等APP应用成爆发式的增长,在众多APP中都涉及到对身份证信息的录入,如第三方支付. ...
- Android Spinner 下拉框简单应用 详细注解
目录 Android Spinner 代码部分 Spinner代码介绍 核心代码 说在最后 @ Android Spinner Spinner 提供下拉列表式的输入方式,该方法可以有效节省手机屏幕上的 ...
- Android仿微信拍摄短视频
近期做项目需要添加上传短视频功能,功能设置为类似于微信,点击开始拍摄,设置最长拍摄时间,经过研究最终实现了这个功能,下面就和大家分享一下,希望对你有帮助. 1.视频录制自定义控件: /** * 视频播 ...
- 详细解读Android中的搜索框—— SearchView
以前总是自己写的 今天看看别人做的 本篇讲的是如何用searchView实现搜索框,其实原理和之前的没啥差别,也算是个复习吧. 一.Manifest.xml 这里我用一个activity进行信息的输入 ...
- Android 下拉列表框、文本框、菜单
1.下拉列表框(Spinner) 项目布局 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/andr ...
- android自定义弹出框样式实现
前言: 做项目时,感觉Android自带的弹出框样式比较丑,很多应用都是自己做的弹出框,这里也试着自己做了一个. 废话不说先上图片: 实现机制 1.先自定义一个弹出框的样式 2.自己实现CustomD ...
随机推荐
- RocketMQ学习笔记(一)eclipse版的quickstart
学而时习之,不亦说乎! 自己搭建个学习用的RocketMQ总是很麻烦,需要虚拟机环境,网络,需要安装rocketmq,启动.时间久了再去看,又不知道这个虚拟机是干嘛的了. 直接在eclipse中启动, ...
- 修改docker的port端口映射
原以为修改docker的端口映射只是一条命令就可以搞定,结果发现没那么简单,记录一下以防忘记.1. 查看需要修改的容器,记住container iddocker ps -a2. 停止容器docker ...
- 【Maven学习】maven中依赖的配置详解
根元素project下的dependencies可以包含一个或者多个dependency元素,以声明一个或多个项目依赖.每个依赖可以包含的元素有: groupId,artifactId和version ...
- js中请求数据的$post和$ajax区别(同步和异步问题)
$.post和$.Ajax都为页面上向后台发送请求,请求数据 1.post 因为post默认为异步请求,可是有时候我们会发现,本来要求请求马上出现,可是异步会导致后面突然再执行,这样就出很多问题 2. ...
- Ibatis框架之系统架构
如果用最简洁的话来总结 iBATIS 主要完成那些功能时,我想下面几个代码足够概括. Class.forName("oracle.jdbc.driver.OracleDriver" ...
- Java将Excel的列数以字母表示的字符串转换成数字表示
我们知道,在 Excel 中,行数用数字表示,而列数是用字母表示的(如下图所示),有时候需要把它转换成数字来使用,或者把数字转换成字母.(例如使用POI操作Excel) 下面是转换代码,用来进行字母和 ...
- 虚拟机安装Ubuntu的上网设置(有线网络和无线网络)
(恩,是转的) 虚拟机下ubuntu共享方式上网: 一. 有线网络 在有线网络的条件下,vmware的安装非常简单,上网方式几乎不用怎么设置(默认NAT模式) 如果默认情况下不能上网,则按以下 ...
- IOS9网络请求报错:The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.
今天下载Xcode7试了下,运行项目时报上面的错误,网上查了下原来iOS9引入了新特性App Transport Security (ATS).详情:App Transport Security (A ...
- OpenTLD在VS2012和opencv246编译通过
最近看到了TLD的跟踪视频,觉得很有意思,刚好最近在看行人检测所以就打算下载源码玩一玩,因为源码是Linux版本的(原作者写的是C++和MATLAB的混合编程)C++源码可以在我的博客TLD(一种目标 ...
- SQL Server 两个时间段的差and时间截取到时分
/*--------------------------时间截取到时分-----------------------------------*/ ), ),)--返回2017-11-24 19:25 ...