作者: Jooyer, 时间: 2019.01.08

Github地址,欢迎点赞,fork

现在部分APP都有一个身份认证,一般需要身份证正面,反面,在度娘那也有很多,我发现他们在属性配置上略少了一些,所以特意写了一个!,四周线条颜色,间距,粗细都有属性设置!

本次代码也是比较简单,就一个系统的 SurfaceView 和 一个自定义的View, 我就直接贴源码,如果还有不清楚的,可以留言或者看github

接下来我们依次讲解:

  1. CameraSufaceView
  2. CameraMaskView
  3. 属性及默认值

首先,看看 CameraSufaceView,必要的注释都有

package cn.molue.jooyer.masker;import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Environment;
import android.support.constraint.ConstraintLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
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;/*** @Date 2018/12/29* @Desc 相机预览界面*/
public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback {private static final String TAG = "CameraSurfaceView";private SurfaceHolder holder;private Camera mCamera;private Rect mCenterMarkRect;private int mScreenWidth;private int mScreenHeight;private File mFile;private OnPathChangedListener 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);getScreenMetrix(context);initView();}//拿到手机屏幕大小private void getScreenMetrix(Context context) {mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;mScreenHeight = context.getResources().getDisplayMetrics().heightPixels;}private void initView() {//获得surfaceHolder引用holder = getHolder();// 屏幕常亮holder.setKeepScreenOn(true);//为SurfaceView的句柄添加一个回调函数holder.addCallback(this);//        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置类型}// 在surface创建后立即被调用。在开发自定义相机时,可以通过重载这个函数调用camera.open()、camera.setPreviewDisplay(),// 来实现获取相机资源、连接camera和surface等操作@Overridepublic void surfaceCreated(SurfaceHolder holder) {Log.i(TAG, "surfaceCreated");if (mCamera == null) {mCamera = Camera.open();try {// 设置用于显示拍照影像的SurfaceHolder对象mCamera.setPreviewDisplay(holder);} catch (IOException e) {e.printStackTrace();}}}// 可以通过重载这个函数调用camera.startPreview来开启相机预览,使得camera预览帧数据可以传递给surface,从而实时显示相机预览图像@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {setCameraParams(mScreenWidth, mScreenHeight);mCamera.startPreview();}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {mCamera.stopPreview();//停止预览mCamera.release();//释放相机资源mCamera = null;}@Overridepublic void onAutoFocus(boolean success, Camera Camera) {}private void setCameraParams(int width, int height) {Log.i(TAG, "------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;Log.i(TAG, "----------picSize.width=" + picSize.width + " --- picSize.height=" + picSize.height);// 设置捕获图片尺寸parameters.setPictureSize(picSize.width, picSize.height);this.setLayoutParams(new ConstraintLayout.LayoutParams((int) (height * (h / w)), height));// 获取摄像头支持的PreviewSize列表List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();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(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 连续对焦模式}mCamera.cancelAutoFocus();//自动对焦。mCamera.setParameters(parameters);// 计算摄像头方向Camera.CameraInfo info =new Camera.CameraInfo();Camera.getCameraInfo(0, info);int rotation = ((Activity) getContext()).getWindowManager().getDefaultDisplay().getRotation();int degrees = 0;switch (rotation) {case Surface.ROTATION_0:degrees = 0;break;case Surface.ROTATION_90:degrees = 90;break;case Surface.ROTATION_180:degrees = 180;break;case Surface.ROTATION_270:degrees = 270;break;}int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360;  // compensate the mirror} else {  // back-facingresult = (info.orientation - degrees + 360) % 360;}// 设置PreviewDisplay的方向,效果就是将捕获的画面旋转多少度显示mCamera.setDisplayOrientation(result);}/*** 从列表中选取合适的分辨率* 默认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() {}};// 获得没有压缩过的图片数据private Camera.PictureCallback raw = new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera Camera) {}};//创建jpeg图片回调数据对象private Camera.PictureCallback jpeg = new Camera.PictureCallback() {private Bitmap bitmap;@Overridepublic void onPictureTaken(byte[] data, Camera Camera) {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);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();}bos = new BufferedOutputStream(new FileOutputStream(mFile));if (null == mCenterMarkRect) {throw new NullPointerException("mCenterMarkRect is not null");}Bitmap sizeBitmap = Bitmap.createScaledBitmap(bitmap,mScreenWidth, mScreenHeight, true);// 截取bm = Bitmap.createBitmap(sizeBitmap,mCenterMarkRect.left, mCenterMarkRect.top,mCenterMarkRect.right - mCenterMarkRect.left,mCenterMarkRect.bottom - mCenterMarkRect.top);bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);//将图片压缩到流中} else {Toast.makeText(getContext(), "没有检测到内存卡", 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) {onPathChangedListener.onValueChange(mFile.getPath());}} catch (IOException e) {e.printStackTrace();}}}};/*** 拍照*/public void takePicture() {//设置参数,并拍照setCameraParams(mScreenWidth, mScreenHeight);// 当调用camera.takePiture方法后,camera关闭了预览,这时需要调用startPreview()来重新开启预览mCamera.takePicture(shutter, null, jpeg);}public void setAutoFocus() {mCamera.autoFocus(this);}public void setCenterMarkRect(Rect centerMarkRect) {this.mCenterMarkRect = centerMarkRect;}public interface OnPathChangedListener {void onValueChange(String path);}}复制代码

