Android自定义相机拍摄二代身份证。

感谢开源,尊重他人劳动成果,本自定义相机拍照核心逻辑取自云栖社区上Android 手把手带你玩转自定义相机
文章,本文仅仅在此基础上再次封装了返回拍照地址等监听事件,另外拍摄按钮没有做动画效果(待优化)

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><ccqlk.homestay.bean.CameraSurfaceView
        android:id="@+id/cameraSurfaceView"android:layout_width="match_parent"android:layout_height="match_parent" /><ccqlk.homestay.bean.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="60dp"android:id="@+id/takePic"android:layout_width="80dp"android:layout_height="50dp"android:background="@color/colorAccent"android:text="点击拍照"android:textColor="@color/white" /></RelativeLayout></FrameLayout>

activity文件:

public class MCamerActivity extends AppCompatActivity implements View.OnClickListener,RectOnCamera.IAutoFocus{private Button button;private CameraSurfaceView mCameraSurfaceView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_camera);mCameraSurfaceView = (CameraSurfaceView) findViewById(R.id.cameraSurfaceView);button = (Button) findViewById(R.id.takePic);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {mCameraSurfaceView.takePicture();mCameraSurfaceView.setOnPathChangedListener(new CameraSurfaceView.OnPathChangedListener() {@Overridepublic void onValueChange(String path) {LogUtil.d("-----拍摄的照片路径:"+path);}});}});}@Overrideprotected void onRestart() {super.onRestart();}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.takePic:mCameraSurfaceView.takePicture();break;default:break;}}@Overridepublic void autoFocus() {mCameraSurfaceView.setAutoFocus();}
}

CameraSurfaceView类:(其中LogUtil为我自己的日志工具,各位可以换成自己的或者Android自带的LOG)

package ccqlk.homestay.bean;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.Date;
import java.util.List;import ccqlk.homestay.ryx.uitls.LogUtil;/*** Created by LK-RYX on 2018/4/28.*/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;private File mFile;private OnPathChangedListener onPathChangedListener;public OnPathChangedListener getOnPathChangedListener() {return onPathChangedListener;}public void setOnPathChangedListener(OnPathChangedListener onPathChangedListener) {this.onPathChangedListener = onPathChangedListener;}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);//设置类型}@Overridepublic void surfaceCreated(SurfaceHolder holder) {Log.i(TAG, "surfaceCreated");if (mCamera == null) {mCamera = Camera.open();try {mCamera.setPreviewDisplay(holder);//摄像头画面显示在Surface上} catch (IOException e) {e.printStackTrace();}}}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {LogUtil.d("------surfaceChanged");setCameraParams(mCamera, mScreenWidth, mScreenHeight);mCamera.startPreview();
//        mCamera.takePicture(null, null, jpeg);}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {LogUtil.d("------surfaceDestroyed");mCamera.stopPreview();//停止预览mCamera.release();//释放相机资源mCamera = null;holder = null;}@Overridepublic void onAutoFocus(boolean success, Camera Camera) {if (success) {LogUtil.d("------onAutoFocus success=" + success);System.out.println(success);}}private void setCameraParams(Camera camera, int width, int height) {//LogUtil.d("------setCameraParams  width="+width+"  height="+height);Camera.Parameters parameters = mCamera.getParameters();// 获取摄像头支持的PictureSize列表List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();/**从列表中选取合适的分辨率*/Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width));if (null == picSize) {Log.i(TAG, "null == picSize");picSize = parameters.getPictureSize();}// 根据选出的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();Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width);if (null != preSize) {LogUtil.d("----------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:3result = size;break;}}}return result;}// 拍照瞬间调用private Camera.ShutterCallback shutter = new Camera.ShutterCallback() {@Overridepublic void onShutter() {LogUtil.d("---------执行到这里了吗+1:shutter");}};// 获得没有压缩过的图片数据private Camera.PictureCallback raw = new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera Camera) {LogUtil.d("---------执行到这里了吗+:raw");}};//创建jpeg图片回调数据对象private Camera.PictureCallback jpeg = new Camera.PictureCallback() {private Bitmap bitmap;@Overridepublic 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)) {//                  图片存储前旋转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);LogUtil.d("---------执行到这里了吗+3");String photo = "IMMQY/IMG_" + String.valueOf(new Date().getTime() + ".jpg");mFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath(), photo);if (!mFile.getParentFile().exists()) mFile.getParentFile().mkdirs();LogUtil.d("---------自定义照片保存路径:" + mFile.getPath());bos = new BufferedOutputStream(new FileOutputStream(mFile));//                    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();// 开启预览if (onPathChangedListener != null) {LogUtil.d("------监听路径:" + mFile.getPath());onPathChangedListener.onValueChange(mFile.getPath());}} catch (IOException e) {e.printStackTrace();}}}};public void takePicture() {//设置参数,并拍照setCameraParams(mCamera, mScreenWidth, mScreenHeight);// 当调用camera.takePiture方法后,camera关闭了预览,这时需要调用startPreview()来重新开启预览mCamera.takePicture(null, null, jpeg);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}public void setAutoFocus() {mCamera.autoFocus(this);}public interface OnPathChangedListener {void onValueChange(String path);}
}

CameraTopRectView类

