Barcode AR Overlays

To deliver more personalized visual feedback when scanning barcodes, override the ScanViewPlugin’s native visual feedback function.

This can be achieved using the enableBarcodeOverlays method by passing an implementation of the BarcodeOverlayListener object as a reference.

BarcodeOverlayListener

Interface for a callback to be invoked on BarcodeOverlayView lifecycle events.

Methods:

Enabling Barcode Overlays

onCreate(barcodeOverlayView: BarcodeOverlayView) : List<OverlayViewHolder>

Required - called every time a BarcodeOverlayView that is not contained on current overlays is discovered. Must return a list of OverlayViewHolder that will be placed near the barcode.

onUpdate(viewHolders: List<OverlayViewHolder>, barcodeOverlayView: BarcodeOverlayView)

Optional - called every time a BarcodeOverlayView needs to be repositioned.

onRemove(viewHolders: List<OverlayViewHolder>, barcodeOverlayView: BarcodeOverlayView)

Optional - called every time a BarcodeOverlayView is no longer detected after the interval set on setClearTimeoutMills and shall be disposed.

Setting Overlay Timeouts

getClearTimeoutMills(value: Long)

Optional - sets the max duration in milliseconds an overlay will persist on the screen after it is not detected by the scanner. Should be a value between 500 and 5000. If used, should be set before calling enableBarcodeOverlays() method.

Default value - 1000

Avoid making long operations inside onCreate, onUpdate and onRemove. Due to the nature of their successive calls, the overlay performance can be dramatically affected by the completion period.
Adding simple clickable framed barcode overlay
  • Kotlin

  • Java

val viewPluginBase = scanView.scanViewPlugin.activeScanViewPlugin
viewPluginBase.first().enableBarcodeOverlays(object: BarcodeOverlayListener {
        override fun onCreate(barcodeOverlayView: BarcodeOverlayView): List<OverlayViewHolder> {
            val buttonOverlay = Button(context).apply {
                text = barcodeOverlayView.visibleBarcode.value
                setOnClickListener {
                    Toast.makeText(context, "${barcodeOverlayView.visibleBarcode.value} barcode clicked", Toast.LENGTH_SHORT)
                        .show()
                }
            }
            return listOf(OverlayViewHolder(buttonOverlay, null))
        }
    }
)
ScanViewPlugin scanViewPLugin = scanView.getScanViewPlugin().getFirstActiveScanViewPlugin();
scanViewPLugin.enableBarcodeOverlays(new BarcodeOverlayListener() {
    @NonNull
    @Override
    public List<OverlayViewHolder> onCreate(@NonNull BarcodeOverlayView barcodeOverlayView) {
        Button buttonOverlay = new Button(context);
        buttonOverlay.setText(barcodeOverlayView.getVisibleBarcode().getValue());
        buttonOverlay.setOnClickListener(view -> {
            Toast.makeText(
                    context,
                            barcodeOverlayView.getVisibleBarcode().getValue()
                                    +  " barcode clicked", Toast.LENGTH_SHORT)
                .show();
        });
        ArrayList<OverlayViewHolder> overlayViewHolderList = new ArrayList<>();
        overlayViewHolderList.add(new OverlayViewHolder(buttonOverlay, null));
        return overlayViewHolderList;
    }

    @Override
    public void onUpdate(@NonNull List<? extends OverlayViewHolder> viewHolders, @NonNull BarcodeOverlayView barcodeOverlayView) {
        BarcodeOverlayListener.super.onUpdate(viewHolders, barcodeOverlayView);
    }

    @Override
    public void onRemove(@NonNull List<? extends OverlayViewHolder> viewHolders, @NonNull BarcodeOverlayView barcodeOverlayView) {
        BarcodeOverlayListener.super.onRemove(viewHolders, barcodeOverlayView);
    }

    @Override
    public long getClearTimeoutMills() {
        return BarcodeOverlayListener.super.getClearTimeoutMills();
    }
});

Disabling BarcodeOverlays

