GraphicOverlay.java
public class GraphicOverlay extends View {
    private final Object lock = new Object();
    private int previewWidth;
    private float widthScaleFactor = 1.0f;
    private int previewHeight;
    private float heightScaleFactor = 1.0f;
    private int facing = CameraCharacteristics.LENS_FACING_BACK;
    private Set graphics = new HashSet<>();

    /**
     * Base class for a custom graphics object to be rendered within the graphic overlay. Subclass
     * this and implement the {@link Graphic#draw(Canvas)} method to define the graphics element. Add
     * instances to the overlay using {@link GraphicOverlay#add(Graphic)}.
     */
    public abstract static class Graphic {
        private GraphicOverlay overlay;

        public Graphic(GraphicOverlay overlay) {
            this.overlay = overlay;
        }

        /**
         * Draw the graphic on the supplied canvas. Drawing should use the following methods to convert
         * to view coordinates for the graphics that are drawn:
         * 

*

    *
  1. {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the * supplied value from the preview scale to the view scale. *
  2. {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the * coordinate from the preview's coordinate system to the view coordinate system. *
* * @param canvas drawing canvas */ public abstract void draw(Canvas canvas); /** * Adjusts a horizontal value of the supplied value from the preview scale to the view scale. */ public float scaleX(float horizontal) { return horizontal * overlay.widthScaleFactor; } /** * Adjusts a vertical value of the supplied value from the preview scale to the view scale. */ public float scaleY(float vertical) { return vertical * overlay.heightScaleFactor; } /** * Returns the application context of the app. */ public Context getApplicationContext() { return overlay.getContext().getApplicationContext(); } /** * Adjusts the x coordinate from the preview's coordinate system to the view coordinate system. */ public float translateX(float x) { if (overlay.facing == CameraCharacteristics.LENS_FACING_FRONT) { return overlay.getWidth() - scaleX(x); } else { return scaleX(x); } } /** * Adjusts the y coordinate from the preview's coordinate system to the view coordinate system. */ public float translateY(float y) { return scaleY(y); } public void postInvalidate() { overlay.postInvalidate(); } } public GraphicOverlay(Context context, AttributeSet attrs) { super(context, attrs); } /** * Removes all graphics from the overlay. */ public void clear() { synchronized (lock) { graphics.clear(); } postInvalidate(); } /** * Adds a graphic to the overlay. */ public void add(Graphic graphic) { synchronized (lock) { graphics.add(graphic); } postInvalidate(); } /** * Removes a graphic from the overlay. */ public void remove(Graphic graphic) { synchronized (lock) { graphics.remove(graphic); } postInvalidate(); } /** * Sets the camera attributes for size and facing direction, which informs how to transform image * coordinates later. */ public void setCameraInfo(int previewWidth, int previewHeight, int facing) { synchronized (lock) { this.previewWidth = previewWidth; this.previewHeight = previewHeight; this.facing = facing; } postInvalidate(); } /** * Draws the overlay with its associated graphic objects. */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); synchronized (lock) { if ((previewWidth != 0) && (previewHeight != 0)) { widthScaleFactor = (float) canvas.getWidth() / (float) previewWidth; heightScaleFactor = (float) canvas.getHeight() / (float) previewHeight; } for (Graphic graphic : graphics) { graphic.draw(canvas); } } } }