安卓识别身份证,自动提取身份证信息功能实现(附源码)

源码下载地址:注:源码里没有腾讯优图的账号需要填写自己的 下载地址

先讲几下.首先我们需要去腾讯优图申请一个账号,因为身份证识别需要用到第三方接口如图所示

我申请的是扫描身份证,当然还有其他的功能,比如扫描银行卡,营业执照,车牌等等  ,大家可以去研究一下

1.记得配权限,需要读写SD卡 ,还需要网络的权限   记得引入第三方库,本人很懒,反正能少些代码的,绝对不手打,能用第三库解决的,绝不自己动手,哈哈

    //网络请求库
    compile 'org.xutils:xutils:3.3.36'
    //图片选择器库
    compile 'com.github.LuckSiege.PictureSelector:picture_library:v2.2.3'
    //解析库
    compile 'com.google.code.gson:gson:2.7'

还有,记得在allprojects添加下代码

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
        maven { url 'https://maven.google.com' }
        //图片选择器库
        maven {
            url "http://dl.bintray.com/nuptboyzhb/maven"
        }
        google()
        jcenter()
    }
}

下面配置好权限

    <!-- 读写 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <!-- 用于申请调用照相机 -->
    <uses-permission android:name="android.permission.CAMERA" />

然后新建一个Application配置下网络请求吧

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
//网络请求
        x.Ext.init(this);
    }
}

记得在Manifests中设置一下name=.MyApplication

 <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

接下来是布局文件来,等会上代码了

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        android:scrollbars="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/activity_autonym_certification_iv_positive"
                android:layout_width="match_parent"
                android:layout_height="180dp"
                android:layout_marginRight="30dp"
                android:layout_marginLeft="30dp"
                android:layout_marginTop="20dp"
                android:background="@drawable/autonym_certification_iv_line"
                android:padding="20dp"
                android:src="@mipmap/idcard_positive" />

            <ImageView
                android:id="@+id/activity_autonym_certification_iv_reverse"
                android:layout_width="match_parent"
                android:layout_height="180dp"
                android:layout_marginTop="25dp"
                android:layout_marginRight="30dp"
                android:layout_marginLeft="30dp"
                android:background="@drawable/autonym_certification_iv_line"
                android:padding="20dp"
                android:src="@mipmap/idcard_reverse" />

            <LinearLayout
                android:layout_marginRight="30dp"
                android:layout_marginLeft="30dp"
                android:layout_marginTop="20dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <TextView
                    android:layout_width="90dp"
                    android:layout_height="wrap_content"
                    android:text="姓名:"
                    android:textColor="@color/black"/>
                <EditText
                    android:enabled="false"
                    android:id="@+id/activity_autonym_certification_et_name"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="@color/black"
                    android:gravity="center"
                    android:background="@drawable/autonym_certification_iv_line"
                    android:text=""/>
            </LinearLayout>

            <LinearLayout
                android:layout_marginTop="10dp"
                android:layout_marginLeft="30dp"
                android:layout_marginRight="30dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <TextView
                    android:layout_width="90dp"
                    android:layout_height="wrap_content"
                    android:text="有效日期:"
                    android:textColor="@color/black"/>
                <EditText
                    android:enabled="false"
                    android:id="@+id/activity_autonym_certification_et_effective_date"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="@color/black"
                    android:gravity="center"
                    android:background="@drawable/autonym_certification_iv_line"
                    android:text=""/>
            </LinearLayout>

            <LinearLayout
                android:layout_marginLeft="30dp"
                android:layout_marginRight="30dp"
                android:layout_marginTop="10dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <TextView
                    android:layout_width="90dp"
                    android:layout_height="wrap_content"
                    android:text="身份证号码:"
                    android:textColor="@color/black"/>
                <EditText
                    android:id="@+id/activity_autonym_certification_et_id_card"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="@color/black"
                    android:gravity="center"
                    android:enabled="false"
                    android:background="@drawable/autonym_certification_iv_line"
                    android:text=""/>
            </LinearLayout>
        </LinearLayout>


    </ScrollView>

</LinearLayout>

然后是两个实体类用来存放身份证 正反两面的数据,先是正面

package com.example.aa.demo.entity;

import java.io.Serializable;
import java.util.List;

/**
 * 识别结果实体
 */
public class IdentifyResult implements Serializable {

    /**
     * errorcode : 0
     * errormsg : OK
     * name : xxx
     * name_confidence_all : [50,60,53]
     * sex : xx
     * sex_confidence_all : [41]
     * nation : xx
     * nation_confidence_all : [38]
     * birth : xxxx/xx/xx
     * birth_confidence_all : [69,43,46,46,44,53,50]
     * address : xxxxxxxxx
     * address_confidence_all : [43,36,34,17,25,24,21,31,45,36,23,62,12,30,9,1,39,34,24,19,16,30,30,4]
     * id : xxxxxxxxxxxxxxxxxx
     * id_confidence_all : [52,58,59,73,54,56,61,63,50,63,60,49,72,50,62,57,63,53]
     * frontimage : xxx
     * frontimage_confidence_all : []
     * watermask_confidence_all : []
     * valid_date_confidence_all : []
     * authority_confidence_all : []
     * backimage_confidence_all : []
     * detail_errorcode : []
     * detail_errormsg : []
     */

