As you followed my blog, I also had 2 posts about this topic:
- Developing a simple Barcode Scanner by get scanned result from ZXing application.
- Embedding the Barcode/QR code scanner to your own application, not depend on the third-party application.
In this tutorial, I am going to help you get started with it.
Project configurations
dependencies
scope of your app-level build.gradle:
compile 'com.google.android.gms:play-services:9.6.1'
Add this meta-data
to <application>
tag in your AndroidManifest.xml:
<meta-data
android:name="com.google.android.gms.vision.DEPENDENCIES"
android:value="barcode" />
Reading barcode/QR code from a photo
assets
folder. I’m going to name the photo qr_code.png
:Firstly, you must decode your photo to a
Bitmap
by using BitmapFactory
, this Bitmap
is needed to Vison API as input:
Bitmap myQRCode = BitmapFactory.decodeStream(getAssets().open("qr_code.png"));
To detect QR codes(and other types of barcodes), you should use an instance of the BarcodeDetector
class. The following code shows you how to create one using BarcodeDetector.Builder
:
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(PhotoActivity.this)
.setBarcodeFormats(Barcode.QR_CODE) // set QR code as the format type
.build();
Create a Frame
using the Bitmap
you created:
Frame frame = new Frame.Builder().setBitmap(myQRCode).build();
Call the detect()
method of the BarcodeDetector
to generate a SparseArray
containing all the QR codes the BarcodeDetector
detected in your photo:
SparseArray barcodes = barcodeDetector.detect(frame);
Okey, after detecting some Barcode
objects, you can get their values by call displayValue
field:
// Check if at least one barcode was detected
if (barcodes.size() != 0) {
// Display the QR code's message
textView.setText("QR CODE Data: " + barcodes.valueAt(0).displayValue);
//Display QR code image to ImageView
imageView.setImageBitmap(myQRCode);
} else {
textView.setText("No QR Code found!");
textView.setTextColor(Color.RED);
}
And this is full code for this activity:
PhotoActivity.java
Running this package info.devexchanges.barcodescannermobilevisionapi;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.android.gms.vision.Frame;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
public class PhotoActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo);
View btnPhotoScan = findViewById(R.id.photo_scan);
final ImageView imageView = (ImageView) findViewById(R.id.image);
final TextView textView = (TextView) findViewById(R.id.qr_code_content);
btnPhotoScan.setOnClickListener(new View.OnClickListener() {
@SuppressLint("SetTextI18n")
@Override
public void onClick(View view) {
try {
Bitmap myQRCode = BitmapFactory.decodeStream(getAssets().open("qr_code.png"));
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(PhotoActivity.this)
.setBarcodeFormats(Barcode.QR_CODE)
.build();
Frame frame = new Frame.Builder().setBitmap(myQRCode).build();
SparseArray barcodes = barcodeDetector.detect(frame);
// Check if at least one barcode was detected
if (barcodes.size() != 0) {
// Display the QR code's message
textView.setText("QR CODE Data: " + barcodes.valueAt(0).displayValue);
//Display QR code image to ImageView
imageView.setImageBitmap(myQRCode);
} else {
textView.setText("No QR Code found!");
textView.setTextColor(Color.RED);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
Activity
, you can get this output:
Reading a barcode Using the Camera
Activity
that does just that.
Firstly, you must request CAMERA permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
Creating a layout file (xml) for this Activity
. I will use a SurfaceView
to display the preview frames captured by the camera and a TextView
to display the content barcode value:
activity_main.xml
In the activity Java code, to "stream" the camera preview scene to the <?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:padding="16dp">
<SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true" />
<TextView
android:id="@+id/barcode_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:text="No Barcode"
android:textColor="@android:color/white"
android:textSize="20sp" />
</RelativeLayout>
SurfaceView
, we'll use an instance of CameraSource
, initializing it with a BarcodeDetector
object:
barcodeDetector = new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build();
cameraSource = new CameraSource.Builder(this, barcodeDetector)
.setRequestedPreviewSize(1600, 1024)
.setAutoFocusEnabled(true) //you should add this feature
.build();
As noted in code, you should use setAutoFocusEnabled(true)
when creating the CameraSource
instance, your "camera preview" will be auto focused, not be blurry!Next, add a callback to the
SurfaceHolder
of the SurfaceView
so that you know when you can start drawing the preview frames. The callback should implement the SurfaceHolder.Callback
interface. Inside the surfaceCreated()
method, call the start()
method of the CameraSource
to start drawing the preview frames:
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
//noinspection MissingPermission
cameraSource.start(cameraView.getHolder());
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
The remaining work is displaying detected barcode content to TextView
. We will use setProcessor()
method of BarcodeDetector
with the parameter is Detector.Processor
:
barcodeDetector.setProcessor(new Detector.Processor() {
@Override
public void release() {
}
@Override
public void receiveDetections(Detector.Detections detections) {
final SparseArray barcodes = detections.getDetectedItems();
if (barcodes.size() != 0) {
barcodeValue.post(new Runnable() {
@Override
public void run() {
//Update barcode value to TextView
barcodeValue.setText(barcodes.valueAt(0).displayValue);
}
});
}
}
});
Moreover, you should override onDestroy()
method of your Activity
to release the CameraSource
to stop drawing the preview frame. Finally, this is full code:
MainActivity.java
Running this activity and scanning a barcode, you may get result like this:
package info.devexchanges.barcodescannermobilevisionapi;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.SparseArray;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;
import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
private BarcodeDetector barcodeDetector;
private CameraSource cameraSource;
private SurfaceView cameraView;
private TextView barcodeValue;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_main);
cameraView = (SurfaceView) findViewById(R.id.surface_view);
barcodeValue = (TextView) findViewById(R.id.barcode_value);
barcodeDetector = new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build();
cameraSource = new CameraSource.Builder(this, barcodeDetector)
.setRequestedPreviewSize(1600, 1024)
.setAutoFocusEnabled(true) //you should add this feature
.build();
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
//noinspection MissingPermission
cameraSource.start(cameraView.getHolder());
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor() {
@Override
public void release() {
}
@Override
public void receiveDetections(Detector.Detections detections) {
final SparseArray barcodes = detections.getDetectedItems();
if (barcodes.size() != 0) {
barcodeValue.post(new Runnable() {
@Override
public void run() {
//Update barcode value to TextView
barcodeValue.setText(barcodes.valueAt(0).displayValue);
}
});
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
cameraSource.release();
barcodeDetector.release();
}
}
Conclusions
These are posts about Mobile Vision API on my blog:
- Faces detection on photo
- Text recognition by device's camera