然后我们来瞧瞧 CameraMaskView ,其实这里就是将四周挡住,留下中间位置作为拍照的具体内容

package cn.molue.jooyer.masker;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;/*** @Date 2018/12/29* @Time 11:37*/
public class CameraMaskView extends View {// 默认如果方框布局中时距离上面尺寸private static final int MASK_MARGIN = 100;// 默认线宽private static final int LINE_WIDTH = 5;// 默认线长private static final int LINE_LENGTH = 40;// 默认线外边距private static final int LINE_MARGIN = 10;// 默认线内边距private static final int LINE_PADDING = 5;// 默认提示文本private static final String TEXT = "请将身份证置于此框内";// 控件的宽高private int viewWidth;private int viewHeight;// 中间透明区域(方框)宽高public int rectWidth;public int rectHeight;// 提示文本private String mTipText = "";// 文本颜色private int mTipTextColor = 0;// 文本大小private int mTipTextSize = 0;// 文本距离底部 遮罩框 间隔private int mTipMargin = 0;// 中间透明区域(方框)坐标private Rect mCenterMarkRect = new Rect();// 遮罩颜色private int mMaskColor = 0;// 遮罩是居中,还是居上private int mMaskPosition = 0;private int mMaskMargin = 0;// 四周线条长度private int mLineLength;// 四周线条宽度(厚度)private int mLineWidth;private int mLineColor = 0;private int mLinePadding = 0;private int mLineMargin = 0;// 文字画笔private Paint maskPaint;// 线条画笔private Paint linePaint;// 文字画笔private Paint wordPaint;// 计算提示文本的位置的private Rect rect;// 文本基线private int baseline;public CameraMaskView(Context context, AttributeSet attrs) {super(context, attrs);parse(context, attrs);init(context);}private void parse(Context context, AttributeSet attrs) {TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CameraMaskView);mMaskMargin = (int) array.getDimension(R.styleable.CameraMaskView_cmv_mask_margin, MASK_MARGIN);mMaskPosition = array.getInt(R.styleable.CameraMaskView_cmv_position_flag, 0);mMaskColor = array.getColor(R.styleable.CameraMaskView_cmv_mask_color, 0xa0000000);mTipText = array.getString(R.styleable.CameraMaskView_cmv_tip_text);if (null == mTipText || mTipText.isEmpty()) {mTipText = TEXT;}mTipTextColor = array.getColor(R.styleable.CameraMaskView_cmv_tip_color, 0xFFFFFFFF);mTipTextSize = (int) array.getDimension(R.styleable.CameraMaskView_cmv_tip_size, sp2px(context, 14));mTipMargin = (int) array.getDimension(R.styleable.CameraMaskView_cmv_tip_margin, dp2px(context, 20));mLineColor = array.getColor(R.styleable.CameraMaskView_cmv_line_color, 0xFF85D28A);mLineWidth = (int) array.getDimension(R.styleable.CameraMaskView_cmv_line_width, dp2px(context, LINE_WIDTH));mLineLength = (int) array.getDimension(R.styleable.CameraMaskView_cmv_line_length, dp2px(context, LINE_LENGTH));mLinePadding = (int) array.getDimension(R.styleable.CameraMaskView_cmv_line_padding, dp2px(context, LINE_PADDING));mLineMargin = (int) array.getDimension(R.styleable.CameraMaskView_cmv_line_margin, dp2px(context, LINE_MARGIN));array.recycle();}private void init(Context context) {viewWidth = context.getResources().getDisplayMetrics().widthPixels;viewHeight = context.getResources().getDisplayMetrics().heightPixels;rectWidth = viewWidth - dp2px(context, 2 * mLineMargin);// 身份证 长度85.6mm,宽度54mmrectHeight = (int) (rectWidth * 54 / 85.6);if (1 == mMaskPosition) {mCenterMarkRect.left = (viewWidth - rectWidth) / 2;mCenterMarkRect.top = mMaskMargin;mCenterMarkRect.right = mCenterMarkRect.left + rectWidth;mCenterMarkRect.bottom = mMaskMargin + rectHeight;} else {mCenterMarkRect.left = (viewWidth - rectWidth) / 2;mCenterMarkRect.top = (viewHeight - rectHeight) / 2;mCenterMarkRect.right = mCenterMarkRect.left + rectWidth;mCenterMarkRect.bottom = mCenterMarkRect.top + rectHeight;}linePaint = new Paint();linePaint.setAntiAlias(true);linePaint.setColor(mLineColor);linePaint.setStyle(Paint.Style.STROKE);linePaint.setStrokeWidth(mLineWidth);// 设置线宽linePaint.setAlpha(255);maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);maskPaint.setColor(mMaskColor);wordPaint = new Paint(Paint.ANTI_ALIAS_FLAG);wordPaint.setColor(mTipTextColor);wordPaint.setTextSize(mTipTextSize);rect = new Rect(mCenterMarkRect.left, mCenterMarkRect.top - mTipTextSize - mTipMargin,mCenterMarkRect.right, mCenterMarkRect.top - mTipMargin);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);if (1 == mMaskPosition) {// 顶部蒙层canvas.drawRect(0, 0, viewWidth, mMaskMargin, maskPaint);// 左侧canvas.drawRect(0, mMaskMargin, (viewWidth - rectWidth) / 2, mMaskMargin + rectHeight, maskPaint);// 右侧canvas.drawRect(viewWidth - (viewWidth - rectWidth) / 2, mMaskMargin, viewWidth, mMaskMargin + rectHeight, maskPaint);// 底部蒙层canvas.drawRect(0, mMaskMargin + rectHeight, viewWidth, viewHeight, maskPaint);} else {// 顶部蒙层canvas.drawRect(0, 0, viewWidth, viewHeight / 2 - rectHeight / 2, maskPaint);// 左侧canvas.drawRect(0, viewHeight / 2 - rectHeight / 2, (viewWidth - rectWidth) / 2, viewHeight / 2 + rectHeight / 2, maskPaint);// 右侧canvas.drawRect(viewWidth - (viewWidth - rectWidth) / 2, viewHeight / 2 - rectHeight / 2, viewWidth, viewHeight / 2 + rectHeight / 2, maskPaint);// 底部蒙层canvas.drawRect(0, viewHeight / 2 + rectHeight / 2, viewWidth, viewHeight, maskPaint);}// 绘制文字canvas.drawText(mTipText, rect.centerX(), baseline, wordPaint);// 绘制四个角canvas.drawLine(mCenterMarkRect.left - dp2px(getContext(), mLinePadding) - mLineWidth / 2.0F, mCenterMarkRect.top - dp2px(getContext(), mLinePadding),mCenterMarkRect.left + mLineLength, mCenterMarkRect.top - dp2px(getContext(), mLinePadding), linePaint);canvas.drawLine(mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.top - dp2px(getContext(), mLinePadding),mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.top + mLineLength, linePaint);canvas.drawLine(mCenterMarkRect.right - mLineLength, mCenterMarkRect.top - dp2px(getContext(), mLinePadding),mCenterMarkRect.right + dp2px(getContext(), mLinePadding) + mLineWidth / 2.0F, mCenterMarkRect.top - dp2px(getContext(), mLinePadding), linePaint);canvas.drawLine(mCenterMarkRect.right + dp2px(getContext(), mLinePadding), mCenterMarkRect.top - dp2px(getContext(), mLinePadding),mCenterMarkRect.right + dp2px(getContext(), mLinePadding), mCenterMarkRect.top + mLineLength, linePaint);canvas.drawLine(mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom - mLineLength,mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding) + mLineWidth / 2.0F, linePaint);canvas.drawLine(mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding),mCenterMarkRect.left + mLineLength, mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding), linePaint);canvas.drawLine(mCenterMarkRect.right - mLineLength, mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding),mCenterMarkRect.right + dp2px(getContext(), mLinePadding) + mLineWidth / 2.0F, mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding), linePaint);canvas.drawLine(mCenterMarkRect.right + dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom - mLineLength,mCenterMarkRect.right + dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding), linePaint);}public Rect getCenterMarkRect() {return mCenterMarkRect;}private int dp2px(Context context, float dpValue) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());}private int sp2px(Context context, int dpValue) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, dpValue, context.getResources().getDisplayMetrics());}}
复制代码

来看看自定义的属性(xml文件)

里面就是一大堆自定义的属性,具体含义往下看