    private int errorcode;
    private String errormsg;
    private String name;
    private String sex;
    private String nation;
    private String birth;
    private String address;
    private String id;
    private String frontimage;
    private List<Integer> name_confidence_all;
    private List<Integer> sex_confidence_all;
    private List<Integer> nation_confidence_all;
    private List<Integer> birth_confidence_all;
    private List<Integer> address_confidence_all;
    private List<Integer> id_confidence_all;
    private List<?> frontimage_confidence_all;
    private List<?> watermask_confidence_all;
    private List<?> valid_date_confidence_all;
    private List<?> authority_confidence_all;
    private List<?> backimage_confidence_all;
    private List<Integer> detail_errorcode;
    private List<String> detail_errormsg;

    public int getErrorcode() {
        return errorcode;
    }

    public void setErrorcode(int errorcode) {
        this.errorcode = errorcode;
    }

    public String getErrormsg() {
        return errormsg;
    }

    public void setErrormsg(String errormsg) {
        this.errormsg = errormsg;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getNation() {
        return nation;
    }

    public void setNation(String nation) {
        this.nation = nation;
    }

    public String getBirth() {
        return birth;
    }

    public void setBirth(String birth) {
        this.birth = birth;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getFrontimage() {
        return frontimage;
    }

    public void setFrontimage(String frontimage) {
        this.frontimage = frontimage;
    }

    public List<Integer> getName_confidence_all() {
        return name_confidence_all;
    }

    public void setName_confidence_all(List<Integer> name_confidence_all) {
        this.name_confidence_all = name_confidence_all;
    }

    public List<Integer> getSex_confidence_all() {
        return sex_confidence_all;
    }

    public void setSex_confidence_all(List<Integer> sex_confidence_all) {
        this.sex_confidence_all = sex_confidence_all;
    }

    public List<Integer> getNation_confidence_all() {
        return nation_confidence_all;
    }

    public void setNation_confidence_all(List<Integer> nation_confidence_all) {
        this.nation_confidence_all = nation_confidence_all;
    }

    public List<Integer> getBirth_confidence_all() {
        return birth_confidence_all;
    }

    public void setBirth_confidence_all(List<Integer> birth_confidence_all) {
        this.birth_confidence_all = birth_confidence_all;
    }

    public List<Integer> getAddress_confidence_all() {
        return address_confidence_all;
    }

    public void setAddress_confidence_all(List<Integer> address_confidence_all) {
        this.address_confidence_all = address_confidence_all;
    }

    public List<Integer> getId_confidence_all() {
        return id_confidence_all;
    }

    public void setId_confidence_all(List<Integer> id_confidence_all) {
        this.id_confidence_all = id_confidence_all;
    }

    public List<?> getFrontimage_confidence_all() {
        return frontimage_confidence_all;
    }

    public void setFrontimage_confidence_all(List<?> frontimage_confidence_all) {
        this.frontimage_confidence_all = frontimage_confidence_all;
    }

    public List<?> getWatermask_confidence_all() {
        return watermask_confidence_all;
    }

    public void setWatermask_confidence_all(List<?> watermask_confidence_all) {
        this.watermask_confidence_all = watermask_confidence_all;
    }

    public List<?> getValid_date_confidence_all() {
        return valid_date_confidence_all;
    }

    public void setValid_date_confidence_all(List<?> valid_date_confidence_all) {
        this.valid_date_confidence_all = valid_date_confidence_all;
    }

    public List<?> getAuthority_confidence_all() {
        return authority_confidence_all;
    }

    public void setAuthority_confidence_all(List<?> authority_confidence_all) {
        this.authority_confidence_all = authority_confidence_all;
    }

    public List<?> getBackimage_confidence_all() {
        return backimage_confidence_all;
    }

    public void setBackimage_confidence_all(List<?> backimage_confidence_all) {
        this.backimage_confidence_all = backimage_confidence_all;
    }

    public List<Integer> getDetail_errorcode() {
        return detail_errorcode;
    }

    public void setDetail_errorcode(List<Integer> detail_errorcode) {
        this.detail_errorcode = detail_errorcode;
    }

    public List<String> getDetail_errormsg() {
        return detail_errormsg;
    }

    public void setDetail_errormsg(List<String> detail_errormsg) {
        this.detail_errormsg = detail_errormsg;
    }
}

再是反面

package com.example.aa.demo.entity;

import java.io.Serializable;
import java.util.List;

/**
 * Created by aa on 2018/10/30.
 */

public class cardReverseRusult implements Serializable {

    /**
     * errorcode : 0
     * errormsg : OK
     * session_id :
     * name_confidence_all : []
     * sex_confidence_all : []
     * nation_confidence_all : []
     * birth_confidence_all : []
     * address_confidence_all : []
     * id_confidence_all : []
     * frontimage_confidence_all : []
     * watermask_confidence_all : []
     * valid_date : 2010.07.21-2020.07.21
     * valid_date_confidence_all : [99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,95,99,99,99,99]
     * authority : 赵县公安局
     * authority_confidence_all : [100]
     * backimage : .....
     * backimage_confidence_all : []
     * detail_errorcode : []
     * detail_errormsg : []
     * card_type : 1
     * recognize_warn_code : []
     * recognize_warn_msg : []
     */

    private int errorcode;
    private String errormsg;
    private String session_id;
    private String valid_date;
    private String authority;
    private String backimage;
    private int card_type;
    private List<?> name_confidence_all;
    private List<?> sex_confidence_all;
    private List<?> nation_confidence_all;
    private List<?> birth_confidence_all;
    private List<?> address_confidence_all;
    private List<?> id_confidence_all;
    private List<?> frontimage_confidence_all;
    private List<?> watermask_confidence_all;
    private List<Integer> valid_date_confidence_all;
    private List<Integer> authority_confidence_all;
    private List<?> backimage_confidence_all;
    private List<?> detail_errorcode;
    private List<?> detail_errormsg;
    private List<?> recognize_warn_code;
    private List<?> recognize_warn_msg;

    public int getErrorcode() {
        return errorcode;
    }

    public void setErrorcode(int errorcode) {
        this.errorcode = errorcode;
    }

    public String getErrormsg() {
        return errormsg;
    }

    public void setErrormsg(String errormsg) {
        this.errormsg = errormsg;
    }

    public String getSession_id() {
        return session_id;
    }

    public void setSession_id(String session_id) {
        this.session_id = session_id;
    }

    public String getValid_date() {
        return valid_date;
    }

    public void setValid_date(String valid_date) {
        this.valid_date = valid_date;
    }

    public String getAuthority() {
        return authority;
    }

    public void setAuthority(String authority) {
        this.authority = authority;
    }

    public String getBackimage() {
        return backimage;
    }

    public void setBackimage(String backimage) {
        this.backimage = backimage;
    }

    public int getCard_type() {
        return card_type;
    }

    public void setCard_type(int card_type) {
        this.card_type = card_type;
    }

    public List<?> getName_confidence_all() {
        return name_confidence_all;
    }

    public void setName_confidence_all(List<?> name_confidence_all) {
        this.name_confidence_all = name_confidence_all;
    }

    public List<?> getSex_confidence_all() {
        return sex_confidence_all;
    }

    public void setSex_confidence_all(List<?> sex_confidence_all) {
        this.sex_confidence_all = sex_confidence_all;
    }

    public List<?> getNation_confidence_all() {
        return nation_confidence_all;
    }

    public void setNation_confidence_all(List<?> nation_confidence_all) {
        this.nation_confidence_all = nation_confidence_all;
    }

    public List<?> getBirth_confidence_all() {
        return birth_confidence_all;
    }

    public void setBirth_confidence_all(List<?> birth_confidence_all) {
        this.birth_confidence_all = birth_confidence_all;
    }

    public List<?> getAddress_confidence_all() {
        return address_confidence_all;
    }

    public void setAddress_confidence_all(List<?> address_confidence_all) {
        this.address_confidence_all = address_confidence_all;
    }

    public List<?> getId_confidence_all() {
        return id_confidence_all;
    }

    public void setId_confidence_all(List<?> id_confidence_all) {
        this.id_confidence_all = id_confidence_all;
    }

    public List<?> getFrontimage_confidence_all() {
        return frontimage_confidence_all;
    }

    public void setFrontimage_confidence_all(List<?> frontimage_confidence_all) {
        this.frontimage_confidence_all = frontimage_confidence_all;
    }

    public List<?> getWatermask_confidence_all() {
        return watermask_confidence_all;
    }

    public void setWatermask_confidence_all(List<?> watermask_confidence_all) {
        this.watermask_confidence_all = watermask_confidence_all;
    }

    public List<Integer> getValid_date_confidence_all() {
        return valid_date_confidence_all;
    }

    public void setValid_date_confidence_all(List<Integer> valid_date_confidence_all) {
        this.valid_date_confidence_all = valid_date_confidence_all;
    }

    public List<Integer> getAuthority_confidence_all() {
        return authority_confidence_all;
    }

    public void setAuthority_confidence_all(List<Integer> authority_confidence_all) {
        this.authority_confidence_all = authority_confidence_all;
    }

    public List<?> getBackimage_confidence_all() {
        return backimage_confidence_all;
    }

    public void setBackimage_confidence_all(List<?> backimage_confidence_all) {
        this.backimage_confidence_all = backimage_confidence_all;
    }

    public List<?> getDetail_errorcode() {
        return detail_errorcode;
    }

    public void setDetail_errorcode(List<?> detail_errorcode) {
        this.detail_errorcode = detail_errorcode;
    }

    public List<?> getDetail_errormsg() {
        return detail_errormsg;
    }

    public void setDetail_errormsg(List<?> detail_errormsg) {
        this.detail_errormsg = detail_errormsg;
    }

    public List<?> getRecognize_warn_code() {
        return recognize_warn_code;
    }

    public void setRecognize_warn_code(List<?> recognize_warn_code) {
        this.recognize_warn_code = recognize_warn_code;
    }

    public List<?> getRecognize_warn_msg() {
        return recognize_warn_msg;
    }

    public void setRecognize_warn_msg(List<?> recognize_warn_msg) {
        this.recognize_warn_msg = recognize_warn_msg;
    }
}

然后需要几个工具类

第一个bitmap编解码工具类

package cn.vonce.doer.utils;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Base64;
import android.widget.Toast;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * bitmap编解码工具类
 */
public class BitMapUtils {


    /**
     * bitmap转为base64
     *
     * @param bitmap
     * @return
     */
    public static String bitmapToBase64(Bitmap bitmap) {
        String result = null;
        ByteArrayOutputStream baos = null;
        try {
            if (bitmap != null) {
                baos = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
                baos.flush();
                baos.close();
                byte[] bitmapBytes = baos.toByteArray();
                result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
            }
        } catch (IOException e) {
            return "";
        } finally {
            try {
                if (baos != null) {
                    baos.flush();
                    baos.close();
                }
            } catch (IOException e) {
                return "";
            }
        }
        return result;
    }


    /**
     * 从SD卡上获取图片。如果不存在则返回null</br>
     * @return 代表图片的Bitmap对象
     */
    public static Bitmap getBitmapFromSDCard(String url) {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(new File(url));
            if (inputStream != null && inputStream.available() > 0) {
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                return bitmap;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // 分享单张图片
    public static void shareSingleImage(Context context, Bitmap copy) {
        if(copy != null){
            File appDir = new File(Environment.getExternalStorageDirectory(), "demo");
            if (!appDir.exists()) {
                appDir.mkdir();
            }
            String fileName = System.currentTimeMillis() + ".jpg";
            File file = new File(appDir, fileName);
            try {
                FileOutputStream fos = new FileOutputStream(file);
                copy.compress(Bitmap.CompressFormat.JPEG, 100, fos);
                fos.flush();
                fos.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            String path = file.getAbsolutePath();

            // 由文件得到uri
            Uri imageUri = Uri.fromFile(new File(path));
            Intent shareIntent = new Intent();
            shareIntent.setAction(Intent.ACTION_SEND);
            shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
            shareIntent.setType("image/*");
            context.startActivity(Intent.createChooser(shareIntent, "分享到"));
        }else {
            Toast.makeText(context, "请先从图库选择一张图片", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 保存图片到本地图库
     *
     * @param context       上下文
     * @param bmp           要保存的bitmap
     */
    public static void saveImageToGallery(Context context, Bitmap bmp) {
        // 首先保存图片
        File appDir = new File(Environment.getExternalStorageDirectory(), "demo");
        if (!appDir.exists()) {
            appDir.mkdir();
        }
        String fileName = System.currentTimeMillis() + ".jpg";
        File file = new File(appDir, fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        String path = file.getAbsolutePath();

        // 其次把文件插入到系统图库
        try {
            MediaStore.Images.Media.insertImage(context.getContentResolver(),
                    file.getAbsolutePath(), fileName, null);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        Toast.makeText(context, "图片已保存至" + path, Toast.LENGTH_SHORT).show();
        // 最后通知图库更新
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + path)));
    }

    /**
     * 选择图片后压缩展示
     */
    public static Bitmap onSelected(String local_path) {
        Bitmap bitmap = BitmapFactory.decodeFile(local_path);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 85, out);
        int size = bitmap.getWidth() * bitmap.getHeight();
        float zoom = (float) Math.sqrt(size * 1024 / (float)out.toByteArray().length);

        Matrix matrix = new Matrix();
        matrix.setScale(zoom, zoom);

        Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        out.reset();
        result.compress(Bitmap.CompressFormat.JPEG, 85, out);
        while(out.toByteArray().length > size * 1024){
            System.out.println(out.toByteArray().length);
            matrix.setScale(0.9f, 0.9f);
            result = Bitmap.createBitmap(result, 0, 0, result.getWidth(), result.getHeight(), matrix, true);
            out.reset();
            result.compress(Bitmap.CompressFormat.JPEG, 85, out);
        }
        bitmap = result;
        return bitmap;
    }

    /**
     * 获取压缩后的图片
     *
     * @param srcPath       要压缩的图片路径
     * @return              压缩后的bitmap
     */
    public static Bitmap getImage(String srcPath) {
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath,newOpts);// 此时返回bm为空

        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 800f;// 这里设置高度为800f
        float ww = 480f;// 这里设置宽度为480f
        // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;// be=1表示不缩放
        if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;// 设置缩放比例
        // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
        return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
    }

    public static Bitmap compressImage(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        int options = 100;
        while (baos.toByteArray().length / 1024 > 100) {  // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
            baos.reset();// 重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中
            options -= 10;// 每次都减少10
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
        return bitmap;
    }

    /**
     * 处理指定的图片
     */
    public static Bitmap handlePicture(Bitmap bitmap){
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Bitmap copy = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        int[] before = new int[width * height];
        int[] after = new int[width * height];
        bitmap.getPixels(before, 0, width, 0, 0, width, height);
        int pixelsA, pixelsR, pixelsG, pixelsB;
        for(int i = 0;i < width * height;i++){
            int color = before[i];
            // ∂获取RGB分量
            pixelsA = Color.alpha(color);
            pixelsR = Color.red(color);
            pixelsG = Color.green(color);
            pixelsB = Color.blue(color);

            if(pixelsA >= 1) {
                // 转换
                pixelsR = (255 - pixelsR);
                pixelsG = (255 - pixelsG);
                pixelsB = (255 - pixelsB);
                // 均小于等于255大于等于0
                if (pixelsR > 255) {
                    pixelsR = 255;
                } else if (pixelsR < 0) {
                    pixelsR = 0;
                }
                if (pixelsG > 255) {
                    pixelsG = 255;
                } else if (pixelsG < 0) {
                    pixelsG = 0;
                }
                if (pixelsB > 255) {
                    pixelsB = 255;
                } else if (pixelsB < 0) {
                    pixelsB = 0;
                }
            }
            // 根据新的RGB生成新像素
            after[i] = Color.argb(pixelsA, pixelsR, pixelsG, pixelsB);
        }
        copy.setPixels(after, 0, width, 0, 0, width, height);
        return copy;
    }


    /**
     * 处理指定的图片为黑白照片
     */
    public static Bitmap colored(Bitmap bitmap){
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Bitmap copy = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        int[] before = new int[width * height];
        int[] after = new int[width * height];
        bitmap.getPixels(before, 0, width, 0, 0, width, height);
        int pixelsA, pixelsR, pixelsG, pixelsB;
        for(int i = 0;i < width * height;i++){
            int color = before[i];
            // 获取RGB分量
            pixelsA = Color.alpha(color);
            pixelsR = Color.red(color);
            pixelsG = Color.green(color);
            pixelsB = Color.blue(color);

            if(pixelsA >= 1) {
                pixelsR = pixelsG = pixelsB = (pixelsR + pixelsG + pixelsB) / 3;
            }

            // 根据新的RGB生成新像素
            after[i] = Color.argb(pixelsA, pixelsR, pixelsG, pixelsB);
        }
        copy.setPixels(after, 0, width, 0, 0, width, height);
        return copy;
    }

    /**
     * 处理指定的图片为半透明照片
     */
    public static Bitmap halfTrans(Bitmap bitmap){
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Bitmap copy = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        int[] before = new int[width * height];
        int[] after = new int[width * height];
        bitmap.getPixels(before, 0, width, 0, 0, width, height);
        int pixelsA, pixelsR, pixelsG, pixelsB;
        for(int i = 0;i < width * height;i++){
            int color = before[i];
            // 获取RGB分量
            pixelsA = Color.alpha(color);
            pixelsR = Color.red(color);
            pixelsG = Color.green(color);
            pixelsB = Color.blue(color);

            // 根据新的RGB生成新像素
            after[i] = Color.argb(pixelsA / 2, pixelsR, pixelsG, pixelsB);
        }
        copy.setPixels(after, 0, width, 0, 0, width, height);
        return copy;
    }

}

第二个网络请求工具类  请求腾讯的接口

package cn.vonce.doer.utils;

import android.util.Log;

import org.xutils.common.Callback;
import org.xutils.http.RequestParams;
import org.xutils.x;

import java.io.File;

import cn.vonce.doer.sign.Constant;
import cn.vonce.doer.sign.YoutuSign;

/**
 * 网络请求工具类
 */
public class TecentHttpUtil {

    public static void uploadIdCard(String bitmap, String card_type, final SimpleCallBack callback) {
        StringBuffer mySign = new StringBuffer("");
        YoutuSign.appSign(Constant.AppID, Constant.SecretID, Constant.SecretKey,
                System.currentTimeMillis() / 1000 + Constant.EXPIRED_SECONDS,
                Constant.QQNumber, mySign);
        RequestParams params = new RequestParams("http://api.youtu.qq.com/youtu/ocrapi/idcardocr");
        params.setAsJsonContent(true);
        params.addHeader("accept", "*/*");
        params.addHeader("Host", "api.youtu.qq.com");
        params.addHeader("user-agent", "youtu-java-sdk");
        params.addHeader("Authorization", mySign.toString());
        params.addHeader("Content-Type", "text/json");
        params.addParameter("card_type", Integer.valueOf(card_type));
        params.addBodyParameter("image", bitmap);
        params.addBodyParameter("app_id", Constant.AppID);
        x.http().post(params, new Callback.CommonCallback<String>() {
            @Override
            public void onSuccess(String result) {
                Log.d("onSuccess",result);
                callback.Succ(result);
            }

            @Override
            public void onError(Throwable ex, boolean isOnCallback) {
                Log.d("onError",ex.getMessage());
            }

            @Override
            public void onCancelled(CancelledException cex) {
                Log.d("onCancelled", cex.getMessage());
            }

            @Override
            public void onFinished() {

            }
        });

    }

    public interface SimpleCallBack {
        void Succ(String result);

        void error();
    }


}

 在网络请求工具类里面会用到你申请的id了   需要几个类

package com.example.aa.demo.sign;

import java.util.Random;

public class YoutuSign {

	/**
	 *app_sign    时效性签名
	 *@param  appId       http://open.youtu.qq.com/上申请的业务ID
	 *@param  secret_id   http://open.youtu.qq.com/上申请的密钥id
	 *@param  secret_key  http://open.youtu.qq.com/上申请的密钥key
	 *@param  expired     签名过期时间
	 *@param  userid      业务账号系统,没有可以不填
	 *@param  mySign      生成的签名
	 *@return 0表示成功
	 */
	public static int appSign(String appId, String secret_id, String secret_key,
                              long expired, String userid, StringBuffer mySign) {
		return appSignBase(appId, secret_id, secret_key, expired, userid, null, mySign);
	}

    
	private static int appSignBase(String appId, String secret_id,
                                   String secret_key, long expired, String userid, String url,
                                   StringBuffer mySign) {
		

		if (empty(secret_id) || empty(secret_key))
    	{
            return -1;
    	}
    	
    	String puserid = "";
    	if (!empty(userid))
    	{
			if (userid.length() > 64)
			{
                return -2;
			}
			puserid = userid;
    	}
    	

        long now = System.currentTimeMillis() / 1000;
        int rdm = Math.abs(new Random().nextInt());
        String plain_text = "a=" + appId + "&k=" + secret_id + "&e=" + expired + "&t=" + now + "&r=" + rdm + "&u=" + puserid ;//+ "&f=" + fileid.toString();

        byte[] bin = hashHmac(plain_text, secret_key);

        byte[] all = new byte[bin.length + plain_text.getBytes().length];
        System.arraycopy(bin, 0, all, 0, bin.length);
        System.arraycopy(plain_text.getBytes(), 0, all, bin.length, plain_text.getBytes().length);
        
        mySign.append(Base64Util.encode(all));
        
        return 0;
	}

	private static byte[] hashHmac(String plain_text, String accessKey) {
		
		try {
			return HMACSHA1.getSignature(plain_text, accessKey);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

    
	public static boolean empty(String s){
		return s == null || s.trim().equals("") || s.trim().equals("null");
	}
		
}
package com.example.aa.demo.sign;

public class Base64Util {
	private static final char last2byte = (char) Integer
			.parseInt("00000011", 2);
	private static final char last4byte = (char) Integer
			.parseInt("00001111", 2);
	private static final char last6byte = (char) Integer
			.parseInt("00111111", 2);
	private static final char lead6byte = (char) Integer
			.parseInt("11111100", 2);
	private static final char lead4byte = (char) Integer
			.parseInt("11110000", 2);
	private static final char lead2byte = (char) Integer
			.parseInt("11000000", 2);
	private static final char[] encodeTable = new char[] { 'A', 'B', 'C', 'D',
			'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
			'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
			'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
			'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
			'4', '5', '6', '7', '8', '9', '+', '/' };

	/**
	 * Base64 encoding.
	 * 
	 * @param from
	 *            The src data.
	 * @return cryto_str
	 */
	public static String encode(byte[] from) {
		StringBuilder to = new StringBuilder((int) (from.length * 1.34) + 3);
		int num = 0;
		char currentByte = 0;
		for (int i = 0; i < from.length; i++) {
			num = num % 8;
			while (num < 8) {
				switch (num) {
				case 0:
					currentByte = (char) (from[i] & lead6byte);
					currentByte = (char) (currentByte >>> 2);
					break;
				case 2:
					currentByte = (char) (from[i] & last6byte);
					break;
				case 4:
					currentByte = (char) (from[i] & last4byte);
					currentByte = (char) (currentByte << 2);
					if ((i + 1) < from.length) {
						currentByte |= (from[i + 1] & lead2byte) >>> 6;
					}
					break;
				case 6:
					currentByte = (char) (from[i] & last2byte);
					currentByte = (char) (currentByte << 4);
					if ((i + 1) < from.length) {
						currentByte |= (from[i + 1] & lead4byte) >>> 4;
					}
					break;
				}
				to.append(encodeTable[currentByte]);
				num += 6;
			}
		}
		if (to.length() % 4 != 0) {
			for (int i = 4 - to.length() % 4; i > 0; i--) {
				to.append("=");
			}
		}
		return to.toString();
	}
}
package com.example.aa.demo.sign;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class HMACSHA1 {

	private static final String HMAC_SHA1 = "HmacSHA1";

	public static byte[] getSignature(String data, String key) throws Exception {
		Mac mac = Mac.getInstance(HMAC_SHA1);
		SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(),
				mac.getAlgorithm());
		mac.init(signingKey);

		return mac.doFinal(data.getBytes());
	}
}
package com.example.aa.demo.sign;

/**
 * 优图账号
 *这里填大家自己申请的
 */
public class Constant {

    public static String QQNumber = "";// 腾讯优图开发者账号(QQ号)
    public static String AppID = "";
    public static String SecretID = "";
    public static String SecretKey = "";
    public static int EXPIRED_SECONDS = ;// 过期时间戳
}

然后就是主类了

package com.example.aa.demo;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

import com.example.aa.demo.entity.IdentifyResult;
import com.example.aa.demo.entity.cardReverseRusult;
import com.example.aa.demo.utils.BitMapUtils;
import com.example.aa.demo.utils.TecentHttpUtil;
import com.google.gson.Gson;
import com.luck.picture.lib.PictureSelector;
import com.luck.picture.lib.config.PictureConfig;
import com.luck.picture.lib.config.PictureMimeType;
import com.luck.picture.lib.entity.LocalMedia;

import java.util.ArrayList;
import java.util.List;

public class AutonymCertificationActivity extends AppCompatActivity implements View.OnClickListener {

    /**
     * 身份证正面
     */
    private ImageView activity_autonym_certification_iv_positive;

    /**
     * 身份证反面
     */
    private ImageView activity_autonym_certification_iv_reverse;

    /**
     * 选择身份证正面
     */
    private List<LocalMedia> cardPositive = new ArrayList<>();

    /**
     * 选择身份证反面
     */
    private List<LocalMedia> cardReverse = new ArrayList<>();

    /**
     * 选择模式:全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio()
     */
    private int chooseMode = PictureMimeType.ofImage();

    /**
     * 主题id:R.style.picture_default_style、R.style.picture_white_style、R.style.picture_QQ_style、R.style.picture_Sina_style
     */
    private int themeId = R.style.picture_default_style;

    /**
     * 最多可选择1张图片
     */
    private int maxSelectNum = 1;

    /**
     * 正反图片路径
     */
    private String cardPositivePath = null, cardReversePath = null;

    /**
     * 保存图片,正反
     */
    private Bitmap cardPositiveBitMap, cardReverseBitMap;

    /**
     * 自动识别姓名  有效日期   身份证号码
     */
    private EditText activity_autonym_certification_et_name;

    private EditText activity_autonym_certification_et_effective_date;

    private EditText activity_autonym_certification_et_id_card;

    /**
     * 存放正面识别数据的类
     */
    IdentifyResult result;

    /**
     * 存放反面识别数据的类
     */
      cardReverseRusult cardReverseRusult;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_autonym_certification);
        //初始化找id
        initViews();
    }

    private void initViews() {
        activity_autonym_certification_iv_positive=findViewById(R.id.activity_autonym_certification_iv_positive);
        activity_autonym_certification_iv_reverse=findViewById(R.id.activity_autonym_certification_iv_reverse);
        activity_autonym_certification_et_name=findViewById(R.id.activity_autonym_certification_et_name);
        activity_autonym_certification_et_effective_date=findViewById(R.id.activity_autonym_certification_et_effective_date);
        activity_autonym_certification_et_id_card=findViewById(R.id.activity_autonym_certification_et_id_card);
        activity_autonym_certification_iv_positive.setOnClickListener(this);
        activity_autonym_certification_iv_reverse.setOnClickListener(this);
        //简单的申请两个权限
        
        if (ContextCompat.checkSelfPermission(AutonymCertificationActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    1);
        }
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            //身份证正面点击事件
            case R.id.activity_autonym_certification_iv_positive:
                //0表示正面
                OpenPhotoAlbum(cardPositive, 0);
                break;

            //身份证反面的点击事件  1表示反面
            case R.id.activity_autonym_certification_iv_reverse:
                OpenPhotoAlbum(cardReverse, 1);
                break;
        }
    }

    /**
     * @param list 相片返回的地方
     * @param i    判断返回的是正反    0是正面  1是反面
     */
    private void OpenPhotoAlbum(List list, int i) {
        // 进入相册 以下是例子:不需要的api可以不写
        PictureSelector.create(this)
                .openGallery(chooseMode)// 全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio()
                .theme(themeId)// 主题样式设置 具体参考 values/styles   用法:R.style.picture.white.style
                .maxSelectNum(maxSelectNum)// 最大图片选择数量
                .minSelectNum(1)// 最小选择数量
                .imageSpanCount(4)// 每行显示个数
                .selectionMode(PictureConfig.MULTIPLE)// PictureConfig.MULTIPLE多选 or 单选PictureConfig.SINGLE
                .previewImage(true)// 是否可预览图片
                //.previewVideo(true)// 是否可预览视频
                //.enablePreviewAudio(ture) // 是否可播放音频
                //.compressGrade(Luban.THIRD_GEAR)// luban压缩档次,默认3档 Luban.FIRST_GEAR、Luban.CUSTOM_GEAR
                .isCamera(true)// 是否显示拍照按钮
                .isZoomAnim(true)// 图片列表点击 缩放效果 默认true
                //.setOutputCameraPath("/CustomPath")// 自定义拍照保存路径
                //.enableCrop(false)// 是否裁剪
                .compress(true)// 是否压缩
                // .compressMode(PictureConfig.SYSTEM_COMPRESS_MODE)//系统自带 or 鲁班压缩 PictureConfig.SYSTEM_COMPRESS_MODE or LUBAN_COMPRESS_MODE
                //.sizeMultiplier(0.5f)// glide 加载图片大小 0~1之间 如设置 .glideOverride()无效
                .glideOverride(160, 160)// glide 加载宽高,越小图片列表越流畅,但会影响列表图片浏览的清晰度
                //.withAspectRatio(0, 0)// 裁剪比例 如16:9 3:2 3:4 1:1 可自定义
                //.hideBottomControls(false)// 是否显示uCrop工具栏,默认不显示
                //.isGif(false)// 是否显示gif图片
                //.freeStyleCropEnabled(false)// 裁剪框是否可拖拽
                //.circleDimmedLayer(false)// 是否圆形裁剪
                //.showCropFrame(false)// 是否显示裁剪矩形边框 圆形裁剪时建议设为false
                //.showCropGrid(false)// 是否显示裁剪矩形网格 圆形裁剪时建议设为false
                .openClickSound(false)// 是否开启点击声音
                .selectionMedia(list)// 是否传入已选图片
//                        .videoMaxSecond(15)
//                        .videoMinSecond(10)
                //.previewEggs(false)// 预览图片时 是否增强左右滑动图片体验(图片滑动一半即可看到上一张是否选中)
                //.cropCompressQuality(90)// 裁剪压缩质量 默认100
                //.compressMaxKB()//压缩最大值kb compressGrade()为Luban.CUSTOM_GEAR有效
                //.compressWH() // 压缩宽高比 compressGrade()为Luban.CUSTOM_GEAR有效
                //.cropWH()// 裁剪宽高比,设置如果大于图片本身宽高则无效
                //.rotateEnabled() // 裁剪是否可旋转图片
                //.scaleEnabled()// 裁剪是否可放大缩小图片
                //.videoQuality()// 视频录制质量 0 or 1
                //.videoSecond()//显示多少秒以内的视频or音频也可适用
                //.recordVideoSecond()//录制视频秒数 默认60s
                .forResult(i);//结果回调onActivityResult code(这里自己设置了0.1 目的区分是正反)
    }

    //选择完照片的回调
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //选择图片回调
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                case 0:
                    /**
                     * 正面识别
                     */
                    // 图片选择结果回调
                    cardPositive = PictureSelector.obtainMultipleResult(data);
                    // 例如 LocalMedia 里面返回三种path
                    // 1.media.getPath(); 为原图path
                    // 2.media.getCutPath();为裁剪后path,需判断media.isCut();是否为true
                    // 3.media.getCompressPath();为压缩后path,需判断media.isCompressed();是否为true
                    // 如果裁剪并压缩了,以取压缩路径为准,因为是先裁剪后压缩的

                    //首先获取图片地址
                    if (cardPositive.size() > 0) {
                        //首先确保大于0
                        LocalMedia localMedia = cardPositive.get(0);
                        cardPositivePath = localMedia.getPath();//正常的地址,看具体的业务,如果需要压缩,则获取压缩地址
                        //拿到了地址,那么就加载图片
                        cardPositiveBitMap = BitMapUtils.getImage(cardPositivePath);
//                        //设置图片
                        activity_autonym_certification_iv_positive.setImageBitmap(cardPositiveBitMap);
                        //设置完成自动识别
                        TecentHttpUtil.uploadIdCard(BitMapUtils.bitmapToBase64(cardPositiveBitMap), "0", new TecentHttpUtil.SimpleCallBack() {
                            @Override
                            public void Succ(String res) {
                                result = new Gson().fromJson(res, IdentifyResult.class);
                                if (result != null) {
                                    if (result.getErrorcode() == 0) {
                                        // 识别成功设置姓名
                                        activity_autonym_certification_et_name.setText(result.getName());
                                        //设置身份证号码
                                        activity_autonym_certification_et_id_card.setText(result.getId());
                                    } else {
                                        Toast.makeText(AutonymCertificationActivity.this, "识别错误", Toast.LENGTH_SHORT).show();
                                /*switch (result.getErrorcode()){
                                    case -7001:
                                        Toast.makeText(MainActivity.this, "未检测到身份证,请对准边框(请避免拍摄时倾角和旋转角过大、摄像头)", Toast.LENGTH_SHORT).show();
                                        break;
                                    case -7002:
                                        Toast.makeText(MainActivity.this, "请使用第二代身份证件进行扫描", Toast.LENGTH_SHORT).show();
                                        break;
                                    case -7003:
                                        Toast.makeText(MainActivity.this, "不是身份证正面照片(请使用带证件照的一面进行扫描)", Toast.LENGTH_SHORT).show();
                                        break;
                                    case -7004:
                                        Toast.makeText(MainActivity.this, "不是身份证反面照片(请使用身份证反面进行扫描)", Toast.LENGTH_SHORT).show();
                                        break;
                                    case -7005:
                                        Toast.makeText(MainActivity.this, "确保扫描证件图像清晰", Toast.LENGTH_SHORT).show();
                                        break;
                                    case -7006:
                                        Toast.makeText(MainActivity.this, "请避开灯光直射在证件表面", Toast.LENGTH_SHORT).show();
                                        break;
                                    default:
                                        Toast.makeText(MainActivity.this, "识别失败,请稍后重试", Toast.LENGTH_SHORT).show();
                                        break;
                                }*/
                                    }
                                }
                            }

                            @Override
                            public void error() {
                                Log.i("识别错误", "error: ");
                            }
                        });
                    }
                    break;

                case 1:
                    /**
                     * 反面识别
                     */
                    cardReverse = PictureSelector.obtainMultipleResult(data);
                    if (cardReverse.size() > 0) {
                        //首先确保大于0
                        LocalMedia localMedia = cardReverse.get(0);
                        cardReversePath = localMedia.getPath();//正常的地址,看具体的业务,如果需要压缩,则获取压缩地址
                        //拿到了地址,那么就加载图片
                        cardReverseBitMap = BitMapUtils.getImage(cardReversePath);
//                        //设置图片
                        activity_autonym_certification_iv_reverse.setImageBitmap(cardReverseBitMap);

                        TecentHttpUtil.uploadIdCard(BitMapUtils.bitmapToBase64(cardReverseBitMap), "1", new TecentHttpUtil.SimpleCallBack() {
                            @Override
                            public void Succ(String res) {
                                Log.e("===========success", res);
                                cardReverseRusult = new Gson().fromJson(res, cardReverseRusult.class);

                                if (cardReverseRusult != null) {
                                    if (cardReverseRusult.getErrorcode() == 0) {
                                        //反面识别成功设置有效日期
                                        activity_autonym_certification_et_effective_date.setText(cardReverseRusult.getValid_date());
                                    } else {
                                        Toast.makeText(AutonymCertificationActivity.this, "识别错误", Toast.LENGTH_SHORT).show();

                                    }
                                }
                            }

                            @Override
                            public void error() {
                                Log.i("识别错误", "error: ");
                            }
                        });
                    }
                    break;
            }
        }

    }
    @Override
    public void onPointerCaptureChanged(boolean hasCapture) {

    }
}

到这来项目基本就跑起来了,代码不好,仅供参考,声明,里面有一些代码是参考了网络上的。

猜你喜欢

转载自blog.csdn.net/qq_34783437/article/details/83585386