Bootstrap

Android 项目必备(十七)-->实现身份证认证功能

在这里插入图片描述

认证身份证的正反面,我现在只是做了一个demo。基本效果如下:
这里写图片描述

这里写图片描述

这里写图片描述

1,先看清单文件,这需要配置权限,以及CameraActivity的一些属性:

	...
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <uses-feature
        android:name="android.hardware.camera"
        android:required="true" />
    <uses-feature
        android:name="android.hardware.camera.autofocus"
        android:required="true" />
	...
    

2,MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    private ImageView iv_front;
    private ImageView iv_back;
    private ImageView iv_show_front;
    private ImageView iv_show_back;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_front = (ImageView) findViewById(R.id.iv_show_z);
        iv_back = (ImageView) findViewById(R.id.iv_show_f);
        iv_show_back = (ImageView) findViewById(R.id.iv_back_img);
        iv_show_front = (ImageView) findViewById(R.id.iv_front_img);

        iv_show_back.setOnClickListener(this);
        iv_show_front.setOnClickListener(this);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != RESULT_OK) {
            return;
        }
        if (requestCode == CameraActivity.REQUEST_CODE) {
            //获取文件路径,显示图片
            if (data != null) {
                String path = data.getStringExtra("result_front");
                String path_b = data.getStringExtra("result_back");

                if (!TextUtils.isEmpty(path)) {
                    iv_front.setImageBitmap(BitmapFactory.decodeFile(path));

                }

                if (!TextUtils.isEmpty(path_b)) {
                    iv_back.setImageBitmap(BitmapFactory.decodeFile(path_b));
                }
            }
        }
    }

    /**
     * 拍摄证件照片
     *
     * @param type 拍摄证件类型
     */
    private void takePhoto(int type) {
        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
        }
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 0x12);
            return;
        }
        CameraActivity.navToCamera(this, type);
    }



    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.iv_front_img :
                takePhoto(CameraActivity.TYPE_ID_CARD_FRONT);
                break;

            case R.id.iv_back_img :
                takePhoto(CameraActivity.TYPE_ID_CARD_BACK);
                break;
        }
    }
}

3,activity_main.xml布局文件,这里使用的约束布局 ConstraintLayout 。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    tools:layout_editor_absoluteY="25dp">


    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />

    <ImageView
        android:id="@+id/iv_show_z"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="67dp"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/ic_front" />

    <ImageView
        android:id="@+id/iv_show_f"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="66dp"
        android:layout_marginTop="69dp"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintEnd_toEndOf="@+id/iv_show_z"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="@+id/iv_show_z"
        app:layout_constraintTop_toBottomOf="@+id/iv_front_img"
        app:srcCompat="@drawable/ic_back" />

    <ImageView
        android:id="@+id/iv_front_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="@+id/iv_show_z"
        app:layout_constraintStart_toEndOf="@+id/iv_show_z"
        app:layout_constraintTop_toTopOf="@+id/iv_show_z"
        app:srcCompat="@drawable/icon_img_front" />

    <ImageView
        android:id="@+id/iv_back_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="@+id/iv_show_f"
        app:layout_constraintStart_toEndOf="@+id/iv_show_f"
        app:layout_constraintTop_toTopOf="@+id/iv_show_f"
        app:srcCompat="@drawable/icon_img_back" />

    <Button
        android:id="@+id/button"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="52dp"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="8dp"
        android:background="@android:color/holo_red_light"
        android:textColor="#ffffff"
        android:textSize="18sp"
        android:text="认证"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</android.support.constraint.ConstraintLayout>

4,点击上传身份证正面(反面),跳转到相应界面 CameraActivity:

public class CameraActivity extends Activity implements View.OnClickListener {

    /**
     * 身份证正面
     */
    public final static int TYPE_ID_CARD_FRONT = 1;
    /**
     * 身份证反面
     */
    public final static int TYPE_ID_CARD_BACK = 2;

    public final static int REQUEST_CODE = 0X13;

    private CustomCameraPreview customCameraPreview;
    private View containerView;
    private ImageView cropView;
    private View optionView;

