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 = CameraSource.CAMERA_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 == CameraSource.CAMERA_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); } } } }