Android 拍身份證(自定義相機)
阿新 • • 發佈:2019-01-30
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;
@Override
protected 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() {
@Override
public void onClick(View view) {
mCameraSurfaceView.takePicture();
mCameraSurfaceView.setOnPathChangedListener(new CameraSurfaceView.OnPathChangedListener() {
@Override
public void onValueChange(String path) {
LogUtil.d("-----拍攝的照片路徑:"+path);
}
});
}
});
}
@Override
protected void onRestart() {
super.onRestart();
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.takePic:
mCameraSurfaceView.takePicture();
break;
default:
break;
}
}
@Override
public 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);//設定型別
}
@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) {
LogUtil.d("------surfaceChanged");
setCameraParams(mCamera, mScreenWidth, mScreenHeight);
mCamera.startPreview();
// mCamera.takePicture(null, null, jpeg);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
LogUtil.d("------surfaceDestroyed");
mCamera.stopPreview();//停止預覽
mCamera.release();//釋放相機資源
mCamera = null;
holder = null;
}
@Override
public 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:3
result = size;
break;
}
}
}
return result;
}
// 拍照瞬間呼叫
private Camera.ShutterCallback shutter = new Camera.ShutterCallback() {
@Override
public void onShutter() {
LogUtil.d("---------執行到這裡了嗎+1:shutter");
}
};
// 獲得沒有壓縮過的圖片資料
private Camera.PictureCallback raw = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera Camera) {
LogUtil.d("---------執行到這裡了嗎+:raw");
}
};
//建立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)) {
// 圖片儲存前旋轉
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);
}
@Override
protected 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 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(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);
}
@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 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);
}
}
為了向別人、向世界證明自己而努力拼搏,而一旦你真的取得了成績,才會明白:人無須向別人證明什麼,只要你能超越自己。