    private int type;

    /**
     * 跳转到拍照页面
     */
    public static void navToCamera(Context context, int type) {
        Intent intent = new Intent(context, CameraActivity.class);
        intent.putExtra("type", type);
        ((Activity) context).startActivityForResult(intent, REQUEST_CODE);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        type = getIntent().getIntExtra("type", 0);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        setContentView(R.layout.activity_camera);

        customCameraPreview = (CustomCameraPreview) findViewById(R.id.camera_surface);
        containerView = findViewById(R.id.camera_crop_container);
        cropView = (ImageView) findViewById(R.id.camera_crop);
        optionView = findViewById(R.id.camera_option);

        //获取屏幕最小边,设置为cameraPreview较窄的一边
        float screenMinSize = Math.min(getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
        //根据screenMinSize,计算出cameraPreview的较宽的一边,长宽比为标准的16:9
        float maxSize = screenMinSize / 9.0f * 16.0f;
        RelativeLayout.LayoutParams layoutParams;

        layoutParams = new RelativeLayout.LayoutParams((int) maxSize, (int) screenMinSize);
        layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
        customCameraPreview.setLayoutParams(layoutParams);

        float height = (int) (screenMinSize * 0.75);
        float width = (int) (height * 75.0f / 47.0f);
        LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams((int) width, ViewGroup.LayoutParams.MATCH_PARENT);
        LinearLayout.LayoutParams cropParams = new LinearLayout.LayoutParams((int) width, (int) height);
        containerView.setLayoutParams(containerParams);
        cropView.setLayoutParams(cropParams);
        switch (type) {
            case TYPE_ID_CARD_FRONT:
                cropView.setImageResource(R.mipmap.camera_front);
                break;
            case TYPE_ID_CARD_BACK:
                cropView.setImageResource(R.mipmap.camera_back);
                break;
        }

        customCameraPreview.setOnClickListener(this);
        findViewById(R.id.camera_close).setOnClickListener(this);
        findViewById(R.id.camera_take).setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.camera_surface:
                customCameraPreview.focus();
                break;
            case R.id.camera_close:
                finish();
                break;
            case R.id.camera_take:
                takePhoto();
        }
    }

    private void takePhoto() {
        optionView.setVisibility(View.GONE);
        customCameraPreview.setEnabled(false);
        customCameraPreview.takePhoto(new Camera.PictureCallback() {
            public void onPictureTaken(final byte[] data, final Camera camera) {
                //子线程处理图片,防止ANR
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Bitmap bitmap = null;
                        if (data != null) {
                            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                            camera.stopPreview();
                        }
                        if (bitmap != null) {
                            //计算裁剪位置
                            float left = ((float) containerView.getLeft() - (float) customCameraPreview.getLeft()) / (float) customCameraPreview.getWidth();
                            float top = (float) cropView.getTop() / (float) customCameraPreview.getHeight();
                            float right = (float) containerView.getRight() / (float) customCameraPreview.getWidth();
                            float bottom = (float) cropView.getBottom() / (float) customCameraPreview.getHeight();

                            //裁剪及保存到文件
                            Bitmap resBitmap = Bitmap.createBitmap(bitmap,
                                    (int) (left * (float) bitmap.getWidth()),
                                    (int) (top * (float) bitmap.getHeight()),
                                    (int) ((right - left) * (float) bitmap.getWidth()),
                                    (int) ((bottom - top) * (float) bitmap.getHeight()));

                            FileUtil.saveBitmap(resBitmap,type);

                            if (!bitmap.isRecycled()) {
                                bitmap.recycle();
                            }
                            if (!resBitmap.isRecycled()) {
                                resBitmap.recycle();
                            }

                            //拍照完成,返回对应图片路径
                            Intent intent = new Intent();
                            intent.putExtra("result_front", FileUtil.getImgPath());
                            intent.putExtra("result_back", FileUtil.getImgPath2());
                            setResult(RESULT_OK, intent);
                            finish();
                        }

                        return;
                    }
                }).start();
            }
        });
    }

    @Override
    public void onPointerCaptureChanged(boolean hasCapture) {

    }
}