Once the overlays are no longer required you can call disableBarcodeOverlays() from the ScanViewPlugin.

  • Kotlin

  • Java

val viewPluginBase = scanView.scanViewPlugin.activeScanViewPlugin
viewPluginBase.first().disableBarcodeOverlays()
ScanViewPlugin scanViewPLugin = scanView.getScanViewPlugin().getFirstActiveScanViewPlugin();
scanViewPLugin.disableBarcodeOverlays();

BarcodeOverlayView

A class that contains the VisibleBarcode information and handles its lifecycle.

Methods:

getVisibleBarcode(): VisibleBarcode

Returns the visible barcode information.

invalidate()

Invalidates the current views associated with this barcode and triggers a new call to BarcodeOverlayListener.onCreate passing this BarcodeOverlayView as reference.

calling invalidate() does not trigger BarcodeOverlayListener.onRemove

VisibleBarcode

The class corresponding to this class on iOS is called ALDetectedBarcode.

A data class that contains general barcode information.

Methods:

getValue(): String

Returns the decoded barcode value.

getBarcode: Barcode

Returns whole information of barcode scan result (as described in io.anyline.plugin.result.Barcode).

getImageRect(): CropRect

Returns the coordinates and dimensions of detected barcode image in pixels.

getBarcodeImage() : Bitmap?

Returns a cropped bitmap containing the barcode image or null if the coordinates are invalid.

Switching overlays depending on the barcode size
  • Kotlin

  • Java

val viewPluginBase = scanView.scanViewPlugin.activeScanViewPlugin
viewPluginBase.first().enableBarcodeOverlays(object: BarcodeOverlayListener {
    val OverlayTypeSmall = 0
    val OverlayTypeBig = 1
    var currentOverlayType = OverlayTypeSmall

    //returns a value of OverlayTypeSmall or OverlayTypeBig depending on the size of the image
    private fun getOverlayTypeFromImageSize(imageRect: CropRect): Int {
        val sizePixels = 400
        if (imageRect.width > sizePixels || imageRect.height > sizePixels)
            return OverlayTypeBig
        return OverlayTypeSmall
    }

    override fun onCreate(barcodeOverlayView: BarcodeOverlayView): List<OverlayViewHolder> {
        //stores a value of OverlayTypeSmall or OverlayTypeBig
        currentOverlayType = getOverlayTypeFromImageSize(barcodeOverlayView.visibleBarcode.imageRect)

        // create an instance of ImageView from small barcode detections
        // or an instance of Button for bigger barcode detections
        val overlayView: View = if (currentOverlayType == OverlayTypeSmall) {
            ImageView(context).apply  {
                scaleType = ImageView.ScaleType.FIT_XY
                layoutParams = ViewGroup.LayoutParams(150, 150)
                setImageResource(android.R.drawable.star_off)
            }
        }
        else {
            Button(context).apply {
                text = barcodeOverlayView.visibleBarcode.value
            }
        }
        return listOf(OverlayViewHolder(overlayView, null))
    }

    override fun onUpdate(
        viewHolders: List<OverlayViewHolder>,
        barcodeOverlayView: BarcodeOverlayView
    ) {
        if (currentOverlayType != getOverlayTypeFromImageSize(barcodeOverlayView.visibleBarcode.imageRect)) {
            // calling invalidate() makes the overlay to be disposed
            // and a new onCreate() will be called for a new view instance
            barcodeOverlayView.invalidate()
        }
    }
})
ScanViewPlugin scanViewPLugin = scanView.getScanViewPlugin().getFirstActiveScanViewPlugin();
scanViewPLugin.enableBarcodeOverlays(new BarcodeOverlayListener() {
    final int OverlayTypeSmall = 0;
    final int OverlayTypeBig = 1;
    int currentOverlayType = OverlayTypeSmall;

    //returns a value of OverlayTypeSmall or OverlayTypeBig depending on the size of the image
    private int getOverlayTypeFromImageSize(CropRect imageRect) {
        final int sizePixels = 400;
        if (imageRect.getWidth() > sizePixels || imageRect.getHeight() > sizePixels)
            return OverlayTypeBig;
        return OverlayTypeSmall;
    }

    @NonNull
    @Override
    public List<OverlayViewHolder> onCreate(@NonNull BarcodeOverlayView barcodeOverlayView) {
        //stores a value of OverlayTypeSmall or OverlayTypeBig
        currentOverlayType = getOverlayTypeFromImageSize(barcodeOverlayView.getVisibleBarcode().getImageRect());

        ArrayList<OverlayViewHolder> overlayViewHolderList = new ArrayList<>();
        // create an instance of ImageView from small barcode detections
        // or an instance of Button for bigger barcode detections
        if (currentOverlayType == OverlayTypeSmall) {
            ImageView imageView = new ImageView(context);
            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
            imageView.setLayoutParams(new ViewGroup.LayoutParams(150, 150));
            imageView.setImageResource(android.R.drawable.star_off);
            overlayViewHolderList.add(new OverlayViewHolder(imageView, null));
        } else {
            Button button = new Button(context);
            button.setText(barcodeOverlayView.getVisibleBarcode().getValue());
            overlayViewHolderList.add(new OverlayViewHolder(button, null));
        }
        return overlayViewHolderList;
    }

    @Override
    public void onUpdate(@NonNull List<? extends OverlayViewHolder> viewHolders, @NonNull BarcodeOverlayView barcodeOverlayView) {
        if (currentOverlayType != getOverlayTypeFromImageSize(barcodeOverlayView.getVisibleBarcode().getImageRect())) {
            // calling invalidate() makes the overlay to be disposed
            // and a new onCreate() will be called for a new view instance
            barcodeOverlayView.invalidate();
        }
    }

    @Override
    public void onRemove(@NonNull List<? extends OverlayViewHolder> viewHolders, @NonNull BarcodeOverlayView barcodeOverlayView) {
        BarcodeOverlayListener.super.onRemove(viewHolders, barcodeOverlayView);
    }

    @Override
    public long getClearTimeoutMills() {
        return BarcodeOverlayListener.super.getClearTimeoutMills();
    }
});

