当前位置: 代码迷 >> Android >> Android 辨别二代身份证证件信息与Camera取景
  详细解决方案

Android 辨别二代身份证证件信息与Camera取景

热度:64   发布时间:2016-04-27 23:05:33.0
Android 识别二代身份证证件信息与Camera取景

识别身份证信息需要用到图像处理的知识,无奈水平不够,于是寻找第三方接口,但是找到的都是收费的,后来找到一个叫云脉的产品http://ocr.ccyunmai.com/,发现他可以免费使用15天,但是15天后就是按识别次数收费的,其价格十分昂贵,0.3元/次,对于苦逼的穷屌来说,这真是天价啊。

但是皇天不负有心人,云脉提供了一个在线识别的demo,其地址为http://ocr.ccyunmai.com/idcard/,但是这个地址有什么用呢,别急,作为专业抓数据出身的来说,我们可以利用该地址进行识别,而且不费一分钱。先打开该网址看看长什么样

这里写图片描述

我们利用云脉提供的一张测试图片上传进行测试

这里写图片描述

在上传前,记得打开开发者模式,Chrome里按F12,切换到NetWork,点击上传,上传完成后会返回识别结果,找到UploadImg.action

这里写图片描述

点击查看其请求体

这里写图片描述

我们着重看红色方框里的内容,只有我们将这些信息提供给该接口http://ocr.ccyunmai.com/UploadImg.action,只要身份证图片正确,它便会给我们返回识别信息。我们要做的就是用程序模拟这个过程。

请求体里需要传递HostOriginRefererUser-Agent,其直直接从浏览器得到的信息中复制即可,请求方式是POSTPOST的内容分为三部分,一个是callbackurl,其值为/idcard/,一个是action,其值为idcard,还有一个就是上传的文件了,叫做img,其文件名就是我们上传的文件名,这里我的文件是test-idcard.jpg,然后其Content-Typeimage/jpeg,接下来我们来模拟这个过程。

我们使用OkHttp作为网络层,结合之前的文章Android OkHttp文件上传与下载的进度监听扩展进行扩展。

增加gradle依赖

compile 'com.squareup.okhttp:okhttp:2.5.0'compile 'cn.edu.zafu:coreprogress:0.0.1'compile 'org.jsoup:jsoup:1.8.3'

我们看到还依赖了jsoup,其实后续会用到它进行解析返回结果。

需要使用网络进行上传,并且需要文件的读取,增加这两个权限

    <uses-permission android:name="android.permission.INTERNET"/>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

初始化OkHttp,避免超时,设置超时时间

OkHttpClient mOkHttpClient=new OkHttpClient();private void initClient() {    mOkHttpClient.setConnectTimeout(1000, TimeUnit.MINUTES);    mOkHttpClient.setReadTimeout(1000, TimeUnit.MINUTES);    mOkHttpClient.setWriteTimeout(1000, TimeUnit.MINUTES);}

然后我们需要对一个变量进行赋值,让它存储本地的身份证图片,其值为文件路径

private String mPhotoPath="文件路径";

接下来开始构造请求头和POST的信息,上传文件的过程中需要监听进度,所以这里使用了前文所说的库