package ccqlk.homestay.bean;import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowManager;/*** Created by LK-RYX on 2018/4/28.*/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 stubActivity 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);// 相对于此viewrectTop = (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(Paint.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);Paint.FontMetricsInt fontMetrics = wordPaint.getFontMetricsInt();baseline = rect.top + (rect.bottom - rect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;wordPaint.setTextAlign(Paint.Align.CENTER);}@Overridepublic 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 ccqlk.homestay.bean;import android.content.Context;/*** Created by LK-RYX on 2018/4/28.*/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);}
}

再次感谢前辈们的奉献,本文核心代码原文地址:https://yq.aliyun.com/articles/26706,该文章分析的很透彻!


为了向别人、向世界证明自己而努力拼搏,而一旦你真的取得了成绩,才会明白:人无须向别人证明什么,只要你能超越自己。

Android 拍身份证(自定义相机)相关推荐

  1. Android之给自定义相机增加贴纸

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/51706520 本文出自:[顾林海的博客] ##前言 给自己的APP增加 ...

  2. android官网自定义相机,android native.js camera自定义相机

    //js代码 //用代码渲染页面 let activity = plus.android.runtimeMainActivity(); let SurfaceView = plus.android.i ...

  3. Android之应用自定义相机拍照并且对拍照文字(英文)进行识别

    本程序的内容如题所示,可以通过实际程况对识别出来的英文进行翻译,参照我另外的英文词典翻译例子.另外,由于导入了外部的包,使得项目比较大,这里我只是上传了代码,而对于项目的实体配置则没有.如果有需要整个 ...

  4. Android 自定义相机 身份证拍照 自定义身份证相机

    项目中需要用到拍摄身份证,拍完照片后直接拿到和身份证比例一致的图片,做成功的结果如下:    拍完照后直接拿到裁剪好的图本文的核心技术来自: https://yq.aliyun.com/article ...

  5. Android自定义相机拍的照片不清楚解决方案

    最近做一个项目要用到自定义相机,预览的时候挺清晰的,但是拍好之后就变模糊了,弄了大半天才解决. 相机照片的尺寸只有几种固定的,2592×1936,2048×1536,1600×1200,1024×76 ...

  6. Android自定义相机详细讲解

    使用surfaceview自定义相机,同时把自己踩过的坑分享给大家,希望大家有所收获. #写在前面的话 前一阵子负责一个自定义相机进行拍照并在另一个页面进行人脸识别的模块,相机部分需求并不怎么复杂,可 ...

  7. android 自定义相机源码,Android 自定义相机及分析源码

    Android 自定义相机及分析源码 使用Android 系统相机的方法: 要想让应用有相机的action,咱们就必须在清单文件中做一些声明,好让系统知道,如下 action的作用就是声明action ...

  8. Android自定义相机拍照、图片裁剪的实现

    原文:Android自定义相机拍照.图片裁剪的实现 最近项目里面又要加一个拍照搜题的功能,也就是用户对着不会做的题目拍一张照片,将照片的文字使用ocr识别出来,再调用题库搜索接口搜索出来展示给用户,类 ...

  9. 安卓 自定义相机,身份证拍照

    最近项目需要实现身份证拍照的功能,系统的相机并不能满足需求,故需要自定义相机,先上效果图,使用了Camera+ SurfaceView; 布局文件 <?xml version="1.0 ...

最新文章

  1. mySQL教程 第10章 事务和锁
  2. 在方法的形参位置使用@Qualifier注解||@Autowired 与@Resource的区别
  3. JavaScript实现判断位是不是偶数isEven算法(附完整源码)
  4. php冒泡排序和快速排序笔记
  5. java上下文即ServletContext
  6. 计算机常用存储芯片分为内存和什么,计算机类论文范例,与安徽省计算机一级考试宝典相关研究生毕业论文开题报告范文...
  7. 快速上手Linux核心命令(二):关机、重启
  8. 一个服务器端和多个客户端消息互发_python:OSError: [WinError 10022] 提供了一个无效的参数...
  9. Gemini创始人:如果我是GameStop CEO 接下来我会买BTC
  10. Amber Group创始人兼CEO Michael Wu:CBDC将成为更广义、重要的加密资产
  11. 关于java代码中的注释问题。(类中方法的注释,我们一般都要写上这个方法的文档(doc),方法的参数也要有它的文档)
  12. import/export win7中电源计划
  13. 代码整洁之道-编写 Pythonic 代码
  14. python--(点餐--元组)enumerate将索引与值一一对应、 模拟手机通信录、 模拟手机通信录--使用集合
  15. C++一本通题库1021
  16. PostgreSQL12.3——pgAdmin4表格的创建
  17. 第27次CCF-CSP计算机软件能力认证(2022-09-18)
  18. Apollo(阿波罗)架构深度剖析
  19. 福州大学计算机科学非全,福大数计/软件学院2020年招收非全日制专业硕士研究生调剂方案...
  20. MATLAB数据类型——整数

热门文章

  1. VUE常用问题hack修改
  2. 华为在鸿蒙的另一张王牌,麒麟芯成绝唱后,华为终于还是,甩出了另外一张王牌...
  3. EDM初级:什么是EDM邮件营销?EDM营销的定义
  4. c语言何钦铭,C语言 何钦铭
  5. Revit二次开发之绘制钢筋
  6. 【机器学习】补完计划
  7. PLC程序案例一:喷泉电路(采用中间继电器完成分步控制)
  8. opencv基础笔记
  9. iOS 模拟各种网络环境
  10. 企业授权:SOA被低估的经济价值