1. 程式人生 > >Android 拍身份證(自定義相機)

Android 拍身份證(自定義相機)

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

為了向別人、向世界證明自己而努力拼搏,而一旦你真的取得了成績,才會明白:人無須向別人證明什麼,只要你能超越自己。