Android - Custom external shadow, internal transparent canvas mask layout


A simple requirement, the external shadow, the internal layout is transparent, and the camera is opened at the bottom (here only the layout code, click here with the camera).


1. Call

Just call in Activity, set the height and width of the middle area.

Activity code

private OcrFaceMaskSurfaceView surfaceview;
surfaceview = (OcrFaceMaskSurfaceView) findViewById(;
surfaceview.setMaskSize(900, 600);

Activity layout

<?xml version="1.0" encoding="utf-8"?>

        android:layout_height="match_parent" >



2. is a custom canvas

Implementation ideas:

(1) Two layers of canvas in the custom layout. The first layer is just to start and load the camera

(2) The second layer draws the outer shadow and the inner transparency, plus the inner text

The code is as follows: (CameraHelper is a camera tool class, which can be commented out. Click here to see the complete code with camera + demo)

(3) UiUtil utility class- click here

(4) The code notes are very clear


import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;


public class OcrFaceMaskSurfaceView extends FrameLayout {

    private Context context;
    private MSurfaceView surfaceView;//Load mask for camera
    private MaskView maskView;//Load mask for layout
    private int width;//Screen width
    private int height;//Screen height
    private int maskWidth;//Middle transparent area width
    private int maskHeight;//Height of transparent area in the middle
    private int screenWidth;//Camera capture width
    private int screenHeight;//Camera capture height

    public OcrFaceMaskSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);

        this.context = context;

        surfaceView = new MSurfaceView(context);//The first layer of canvas is used to load the camera
        maskView = new MaskView(context);//The second layer of canvas is used to draw all the layouts
        this.addView(surfaceView, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
        this.addView(maskView, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);

        Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        screenHeight = display.getHeight();
        screenWidth = display.getWidth();

    public void setMaskSize(Integer width, Integer height) {
        maskHeight = height;
        maskWidth = width;

    public int[] getMaskSize() {
        return new MaskSize().size;

    private class MaskSize {
        private int[] size;

        private MaskSize() {
            this.size = new int[]{maskWidth, maskHeight, width, height};

     * Layout of the carrying camera
    private class MSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
        private SurfaceHolder holder;

        public MSurfaceView(Context context) {
            this.holder = this.getHolder();
            //Transparent transparent

        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
            width = w;
            height = h;
            CameraHelper.getInstance().openCamera(holder, format, width, height, screenWidth, screenHeight);

        public void surfaceCreated(SurfaceHolder holder) {


        public void surfaceDestroyed(SurfaceHolder holder) {

     * All layout of mask layer
    private class MaskView extends View {
        private Paint linePaint;
        private Paint rectPaint;
        private Paint topTextPaint;
        private Paint bottomTextPaint;

        public MaskView(Context context) {

            //Paint the rectangle border of the transparent area in the middle
            linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            linePaint.setColor(Color.TRANSPARENT);//Set the middle area color to transparent
            linePaint.setAlpha(0);//The value range is 0-255, the smaller the value, the more transparent

            //Draw rectangular shadow area around
            rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            rectPaint.setAlpha(170);//The value range is 0-255, the smaller the value, the more transparent

            //Draw top middle prompt font
            topTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            topTextPaint.setTextAlign(Paint.Align.CENTER);//Put the x,y coordinates in the middle of the font (the default x,y coordinates are the font head)
            topTextPaint.setTextSize(UiUtil.sp2px(context, 14));

            //Draw top middle prompt font
            bottomTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            bottomTextPaint.setTextAlign(Paint.Align.CENTER);//Put the x,y coordinates in the middle of the font (the default x,y coordinates are the font head)
            bottomTextPaint.setTextSize(UiUtil.sp2px(context, 12));

        protected void onDraw(Canvas canvas) {
            if (maskHeight == 0 && maskWidth == 0) {
            if (maskHeight == height || maskWidth == width) {

            if ((height > width && maskHeight < maskWidth) || (height < width && maskHeight > maskWidth)) {
                int temp = maskHeight;
                maskHeight = maskWidth;
                maskWidth = temp;

            //Height: screen height
            //Width: screen width
            //maskHeight: height of transparent area in the middle
            //maskWidth: width of transparent area in the middle
            int h = Math.abs((height - maskHeight) / 2);//Top shadow height
            int w = Math.abs((width - maskWidth) / 2);//Right shadow width

            //Upper shadow
            canvas.drawRect(0, 0, width, h, this.rectPaint);
            //Right shadow
            canvas.drawRect(width - w, h, width, height - h, this.rectPaint);
            //Lower shadow
            canvas.drawRect(0, height - h, width, height, this.rectPaint);
            //Left shadow
            canvas.drawRect(0, h, w, h + maskHeight, this.rectPaint);
            //Medium transparency
            canvas.drawRect(w, h, w + maskWidth, h + maskHeight, this.linePaint);
  ;//Save the top, bottom, left, right, middle
            //Medium top font
            canvas.rotate(90, width - w / 2, height/2);//Rotate the canvas 90 degrees
            canvas.drawText("Please scan the face of your ID witness", width - w / 2, height/2, topTextPaint);
            canvas.restore();//Restore the canvas to the last saved position to prevent this rotation from affecting the following operations
            //Middle bottom font
            canvas.rotate(90,w / 2, height/2);//Rotate 90 degrees
            canvas.drawText("Please keep the light sufficient, the background clean, and the mobile phone level with the card", w / 2, height/2, bottomTextPaint);

            //Print logo
            Log.e("Height width", "height:" + height + ",width:" + width + ",h:" + h + ",w:" + w + ",mskHeight:" + maskHeight + ",maskWidth:" + maskWidth);



When using canvas canvas, pay attention to use save and restore. Because rotation is used to rotate the entire canvas, the layout behind will be affected. Therefore, before restoring the canvas to save, the new layout will not be affected.

Tags: Android SurfaceView xml encoding

Posted on Sun, 05 Jan 2020 11:00:14 -0500 by mpunn