OverlayViewHolder

Class definition for a callback to be invoked to retrieve BarcodeOverlayView placement information.

Methods:

OverlayViewHolder(overlayView: View?, overlayConfig: OverlayConfig?)

Required - constructor that receives a View to be placed in the FrameLayout according with OverlayConfig. To use default values for overlayConfig just provide a null value.

OverlayConfig

Configuration for barcode overlays placement.

Methods:

getAnchor(): OverlayAnchorConfig

Optional - returns the desired OverlayAnchorConfig for a BarcodeOverlayView

Value

TOP_LEFT

TOP_CENTER

TOP_RIGHT

CENTER_LEFT

CENTER

CENTER_RIGHT

BOTTOM_LEFT

BOTTOM_CENTER

BOTTOM_RIGHT

Default value - CENTER (anchored to the center of the barcode)

getSizeDimension(): OverlayDimensionConfig

Optional - returns the desired size using OverlayDimensionConfig for a BarcodeOverlayView

Default value - detected barcode width and height

  • Kotlin

  • Java

OverlayDimensionConfig().apply {
    scaleX = OverlayScaleConfig().apply {
        scaleValue = 1.0
        scaleType = OverlayScaleConfig.OverlayScaleTypeConfig.OVERLAY
    }
    scaleY = OverlayScaleConfig().apply {
        scaleValue = 1.0
        scaleType = OverlayScaleConfig.OverlayScaleTypeConfig.OVERLAY
    }
}
OverlayScaleConfig xScaleOverlayScaleConfig = new OverlayScaleConfig();
xScaleOverlayScaleConfig.setScaleValue(1.0);
xScaleOverlayScaleConfig.setScaleType(OverlayScaleConfig.OverlayScaleTypeConfig.OVERLAY);