5,相应的布局文件,还有需要放在layout-land文件夹下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000">

    <com.gyq.constraintlayouttest.camera.CustomCameraPreview
        android:id="@+id/camera_surface"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal">

            <View
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@color/preview_mock" />

            <LinearLayout
                android:id="@+id/camera_crop_container"
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:orientation="vertical">

                <View
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1"
                    android:background="@color/preview_mock" />

                <ImageView
                    android:id="@+id/camera_crop"
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    android:scaleType="fitXY" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1"
                    android:background="@color/preview_mock"
                    android:gravity="center"
                    android:text="@string/touch_to_focus"
                    android:textColor="#afff"
                    android:textSize="16dp" />

            </LinearLayout>

        </LinearLayout>

        <FrameLayout
            android:layout_width="136dp"
            android:layout_height="match_parent"
            android:background="@color/preview_mock">

            <LinearLayout
                android:id="@+id/camera_option"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:orientation="vertical">

                <ImageView
                    android:id="@+id/camera_take"
                    android:layout_width="72dp"
                    android:layout_height="72dp"
                    android:layout_margin="32dp"
                    android:src="@mipmap/camera_take" />

                <ImageView
                    android:id="@+id/camera_close"
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:src="@mipmap/camera_close" />

            </LinearLayout>
        </FrameLayout>

    </LinearLayout>
</RelativeLayout>

,6,自定义CustomCameraPreview.java


public class CustomCameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private static String TAG = CustomCameraPreview.class.getName();

    private Camera mCamera;

    public CustomCameraPreview(Context context) {
        super(context);
        init();
    }

    public CustomCameraPreview(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomCameraPreview(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        SurfaceHolder surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setKeepScreenOn(true);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        mCamera = CamParaUtil.openCamera();
        if (mCamera != null) {
            startPreview(holder);
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        mCamera.stopPreview();
        startPreview(holder);
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        //回收释放资源
        release();
    }

    /**
     * 预览相机
     */
    private void startPreview( SurfaceHolder holder) {
        try {
            mCamera.setPreviewDisplay(holder);
            Camera.Parameters parameters = mCamera.getParameters();
            if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
                //竖屏拍照时,需要设置旋转90度,否者看到的相机预览方向和界面方向不相同
                mCamera.setDisplayOrientation(90);
                parameters.setRotation(90);
            } else {
                mCamera.setDisplayOrientation(0);
                parameters.setRotation(0);
            }
            Camera.Size bestSize = CamParaUtil.getBestSize(parameters.getSupportedPreviewSizes());
            if (bestSize != null) {
                parameters.setPreviewSize(bestSize.width, bestSize.height);
                parameters.setPictureSize(bestSize.width, bestSize.height);
            } else {
                parameters.setPreviewSize(1920, 1080);
                parameters.setPictureSize(1920, 1080);
            }
            mCamera.setParameters(parameters);
            mCamera.startPreview();
            focus();
        } catch (Exception e) {
            try {
                Camera.Parameters parameters = mCamera.getParameters();
                if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
                    mCamera.setDisplayOrientation(90);
                    parameters.setRotation(90);
                } else {
                    mCamera.setDisplayOrientation(0);
                    parameters.setRotation(0);
                }
                mCamera.setParameters(parameters);
                mCamera.startPreview();
                focus();
            } catch (Exception e1) {
                e.printStackTrace();
                mCamera = null;
            }
        }
    }
    /**
     * 释放资源
     */
    private void release() {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    /**
     * 对焦,在CameraActivity中触摸对焦
     */
    public void focus() {
        if (mCamera != null) {
            mCamera.autoFocus(null);
        }
    }

    /**
     * 拍摄照片
     *
     * @param pictureCallback 在pictureCallback处理拍照回调
     */
    public void takePhoto(Camera.PictureCallback pictureCallback) {
        if (mCamera != null) {
            mCamera.takePicture(null, null, pictureCallback);
        }
    }
}

相应的工具类及图片等可以查看 传送门

;