private void uploadAndRecognize() {    if (!TextUtils.isEmpty(mPhotoPath)){        File file=new File(mPhotoPath);        //构造请求体        RequestBody requestBody = new MultipartBuilder().type(MultipartBuilder.FORM)                .addPart(Headers.of("Content-Disposition", "form-data; name=\"callbackurl\""), RequestBody.create(null, "/idcard/"))                .addPart(Headers.of("Content-Disposition", "form-data; name=\"action\""), RequestBody.create(null, "idcard"))                .addPart(Headers.of("Content-Disposition", "form-data; name=\"img\"; filename=\"idcardFront_user.jpg\""), RequestBody.create(MediaType.parse("image/jpeg"), file))                .build();        //这个是ui线程回调,可直接操作UI        final UIProgressRequestListener uiProgressRequestListener = new UIProgressRequestListener() {            @Override            public void onUIRequestProgress(long bytesWrite, long contentLength, boolean done) {                Log.e("TAG", "bytesWrite:" + bytesWrite);                Log.e("TAG", "contentLength" + contentLength);                Log.e("TAG", (100 * bytesWrite) / contentLength + " % done ");                Log.e("TAG", "done:" + done);                Log.e("TAG", "================================");                //ui层回调                mProgressBar.setProgress((int) ((100 * bytesWrite) / contentLength));            }        };        //构造请求头        final Request request = new Request.Builder()                .header("Host", "ocr.ccyunmai.com")                .header("Origin", "http://ocr.ccyunmai.com")                .header("Referer", "http://ocr.ccyunmai.com/idcard/")                .header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2398.0 Safari/537.36")                .url("http://ocr.ccyunmai.com/UploadImg.action")                .post(ProgressHelper.addProgressRequestListener(requestBody, uiProgressRequestListener))                .build();        //开始请求        mOkHttpClient.newCall(request).enqueue(new Callback() {            @Override            public void onFailure(Request request, IOException e) {                Log.e("TAG", "error");            }            @Override            public void onResponse(Response response) throws IOException {                String result=response.body().string();            }        });    }}

请求成功后在onResponse里会进行回调,局部变量拿到的就是最终的返回结果。

我们在浏览器中查看下返回的信息的源代码,以进一步便于解析识别结果

这里写图片描述

返回结果被包含在一个div中,其classleft,在div中还包含了一层fieldset,识别结果就在这里面,于是,我们对result进行解析,使用的就是前面加入的依赖Jsoup

String result=response.body().string();Document parse = Jsoup.parse(result);Elements select = parse.select("div.left fieldset");Log.e("TAG",select.text());Document parse1 = Jsoup.parse(select.text());StringBuilder builder=new StringBuilder();String name=parse1.select("name").text();String cardno=parse1.select("cardno").text();String sex=parse1.select("sex").text();String folk=parse1.select("folk").text();String birthday=parse1.select("birthday").text();String address=parse1.select("address").text();String issue_authority=parse1.select("issue_authority").text();String valid_period=parse1.select("valid_period").text();builder.append("name:"+name)        .append("\n")        .append("cardno:" + cardno)        .append("\n")        .append("sex:" + sex)        .append("\n")        .append("folk:" + folk)        .append("\n")        .append("birthday:" + birthday)        .append("\n")        .append("address:" + address)        .append("\n")        .append("issue_authority:" + issue_authority)        .append("\n")        .append("valid_period:" + valid_period)        .append("\n");Log.e("TAG", "name:" + name);Log.e("TAG","cardno:"+cardno);Log.e("TAG","sex:"+sex);Log.e("TAG","folk:"+folk);Log.e("TAG","birthday:"+birthday);Log.e("TAG","address:"+address);Log.e("TAG","issue_authority:"+issue_authority);Log.e("TAG","valid_period:"+valid_period);

很简单有木有,所有信息都被抽取出来了,看下Log,看下识别结果是什么。

这里写图片描述

其实识别的还是挺准的。识别是可以识别了,但是我们希望能够自己拍摄照片然后上传识别,就像这样子。

这里写图片描述

这个就涉及到Android Camera和SurfaceView的知识了。在这之前,我们先编写一个自动对焦的管理类。

/** * 自动对焦 * User:lizhangqu(513163535@qq.com) * Date:2015-09-05 * Time: 11:11 */public class AutoFocusManager implements Camera.AutoFocusCallback{    private static final String TAG = AutoFocusManager.class.getSimpleName();    private static final long AUTO_FOCUS_INTERVAL_MS = 2000L;    private static final Collection<String> FOCUS_MODES_CALLING_AF;    static {        FOCUS_MODES_CALLING_AF = new ArrayList<String>(2);        FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);        FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);    }    private boolean stopped;    private boolean focusing;    private final boolean useAutoFocus;    private final Camera camera;    private AsyncTask<?,?,?> outstandingTask;    public AutoFocusManager(Camera camera) {        this.camera = camera;        String currentFocusMode = camera.getParameters().getFocusMode();        useAutoFocus = FOCUS_MODES_CALLING_AF.contains(currentFocusMode);        Log.e(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);        start();    }    @Override    public synchronized void onAutoFocus(boolean success, Camera theCamera) {        focusing = false;        autoFocusAgainLater();    }    private synchronized void autoFocusAgainLater() {        if (!stopped && outstandingTask == null) {            AutoFocusTask newTask = new AutoFocusTask();            try {                newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);                outstandingTask = newTask;            } catch (RejectedExecutionException ree) {                Log.e(TAG, "Could not request auto focus", ree);            }        }    }    /**     * 开始自动对焦     */    public synchronized void start() {        if (useAutoFocus) {            outstandingTask = null;            if (!stopped && !focusing) {                try {                    camera.autoFocus(this);                    focusing = true;                } catch (RuntimeException re) {                    // Have heard RuntimeException reported in Android 4.0.x+; continue?                    Log.e(TAG, "Unexpected exception while focusing", re);                    // Try again later to keep cycle going                    autoFocusAgainLater();                }            }        }    }    private synchronized void cancelOutstandingTask() {        if (outstandingTask != null) {            if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {                outstandingTask.cancel(true);            }            outstandingTask = null;        }    }    /**     * 停止自动对焦     */    public synchronized void stop() {        stopped = true;        if (useAutoFocus) {            cancelOutstandingTask();            // Doesn't hurt to call this even if not focusing            try {                camera.cancelAutoFocus();            } catch (RuntimeException re) {                // Have heard RuntimeException reported in Android 4.0.x+; continue?                Log.e(TAG, "Unexpected exception while cancelling focusing", re);            }        }    }    private final class AutoFocusTask extends AsyncTask<Object,Object,Object> {        @Override        protected Object doInBackground(Object... voids) {            try {                Thread.sleep(AUTO_FOCUS_INTERVAL_MS);            } catch (InterruptedException e) {                // continue            }            start();            return null;        }    }}

其实这个类是从Zxing中提取出来的,其功能就是每隔一段时间进行自动对焦,看代码就能看懂,这里不再累赘。

接下来就是和Camera相关的管理类,这个类也是从Zxing中提取出来进行了精简

/** * Camera管理类 * User:lizhangqu([email protected]) * Date:2015-09-05 * Time: 10:56 */public class CameraManager {    private static final String TAG = CameraManager.class.getName();    private Camera camera;    private Camera.Parameters parameters;    private AutoFocusManager autoFocusManager;    private int requestedCameraId = -1;    private boolean initialized;    private boolean previewing;    /**     * 打开摄像头     *     * @param cameraId 摄像头id     * @return Camera     */    public Camera open(int cameraId) {        int numCameras = Camera.getNumberOfCameras();        if (numCameras == 0) {            Log.e(TAG, "No cameras!");            return null;        }        boolean explicitRequest = cameraId >= 0;        if (!explicitRequest) {            // Select a camera if no explicit camera requested            int index = 0;            while (index < numCameras) {                Camera.CameraInfo cameraInfo = new Camera.CameraInfo();                Camera.getCameraInfo(index, cameraInfo);                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {                    break;                }                index++;            }            cameraId = index;        }        Camera camera;        if (cameraId < numCameras) {            Log.e(TAG, "Opening camera #" + cameraId);            camera = Camera.open(cameraId);        } else {            if (explicitRequest) {                Log.e(TAG, "Requested camera does not exist: " + cameraId);                camera = null;            } else {                Log.e(TAG, "No camera facing back; returning camera #0");                camera = Camera.open(0);            }        }        return camera;    }    /**     * 打开camera     *     * @param holder SurfaceHolder     * @throws IOException IOException     */    public synchronized void openDriver(SurfaceHolder holder)            throws IOException {        Log.e(TAG, "openDriver");        Camera theCamera = camera;        if (theCamera == null) {            theCamera = open(requestedCameraId);            if (theCamera == null) {                throw new IOException();            }            camera = theCamera;        }        theCamera.setPreviewDisplay(holder);        if (!initialized) {            initialized = true;            parameters = camera.getParameters();            parameters.setPreviewSize(800, 600);            parameters.setPictureFormat(ImageFormat.JPEG);            parameters.setJpegQuality(100);            parameters.setPictureSize(800, 600);            theCamera.setParameters(parameters);        }    }    /**     * camera是否打开     *     * @return camera是否打开     */    public synchronized boolean isOpen() {        return camera != null;    }    /**     * 关闭camera     */    public synchronized void closeDriver() {        Log.e(TAG, "closeDriver");        if (camera != null) {            camera.release();            camera = null;        }    }    /**     * 开始预览     */    public synchronized void startPreview() {        Log.e(TAG, "startPreview");        Camera theCamera = camera;        if (theCamera != null && !previewing) {            theCamera.startPreview();            previewing = true;            autoFocusManager = new AutoFocusManager(camera);        }    }    /**     * 关闭预览     */    public synchronized void stopPreview() {        Log.e(TAG, "stopPreview");        if (autoFocusManager != null) {            autoFocusManager.stop();            autoFocusManager = null;        }        if (camera != null && previewing) {            camera.stopPreview();            previewing = false;        }    }    /**     * 打开闪光灯     */    public synchronized void openLight() {        Log.e(TAG, "openLight");        if (camera != null) {            parameters = camera.getParameters();            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);            camera.setParameters(parameters);        }    }    /**     * 关闭闪光灯     */    public synchronized void offLight() {        Log.e(TAG, "offLight");        if (camera != null) {            parameters = camera.getParameters();            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);            camera.setParameters(parameters);        }    }    /**     * 拍照     *     * @param shutter ShutterCallback     * @param raw     PictureCallback     * @param jpeg    PictureCallback     */    public synchronized void takePicture(final Camera.ShutterCallback shutter, final Camera.PictureCallback raw,                                         final Camera.PictureCallback jpeg) {        camera.takePicture(shutter, raw, jpeg);    }}

我们看到上面的截图的取景框是蓝色边框,上面还有一行提示的字,这是个自定义的SurfaceView,我们需要自己去实现绘制逻辑

/** * 边框绘制 * User:lizhangqu([email protected]) * Date:2015-09-04 * Time: 18:03 */public class PreviewBorderView extends SurfaceView implements SurfaceHolder.Callback, Runnable {    private int mScreenH;    private int mScreenW;    private Canvas mCanvas;    private Paint mPaint;    private Paint mPaintLine;    private SurfaceHolder mHolder;    private Thread mThread;    private static final String DEFAULT_TIPS_TEXT = "请将方框对准证件拍摄";    private static final int DEFAULT_TIPS_TEXT_SIZE = 16;    private static final int DEFAULT_TIPS_TEXT_COLOR = Color.GREEN;    /**     * 自定义属性     */    private float tipTextSize;    private int tipTextColor;    private String tipText;    public PreviewBorderView(Context context) {        this(context, null);    }    public PreviewBorderView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public PreviewBorderView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initAttrs(context, attrs);        init();    }    /**     * 初始化自定义属性     *     * @param context Context     * @param attrs   AttributeSet     */    private void initAttrs(Context context, AttributeSet attrs) {        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PreviewBorderView);        try {            tipTextSize = a.getDimension(R.styleable.PreviewBorderView_tipTextSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_TIPS_TEXT_SIZE, getResources().getDisplayMetrics()));            tipTextColor = a.getColor(R.styleable.PreviewBorderView_tipTextColor, DEFAULT_TIPS_TEXT_COLOR);            tipText = a.getString(R.styleable.PreviewBorderView_tipText);            if (tipText == null) {                tipText = DEFAULT_TIPS_TEXT;            }        } finally {            a.recycle();        }    }    /**     * 初始化绘图变量     */    private void init() {        this.mHolder = getHolder();        this.mHolder.addCallback(this);        this.mHolder.setFormat(PixelFormat.TRANSPARENT);        setZOrderOnTop(true);        this.mPaint = new Paint();        this.mPaint.setAntiAlias(true);        this.mPaint.setColor(Color.WHITE);        this.mPaint.setStyle(Paint.Style.FILL_AND_STROKE);        this.mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));        this.mPaintLine = new Paint();        this.mPaintLine.setColor(tipTextColor);        this.mPaintLine.setStrokeWidth(3.0F);        setKeepScreenOn(true);    }    /**     * 绘制取景框     */    private void draw() {        try {            this.mCanvas = this.mHolder.lockCanvas();            this.mCanvas.drawARGB(100, 0, 0, 0);            this.mScreenW = (this.mScreenH * 4 / 3);            Log.e("TAG","mScreenW:"+mScreenW+" mScreenH:"+mScreenH);            this.mCanvas.drawRect(new RectF(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6), this.mPaint);            this.mCanvas.drawLine(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH * 1 / 6 + 50, this.mPaintLine);            this.mCanvas.drawLine(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6 + 50, this.mScreenH * 1 / 6, this.mPaintLine);            this.mCanvas.drawLine(this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH * 1 / 6 + 50, this.mPaintLine);            this.mCanvas.drawLine(this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6 - 50, this.mScreenH * 1 / 6, this.mPaintLine);            this.mCanvas.drawLine(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6 - 50, this.mPaintLine);            this.mCanvas.drawLine(this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6 + 50, this.mScreenH - this.mScreenH * 1 / 6, this.mPaintLine);            this.mCanvas.drawLine(this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6 - 50, this.mPaintLine);            this.mCanvas.drawLine(this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6, this.mScreenH - this.mScreenH * 1 / 6, this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6 - 50, this.mScreenH - this.mScreenH * 1 / 6, this.mPaintLine);            mPaintLine.setTextSize(tipTextSize);            mPaintLine.setAntiAlias(true);            mPaintLine.setDither(true);            float length = mPaintLine.measureText(tipText);            this.mCanvas.drawText(tipText, this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6 + this.mScreenH / 2 - length / 2, this.mScreenH * 1 / 6 - tipTextSize, mPaintLine);            Log.e("TAG", "left:" + (this.mScreenW / 2 - this.mScreenH * 2 / 3 + this.mScreenH * 1 / 6));            Log.e("TAG", "top:" + (this.mScreenH * 1 / 6));            Log.e("TAG", "right:" + (this.mScreenW / 2 + this.mScreenH * 2 / 3 - this.mScreenH * 1 / 6));            Log.e("TAG", "bottom:" + (this.mScreenH - this.mScreenH * 1 / 6));        } catch (Exception e) {            e.printStackTrace();        } finally {            if (this.mCanvas != null) {                this.mHolder.unlockCanvasAndPost(this.mCanvas);            }        }    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        //获得宽高,开启子线程绘图        this.mScreenW = getWidth();        this.mScreenH = getHeight();        this.mThread = new Thread(this);        this.mThread.start();    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        //停止线程        try {            mThread.interrupt();            mThread = null;        } catch (Exception e) {            e.printStackTrace();        }    }    @Override    public void run() {        //子线程绘图        draw();    }}

这里面用到了图形的混合模式PorterDuff.Mode.CLEAR,注意SurfeceView的绘制是可以在子线程中进行的,还有一点就是取景宽外围的整个SurfaceView的宽高比例是4:3,这个和Camera的预览和图像的比例设置的一样,避免图形预览变形。这个类的绘制逻辑并不复杂,只不过绘制的长度等信息需要测量过。

还有几个自定义属性

<resources>    <declare-styleable name="PreviewBorderView">        <attr name="tipText" format="string"/>        <attr name="tipTextColor" format="color|reference"/>        <attr name="tipTextSize" format="dimension"/>    </declare-styleable></resources>

最后剩下的就是预览并拍照的Activity了,里面有几个辅助方法用于获取长宽,然后重置了布局文件里的控件的长宽比例为4:3,并且这个Activity需要给调用者返回结果,返回的数据可能有点大,Bundle传递数据最大不能超过1M,于是这里直接传递保存的文件的路径回去。在onCreate里进行了Intent的获取,获取调用方传来的参数,如果没有传过来,则使用默认值。

里面有两个按钮,一个是拍照的,一个是打开或关闭闪光灯的,设置了事件监听并调用对应的方法,拍照需要传递一个回调,这个回调里面进行了数据的存储与返回调用方结果。其他的都是一些初始化和销毁的动作了,看下源码就知道了。

public class CameraActivity extends AppCompatActivity implements SurfaceHolder.Callback {    private LinearLayout mLinearLayout;    private PreviewBorderView mPreviewBorderView;    private SurfaceView mSurfaceView;    private CameraManager cameraManager;    private boolean hasSurface;    private Intent mIntent;    private static final String DEFAULT_PATH = "/sdcard/";    private static final String DEFAULT_NAME = "default.jpg";    private static final String DEFAULT_TYPE = "default";    private String filePath;    private String fileName;    private String type;    private Button take, light;    private boolean toggleLight;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_camera);        initIntent();        initLayoutParams();    }    private void initIntent() {        mIntent = getIntent();        filePath = mIntent.getStringExtra("path");        fileName = mIntent.getStringExtra("name");        type = mIntent.getStringExtra("type");        if (filePath == null) {            filePath = DEFAULT_PATH;        }        if (fileName == null) {            fileName = DEFAULT_NAME;        }        if (type == null) {            type = DEFAULT_TYPE;        }        Log.e("TAG", filePath + "/" + fileName + "_" + type);    }    /**     * 重置surface宽高比例为3:4,不重置的话图形会拉伸变形     */    private void initLayoutParams() {        take = (Button) findViewById(R.id.take);        light = (Button) findViewById(R.id.light);        take.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                cameraManager.takePicture(null, null, myjpegCallback);            }        });        light.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                if (!toggleLight) {                    toggleLight = true;                    cameraManager.openLight();                } else {                    toggleLight = false;                    cameraManager.offLight();                }            }        });        //重置宽高,3:4        int widthPixels = getScreenWidth(this);        int heightPixels = getScreenHeight(this);        mLinearLayout = (LinearLayout) findViewById(R.id.linearlaout);        mPreviewBorderView = (PreviewBorderView) findViewById(R.id.borderview);        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);        RelativeLayout.LayoutParams surfaceviewParams = (RelativeLayout.LayoutParams) mSurfaceView.getLayoutParams();        surfaceviewParams.width = heightPixels * 4 / 3;        surfaceviewParams.height = heightPixels;        mSurfaceView.setLayoutParams(surfaceviewParams);        RelativeLayout.LayoutParams borderViewParams = (RelativeLayout.LayoutParams) mPreviewBorderView.getLayoutParams();        borderViewParams.width = heightPixels * 4 / 3;        borderViewParams.height = heightPixels;        mPreviewBorderView.setLayoutParams(borderViewParams);        RelativeLayout.LayoutParams linearLayoutParams = (RelativeLayout.LayoutParams) mLinearLayout.getLayoutParams();        linearLayoutParams.width = widthPixels - heightPixels * 4 / 3;        linearLayoutParams.height = heightPixels;        mLinearLayout.setLayoutParams(linearLayoutParams);        Log.e("TAG","Screen width:"+heightPixels * 4 / 3);        Log.e("TAG","Screen height:"+heightPixels);    }    @Override    protected void onResume() {        super.onResume();        /**         * 初始化camera         */        cameraManager = new CameraManager();        SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceview);        SurfaceHolder surfaceHolder = surfaceView.getHolder();        if (hasSurface) {            // activity在paused时但不会stopped,因此surface仍旧存在;            // surfaceCreated()不会调用,因此在这里初始化camera            initCamera(surfaceHolder);        } else {            // 重置callback,等待surfaceCreated()来初始化camera            surfaceHolder.addCallback(this);        }    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        if (!hasSurface) {            hasSurface = true;            initCamera(holder);        }    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        hasSurface = false;    }    /**     * 初始camera     *     * @param surfaceHolder SurfaceHolder     */    private void initCamera(SurfaceHolder surfaceHolder) {        if (surfaceHolder == null) {            throw new IllegalStateException("No SurfaceHolder provided");        }        if (cameraManager.isOpen()) {            return;        }        try {            // 打开Camera硬件设备            cameraManager.openDriver(surfaceHolder);            // 创建一个handler来打开预览,并抛出一个运行时异常            cameraManager.startPreview();        } catch (Exception ioe) {        }    }    @Override    protected void onPause() {        /**         * 停止camera,是否资源操作         */        cameraManager.stopPreview();        cameraManager.closeDriver();        if (!hasSurface) {            SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceview);            SurfaceHolder surfaceHolder = surfaceView.getHolder();            surfaceHolder.removeCallback(this);        }        super.onPause();    }    /**     * 拍照回调     */    Camera.PictureCallback myjpegCallback = new Camera.PictureCallback() {        @Override        public void onPictureTaken(final byte[] data, Camera camera) {            // 根据拍照所得的数据创建位图            final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,                    data.length);            int height = bitmap.getHeight();            int width = bitmap.getWidth();            final Bitmap bitmap1 = Bitmap.createBitmap(bitmap, (width - height) / 2, height / 6, height, height * 2 / 3);            Log.e("TAG","width:"+width+" height:"+height);            Log.e("TAG","x:"+(width - height) / 2+" y:"+height / 6+" width:"+height+" height:"+height * 2 / 3);            // 创建一个位于SD卡上的文件            File path=new File(filePath);            if (!path.exists()){                path.mkdirs();            }            File file = new File(path, type+"_"+fileName);            FileOutputStream outStream = null;            try {                // 打开指定文件对应的输出流                outStream = new FileOutputStream(file);                // 把位图输出到指定文件中                bitmap1.compress(Bitmap.CompressFormat.JPEG,                        100, outStream);                outStream.close();            } catch (Exception e) {                e.printStackTrace();            }            Intent intent = new Intent();            Bundle bundle = new Bundle();            bundle.putString("path", file.getAbsolutePath());            bundle.putString("type", type);            intent.putExtras(bundle);            setResult(RESULT_OK, intent);            CameraActivity.this.finish();        }    };    /**     * 获得屏幕宽度,单位px     *     * @param context 上下文     * @return 屏幕宽度     */    public int getScreenWidth(Context context) {        DisplayMetrics dm = context.getResources().getDisplayMetrics();        return dm.widthPixels;    }    /**     * 获得屏幕高度     *     * @param context 上下文     * @return 屏幕除去通知栏的高度     */    public int getScreenHeight(Context context) {        DisplayMetrics dm = context.getResources().getDisplayMetrics();        return dm.heightPixels-getStatusBarHeight(context);    }    /**     * 获取通知栏高度     *     * @param context 上下文     * @return 通知栏高度     */    public int getStatusBarHeight(Context context) {        int statusBarHeight = 0;        try {            Class<?> clazz = Class.forName("com.android.internal.R$dimen");            Object obj = clazz.newInstance();            Field field = clazz.getField("status_bar_height");            int temp = Integer.parseInt(field.get(obj).toString());            statusBarHeight = context.getResources().getDimensionPixelSize(temp);        } catch (Exception e) {            e.printStackTrace();        }        return statusBarHeight;    }}