OverlayScaleConfig yScaleOverlayScaleConfig = new OverlayScaleConfig();
yScaleOverlayScaleConfig.setScaleValue(1.0);
yScaleOverlayScaleConfig.setScaleType(OverlayScaleConfig.OverlayScaleTypeConfig.OVERLAY);

OverlayDimensionConfig sizeOverlayDimensionConfig = new OverlayDimensionConfig();
sizeOverlayDimensionConfig.setScaleX(xScaleOverlayScaleConfig);
sizeOverlayDimensionConfig.setScaleY(yScaleOverlayScaleConfig);

getOffsetDimension(): OverlayDimensionConfig

Optional - returns the desired offset using OverlayDimensionConfig for a BarcodeOverlayView

Default value - no offset

  • Kotlin

  • Java

OverlayDimensionConfig().apply {
    scaleX = OverlayScaleConfig().apply {
        scaleValue = 0.0
        scaleType = OverlayScaleConfig.OverlayScaleTypeConfig.NONE
    }
    scaleY = OverlayScaleConfig().apply {
        scaleValue = 0.0
        scaleType = OverlayScaleConfig.OverlayScaleTypeConfig.NONE
    }
}
OverlayScaleConfig xScaleOverlayScaleConfig = new OverlayScaleConfig();
xScaleOverlayScaleConfig.setScaleValue(0.0);
xScaleOverlayScaleConfig.setScaleType(OverlayScaleConfig.OverlayScaleTypeConfig.NONE);

OverlayScaleConfig yScaleOverlayScaleConfig = new OverlayScaleConfig();
yScaleOverlayScaleConfig.setScaleValue(0.0);
yScaleOverlayScaleConfig.setScaleType(OverlayScaleConfig.OverlayScaleTypeConfig.NONE);

OverlayDimensionConfig offsetOverlayDimensionConfig = new OverlayDimensionConfig();
offsetOverlayDimensionConfig.setScaleX(xScaleOverlayScaleConfig);
offsetOverlayDimensionConfig.setScaleY(yScaleOverlayScaleConfig);

OverlayDimensionConfig

Class containing OverlayScaleConfig for X and Y

scaleX: OverlayScaleConfig

scaleY: OverlayScaleConfig

Parameters - OverlayScaleConfig for X and Y axes.

OverlayScaleConfig

Class containing scale information for a single axe.

scaleValue: Float

scaleType: OverlayScaleTypeConfig

Parameters - a Float value to be used with the OverlayScaleTypeConfig for positioning overlays.

OverlayScaleTypeConfig

Enum class containing possible values for scaling overlays.

Value

Description

NONE

No scaling

OVERLAY

Scale based on barcode size

FIXED_PX

Fixed size in pixels

KEEP_RATIO

Keep original view size ratio

Adding conditional color and image barcode overlay
  • Kotlin

  • Java