   <?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="CameraMaskView"><!-- 蒙层颜色 --><attr name="cmv_mask_color" format="color|reference" /><!-- 提示文本 --><attr name="cmv_tip_text" format="string|reference" /><!-- 文字颜色 --><attr name="cmv_tip_color" format="color|reference" /><!-- 文字大小 --><attr name="cmv_tip_size" format="integer|dimension" /><!-- 文字底部距离方框的间隔 --><attr name="cmv_tip_margin" format="integer|dimension" /><!-- 边线颜色 --><attr name="cmv_line_color" format="color|reference" /><!-- 边线宽度(厚度) --><attr name="cmv_line_width" format="integer|dimension" /><!-- 边线长度 --><attr name="cmv_line_length" format="integer|dimension" /><!-- 边线与内部透明方框的间隔 --><attr name="cmv_line_padding" format="integer|dimension" /><!-- 边线与外面手机屏幕边框的间隔 --><attr name="cmv_line_margin" format="integer|dimension"/><!-- 默认方框是剧中的 --><attr name="cmv_position_flag"><enum name="center" value="0"/><enum name="margin" value="1"/></attr><!-- 只有当设置了 cmv_position_flag = margin 才有效 --><attr name="cmv_mask_margin" format="integer|dimension"/></declare-styleable></resources>
复制代码

属性虽然有些多,但是大都都见名知意.

最后来看看在 Activity 布局中的用法

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".CameraActivity"><cn.molue.jooyer.masker.CameraSurfaceViewandroid:id="@+id/camera_surface_view"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"/><cn.molue.jooyer.masker.CameraMaskViewandroid:id="@+id/camera_rect_view"android:layout_width="0dp"android:layout_height="0dp"app:cmv_line_color="#85D28A"app:cmv_line_length="40dp"app:cmv_line_margin="10dp"app:cmv_line_padding="2dp"app:cmv_line_width="2dp"app:cmv_mask_color="#FF76635A"app:cmv_mask_margin="80dp"app:cmv_position_flag="margin"app:cmv_tip_color="#FFFFFF"app:cmv_tip_margin="40dp"app:cmv_tip_size="15sp"app:cmv_tip_text="请将身份证头像面置于此框内"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"/><Viewandroid:id="@+id/view_background"android:layout_width="0dp"android:layout_height="123dp"android:background="#FF2F2A28"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"/><TextViewandroid:id="@+id/text_view_cancel"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="33dp"android:layout_marginEnd="50dp"android:padding="18dp"android:gravity="center_vertical"android:text="取消"android:textColor="#FFF"android:textSize="14sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintRight_toLeftOf="@id/image_view_take"/><ImageViewandroid:id="@+id/image_view_take"android:layout_width="60dp"android:layout_height="60dp"android:background="@drawable/camera_take_background"android:layout_marginBottom="30dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"/></android.support.constraint.ConstraintLayout>
复制代码

这个就不解释了,最后看看 Activity中的操作

package cn.molue.jooyer.masker;import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;/*** @Author Jooyer* @Date 2018/01/08* @Time 14.27*/
public class CameraActivity extends AppCompatActivity {private CameraSurfaceView cameraSurfaceView;private CameraMaskView cameraRectView;private TextView tvCancel;private ImageView ivTake;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_camera);findView();initListener();}private void findView() {tvCancel = findViewById(R.id.text_view_cancel);ivTake = findViewById(R.id.image_view_take);cameraSurfaceView = findViewById(R.id.camera_surface_view);cameraRectView = findViewById(R.id.camera_rect_view);}@Overridepublic void onWindowFocusChanged(boolean hasFocus) {super.onWindowFocusChanged(hasFocus);// 设置中间预览大小cameraSurfaceView.setCenterMarkRect(cameraRectView.getCenterMarkRect());}private void initListener() {tvCancel.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {finish();}});ivTake.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {cameraSurfaceView.takePicture();}});cameraSurfaceView.setOnPathChangedListener(new CameraSurfaceView.OnPathChangedListener() {@Overridepublic void onValueChange(String path) {Toast.makeText(CameraActivity.this, path, Toast.LENGTH_SHORT).show();}});}
}
复制代码