申明Activity为横屏模式以及拍照的权限等相关信息

    <uses-permission android:name="android.permission.CAMERA"/>    <uses-feature android:name="android.hardware.camera"/>    <uses-feature android:name="android.hardware.camera.autofocus"/> <activity android:name="cn.edu.zafu.camera.activity.CameraActivity"                  android:screenOrientation="landscape"            />

在其他的Activity中直接调用即可

Intent intent = new Intent(MainActivity.this, CameraActivity.class);String pathStr = mPath.getText().toString();String nameStr = mName.getText().toString();String typeStr = mType.getText().toString();if (!TextUtils.isEmpty(pathStr)) {    intent.putExtra("path", pathStr);}if (!TextUtils.isEmpty(nameStr)) {    intent.putExtra("name", nameStr);}if (!TextUtils.isEmpty(typeStr)) {    intent.putExtra("type", typeStr);}startActivityForResult(intent, 100);

获得返回结果并显示在ImageView上

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {    Log.e("TAG","onActivityResult");    if (requestCode == 100) {        if (resultCode == RESULT_OK) {            Bundle extras = data.getExtras();            String path=extras.getString("path");            String type=extras.getString("type");            Toast.makeText(getApplicationContext(),"path:"+ path + " type:" + type, Toast.LENGTH_LONG).show();            File file = new File(path);            FileInputStream inStream = null;            try {                inStream = new FileInputStream(file);                Bitmap bitmap = BitmapFactory.decodeStream(inStream);                mPhoto.setImageBitmap(bitmap);                inStream.close();            } catch (Exception e) {                e.printStackTrace();            }        }    }    super.onActivityResult(requestCode, resultCode, data);}

最终的U如下所示

这里写图片描述

具体细节见源码吧,由于csdn抽了,文件上传不了,所以把代码传github了

  • https://github.com/lizhangqu/Camera

版权声明:本文为博主原创文章,未经博主允许不得转载。

2楼soledadzz昨天 15:55
注意打上马赛克
Re: sbsujjbcy昨天 15:56
回复soledadzzn证件信息并非真实,云脉官网提供的
1楼tuhaihe昨天 14:21
太勇敢了,直接暴露自己的身份证件信息了。。。
Re: sbsujjbcy昨天 15:25
回复tuhaihen证件信息并非真实,云脉官网提供的
  相关解决方案