val viewPluginBase = scanView.scanViewPlugin.activeScanViewPlugin
viewPluginBase.first().enableBarcodeOverlays(object: BarcodeOverlayListener {
    override fun onCreate(barcodeOverlayView: BarcodeOverlayView): List<OverlayViewHolder> {
        var imageRes = android.R.drawable.star_off
        var backColor = android.R.color.background_light
        if (barcodeOverlayView.visibleBarcode.barcode.format.equals(BarcodeFormat.CODE_128.name)) {
            //setting a specific color and image for CODE 128 barcodes
            imageRes = android.R.drawable.star_on
            backColor = android.R.color.background_dark
        }

        val frameOverlayViewHolder = OverlayViewHolder(
            View(context).apply  {
                setBackgroundResource(backColor)
            }, null //OverlayConfig is omitted to use default barcode surrounding placement
        )

        val imageOverlayViewHolder = OverlayViewHolder(
            ImageView(context).apply  {
                scaleType = ImageView.ScaleType.FIT_XY
                layoutParams = ViewGroup.LayoutParams(150, 150)
                setImageResource(imageRes)
            }, OverlayConfig().apply {
                anchor = OverlayConfig.OverlayAnchorConfig.TOP_CENTER
                sizeDimension = OverlayDimensionConfig().apply {
                    scaleX = OverlayScaleConfig().apply {
                        scaleValue = 1.0
                        scaleType = OverlayScaleConfig.OverlayScaleTypeConfig.KEEP_RATIO
                    }
                    scaleY = OverlayScaleConfig().apply {
                        scaleValue = 1.0
                        scaleType = OverlayScaleConfig.OverlayScaleTypeConfig.OVERLAY
                    }
                }
            }
        )

        return listOf(frameOverlayViewHolder, imageOverlayViewHolder)
    }
})
ScanViewPlugin scanViewPLugin = scanView.getScanViewPlugin().getFirstActiveScanViewPlugin();
scanViewPLugin.enableBarcodeOverlays(new BarcodeOverlayListener() {

    @NonNull
    @Override
    public List<OverlayViewHolder> onCreate(@NonNull BarcodeOverlayView barcodeOverlayView) {
        int imageRes = android.R.drawable.star_off;
        int backColor = android.R.color.background_light;
        if (barcodeOverlayView.getVisibleBarcode().getBarcode().getFormat().equals(BarcodeFormat.CODE_128.name())) {
            //setting a specific color and image for CODE 128 barcodes
            imageRes = android.R.drawable.star_on;
            backColor = android.R.color.background_dark;
        }

        View view = new View(context);
        view.setBackgroundResource(backColor);
        OverlayViewHolder frameOverlayViewHolder = new OverlayViewHolder(
                view,
                null //OverlayConfig is omitted to use default barcode surrounding placement
        );

        ImageView imageView = new ImageView(context);
        imageView.setScaleType(ImageView.ScaleType.FIT_XY);
        imageView.setLayoutParams(new ViewGroup.LayoutParams(150, 150));
        imageView.setImageResource(imageRes);

        OverlayScaleConfig imageScaleX = new OverlayScaleConfig();
        imageScaleX.setScaleValue(1.0);
        imageScaleX.setScaleType(OverlayScaleConfig.OverlayScaleTypeConfig.KEEP_RATIO);

        OverlayScaleConfig imageScaleY = new OverlayScaleConfig();
        imageScaleY.setScaleValue(1.0);
        imageScaleY.setScaleType(OverlayScaleConfig.OverlayScaleTypeConfig.OVERLAY);

        OverlayDimensionConfig imageSizeDimension = new OverlayDimensionConfig();
        imageSizeDimension.setScaleX(imageScaleX);
        imageSizeDimension.setScaleY(imageScaleY);

        OverlayConfig imageOverlayConfig = new OverlayConfig();
        imageOverlayConfig.setAnchor(OverlayConfig.OverlayAnchorConfig.TOP_CENTER);
        imageOverlayConfig.setSizeDimension(imageSizeDimension);

        OverlayViewHolder imageOverlayViewHolder = new OverlayViewHolder(
                imageView,
                imageOverlayConfig
        );

        ArrayList<OverlayViewHolder> overlayViewHolderList = new ArrayList<>();
        overlayViewHolderList.add(frameOverlayViewHolder);
        overlayViewHolderList.add(imageOverlayViewHolder);
        return overlayViewHolderList;
    }

    @Override
    public void onUpdate(@NonNull List<? extends OverlayViewHolder> viewHolders, @NonNull BarcodeOverlayView barcodeOverlayView) {
        BarcodeOverlayListener.super.onUpdate(viewHolders, barcodeOverlayView);
    }

    @Override
    public void onRemove(@NonNull List<? extends OverlayViewHolder> viewHolders, @NonNull BarcodeOverlayView barcodeOverlayView) {
        BarcodeOverlayListener.super.onRemove(viewHolders, barcodeOverlayView);
    }

    @Override
    public long getClearTimeoutMills() {
        return BarcodeOverlayListener.super.getClearTimeoutMills();
    }
});