记得在使用时申请相机和存储权限

喜欢记得点赞,收藏,转发哈!

参考地址: blog.csdn.net/github_3784… ,感谢大佬指引!

Android 最简单的自定义证件照Mask之一相关推荐

  1. Android 最简单的自定义MenuItem之一

    作者: Jooyer, 时间: 2018.10.08 Github地址,欢迎点赞,fork 其实本篇和上一篇 Toolbar 是一起的,因为很多时候我们会自定义一个左边图片文本,右边文本图片的Menu ...

  2. android 文件简单的自定义加密和解密

    在android或其他项目中常常会下载和上传文件,为了这些文件的安全我们与服务器统一加密的key,即可进行加密解密文件. 代码: /** * 文件file进行加密解密 * * @param fileU ...

  3. Android PC投屏简单尝试- 自定义协议章(Socket+Bitmap)

    代码地址 :https://github.com/deepsadness/MediaProjectionDemo 效果预览 投屏效果预览 简单说明: 使用Android MediaProjection ...

  4. Android自定义View(1)——初步实现简单的自定义View

    已经有一段时间没有给大家更新博客了,貌似自从学校的实训一开始就一直没有心思去学新的东西和写博客,因为这段时间一直都有很多事情要忙,而且笔者马上就要开始实习工作了,可能心理上也是有一定的压力哈哈.现在事 ...

  5. android简单的自定义涂鸦控件

    简单的自定义涂鸦控件,没有写自定义属性 java代码中找到view后直接setBitmap(Bitmap bitmap)后就可以使用了 提供清除方法clear() 保存可以参考另一篇view转bitm ...

  6. android 夜间模式代码,Android 超简单的夜间模式如何实现?

    原标题:Android 超简单的夜间模式如何实现? 本文作者 作者: 唐子玄 实现夜间模式有很多种方式,经过多次尝试,算是找到了一种性价比较高的方式. 1 主题方式 这是最正统的方式,但工作量巨大,因 ...

  7. android 自定义图片容器,Android应用开发中自定义ViewGroup视图容器的教程

    一.概述在写代码之前,我必须得问几个问题: 1.ViewGroup的职责是啥?ViewGroup相当于一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性, ...

  8. android仿微信的activity平滑水平切换动画,Android实现简单底部导航栏 Android仿微信滑动切换效果...

    Android实现简单底部导航栏 Android仿微信滑动切换效果 发布时间:2020-10-09 19:48:00 来源:脚本之家 阅读:96 作者:丶白泽 Android仿微信滑动切换最终实现效果 ...

  9. android Glide简单使用

    今天,简单讲讲Android里Glide的简单使用. Android框架系列: 一.android EventBus的简单使用 二.android Glide简单使用 对于Glide这个加载图片的框架 ...

最新文章

  1. 二叉树中序遍历-递归与非递归
  2. ce修改器传奇刷元宝_真原始传奇刷元宝方法 不封号刷元宝技巧
  3. QT每日一练day24:绘画事件
  4. 大话IT职场之搞技术是青春饭吗?
  5. 程序设计导引及在线实践之大整数除法
  6. 黑莓BlackBerry手机刷机ROM常见问题
  7. python ssim代码
  8. ssb的matlab仿真,单边带调制(SSB调制)的理论基础和MATLAB仿真
  9. 单调栈及单调栈的应用
  10. halcon 20.11.02 深度学习语义分割例程报错
  11. 进程同步机制四大基本准则
  12. 企业微信(H5打开)调用微信小程序
  13. FairGuard游戏Lua加密方案解析
  14. 在你最穷的时候,是怎么翻身的?
  15. 有限元方法基础-以二维拉普拉斯方程为例(附程序)
  16. [导入]推荐一个好网站
  17. pathon fastapi报错value is not a valid dict (type=type_error.dict)
  18. 本特利前置器330878-50-00
  19. HTML5+CSS3 鼠标悬停3D立体图片效果
  20. Linux 小知识翻译 - 「i386」是什么?

热门文章

  1. 17. 如何通过 SAP ABAP OData $expand 操作在同一个 HTTP 请求中返回多个节点的数据
  2. OS学习笔记-8(清华大学慕课)虚拟存储管理
  3. android银行卡输入密码,android 仿微信添加银行卡时输入支付密码
  4. npm 安装碰到SSL问题
  5. python输入一个正整数、计算其各个位的数字之和_C语言程序设计:编写程序,输入一个正整数,统计该整数的位数并计算其各个数位上的数字之和。...
  6. 为服务器选择固态硬盘的一个优点和缺点
  7. cobble批量装机原理与部署
  8. Python画五角星(turtle初识)
  9. 微信小程序开发案例分享-必背诗:从0到1,从前端到数据库
  10. 高压直流电源为什么要“接地”?如何“接地”?