Use the ZXing Barcode Scanner in an Android App
Barcodes and Quick Response (QR) codes are useful for storing short sequences of data that can be recognised by a variety of machines and devices. They are particularly good for stock control hence the universal use on packaging and for logistics. QR codes are useful for communicating web addresses and contact details. Adding support for barcodes and QR codes in an App opens up new interface, communication and feature possibilities. Here is this website's address as a QR Code.
One of the great features of Android is how easy it is to tap into existing functionality. Scanning barcodes and Quick Response (QR) codes is a good example. Google has a free scanning App that can be accessed from another App, using an Intent
. This tutorial is an example of how to access the Google Barcode Scanner. For the following example code to work the scanner needs to be installed. Search for Barcode Scanner on Google Play and install it. It's published by the Google ZXing team, this link takes you to their Play page. ZXing stands for Zebra Crossing (a.k.a. pedestrian crosswalk) which sort of look like barcodes.
(This Android tutorial assumes that Android Studio is installed; a basic App can be created and run; and code can be correctly copied into Studio. Here, for the tutorial App, the Application name is set to Scan Barcode and an Empty Activity is used. Adapt the tutorial code as required.)
Creating a Basic Barcode Scanning App
In Studio delete the TextView
on the starting activity_main.xml layout. Drop three Button
widgets on to the screen and set their text to QR CODE, PRODUCT and OTHER. Set their ids to butQR, butProd and butOther. Add a Large Text below the buttons setting the background property to black, the textColor property to white and the id to tvStatus. Add another Large Text and set the layout:width and layout:height to fill_parent and the id to tvResult:
The two TextViews will display the type of barcode scanned and the data it contains. In MainActivity.java a function called HandleClick processes the pressed button (using the button id to determine which mode to call the scanner). Depending upon which button is pressed the program puts the relevant parameters into an Intent. The Intent initiates the scan barcode ZXing activity. The result is returned and processed in the onActivityResult function. Here's the code (when prompted in Studio use Alt-Enter to add required imports, e.g. for View
, Intent
and TextView
):
package com.example.scanbarcode;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void HandleClick(View arg0) {
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
switch(arg0.getId()){
case R.id.butQR:
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
break;
case R.id.butProd:
intent.putExtra("SCAN_MODE", "PRODUCT_MODE");
break;
case R.id.butOther:
intent.putExtra("SCAN_FORMATS", "CODE_39,CODE_93,CODE_128,DATA_MATRIX,ITF,CODABAR");
break;
}
startActivityForResult(intent, 0); //Barcode Scanner to scan for us
}
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == 0) {
TextView tvStatus=(TextView)findViewById(R.id.tvStatus);
TextView tvResult=(TextView)findViewById(R.id.tvResult);
if (resultCode == RESULT_OK) {
tvStatus.setText(intent.getStringExtra("SCAN_RESULT_FORMAT"));
tvResult.setText(intent.getStringExtra("SCAN_RESULT"));
} else if (resultCode == RESULT_CANCELED) {
tvStatus.setText("Press a button to start a scan.");
tvResult.setText("Scan cancelled.");
}
}
}
}
The layout should resemble this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.scanbarcode.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="QR Code"
android:id="@+id/butQR"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:onClick="HandleClick" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Product"
android:id="@+id/butProd"
android:layout_alignBottom="@+id/butQR"
android:layout_toRightOf="@+id/butQR"
android:layout_toEndOf="@+id/butQR"
android:onClick="HandleClick" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Other"
android:id="@+id/butOther"
android:layout_alignBottom="@+id/butProd"
android:layout_toRightOf="@+id/butProd"
android:layout_toEndOf="@+id/butProd"
android:onClick="HandleClick" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Format"
android:id="@+id/tvStatus"
android:layout_below="@+id/butQR"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="@android:color/black"
android:textColor="@android:color/white" />
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Ready"
android:id="@+id/tvResult"
android:layout_below="@+id/tvStatus"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
Scan Formats
Notice how it is possbile to scan for a family of barcodes (using SCAN_MODE
) or a specific type of barcode (using SCAN_FORMATS
). If you know what type barcode is being decoded than setting a scan format to that single particular type may result in faster decoding (it will not be trying to run through all the barcode decoding algorithms), e.g. intent.putExtra("SCAN_FORMATS", "CODE_39"). For mutliple SCAN_FORMATS pass a comma separated list as shown in the example code.
SCAN_MODE | SCAN_FORMATS |
---|---|
QR_CODE_MODE | QR_CODE |
PRODUCT_MODE | EAN_13 |
EAN_8 | |
UPC_A | |
UPC_E | |
RSS_14 | |
RSS_EXPANDED | |
ONE_D_MODE | As for product mode plus... |
CODE_39 | |
CODE_93 | |
CODE_128 | |
ITF | |
CODABAR | |
DATA_MATRIX_MODE | DATA_MATRIX |
AZTEC_MODE | AZTEC (beta) |
PDF417_MODE | PDF_417 (beta) |
Example Project Code
Download some code that covers this article ready for importing into Android Studio. The code can also be accessed via the Android Example Projects page. A version of this article was produced for the Android Cookbook. Read the ZXing project Wiki for detailed information on the ZXing code.
See Also
Archived Comments
Thet on February 28, 2012 at 2:56 am
Hello
Thank you very much for this code. How about scanning several barcodes and listing the information of them? eg Barcode1 = product1 with price $100, Barcode 2= product2 with price $100 etc.
Tek Eye in reply to Thet on February 28, 2012 at 2:30 pm
That would make a good follow up topic. Unfortunately with the billions of product barcodes that exist it is not something that is possible with a stand alone app. For a networked app there is no universal database of barcodes that is easily and freely available. A tutorial using a company’s internal database will be produced at later date.
Thet in reply to Tek Eye on February 28, 2012 at 3:00 pm
Hi, how about if we use just google product for source? Just for normal flow. Because I am not that familiar with ListView.
Regards Thet
Tek Eye in reply to Thet on February 28, 2012 at 4:11 pm
See how ZXing does it at https://github.com/zxing/zxing (UPDATED) integrating with the Google Shopper App. Alternativley use an Intent with ACTION_WEB_SEARCH, or add the scanned code to the end of https://www.google.co.uk/search?tbm=shop&q=
(UPDATED) and give it to an Intent with ACTION_VIEW.
Daniel on April 19, 2012 at 5:19 pm
Hi nice work around with ZXing, but it crashes when it can’t find the barcode scanner app. But is it possible to check if the barcode scanner is installed and if not send the user to Google Play (UPDATED) for installing.
Best Daniel
Tek Eye in reply to Daniel on April 20, 2012 at 1:51 pm
Yes that can be done. Your App can prompt the user to download the ZXing App if it is not installed when your App attempts to read a barcode. Look at the IntentIntegrator.java class in android-integration of the main trunk. Basically is uses PackageManager to query for an intent and then prompts to download it. (UPDATED - Now see https://github.com/zxing/zxing/wiki/Scanning-Via-Intent).
ahmad upi on May 1, 2012 at 7:24 am
thanks for the tutorial, it’s very rewarding for me.
Simon S on November 21, 2012 at 3:57 pm
Brilliant. Thank you.
Zoran Kukulj on January 27, 2013 at 3:01 pm
Hi,it is a great example, thanks for it, but seems to erase found result when changing position of the phone. What could cause that kind of behaviour?
Tek Eye in reply to Zoran Kukulj on January 28, 2013 at 11:05 am
Thankyou.
When the phone rotates by default activities are torn down and recreated (to allow screens to redraw for the new orientation). Controls, with ids, that users can change preserve their current state when this happens (see Saving Activity State in an App when it's Interrupted). Here a TextView was used (not user changable) so the state was not preserved. Either change the result TextView to an EditText, or use onSaveInstanceState and onRestoreInstanceState:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("SCAN_RESULT", (String) ((TextView)findViewById(R.id.tvResult)).getText());
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
((TextView)findViewById(R.id.tvResult)).setText(savedInstanceState.getString("SCAN_RESULT"));
}
Zoran Kukulj in reply to Tek Eye on January 28, 2013 at 11:48 am
Hi, Tek Eye, I have changed TextView-s to EditText-s and it works perfectly! But I have quite a bit of a hassle with attaching it to a widget.
mScan.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(“com.google.zxing.client.android.SCAN”);
intent.setPackage(“com.google.zxing.client.android”);
intent.putExtra(“SCAN_MODE”, “QR_CODE_MODE”);
startActivity(intent, 0);
return true;
}
return false;
};
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == 0) {
EditText edit = (EditText) view.findViewById(R.id.T1);
EditText edit2 = (EditText) view.findViewById(R.id.T2);
if (resultCode == RESULT_OK) {
String contents = intent.getStringExtra(“SCAN_RESULT”);
String format = intent.getStringExtra(“SCAN_RESULT_FORMAT”);
edit.setText(intent.getStringExtra(“SCAN_RESULT_FORMAT”));
edit2.setText(intent.getStringExtra(“SCAN_RESULT”));
} else if (resultCode == RESULT_CANCELED) {
edit.setText(“barkod”);
edit2.setText(“vrsta barkoda”);
I here there are several errors I am unable to find fix. Could You share some thoughts on how the error occurs? Many thanks!
Tek Eye in reply to Zoran Kukulj on January 28, 2013 at 6:48 pm
Hi,
stackoverflow.com is a good site to get help on errors in your code. Without seeing your entire project I cannot provide much help. However, the code you posted can be simplified:
mScan.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
startActivityForResult(intent,0);
}
});
}
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == 0) {
EditText edit = (EditText) findViewById(R.id.T1);
EditText edit2 = (EditText) findViewById(R.id.T2);
if (resultCode == RESULT_OK) {
String contents = intent.getStringExtra("SCAN_RESULT");
String format = intent.getStringExtra("SCAN_RESULT_FORMAT");
edit.setText(format);
edit2.setText(contents);
} else if (resultCode == RESULT_CANCELED) {
edit.setText("barkod");
edit2.setText("vrsta barkoda");
}
}
}
Zoran Kukulj in reply to Tek Eye on January 28, 2013 at 7:19 pm
many,many thanks!
vohaongtuit on June 26, 2013 at 10:11 am
Help! when i run project and click button Qrcode or Barcode, it stop & messenge from logcat: 06-26 10:08:13.602: D/gralloc_goldfish(1043): Emulator without GPU emulation detected. 06-26 10:09:37.242: I/Choreographer(1043): Skipped 58 frames! The application may be doing too much work on its main thread.
Tek Eye in reply to vohaongtuit on June 26, 2013 at 1:14 pm
Are you trying to run it on an AVD (Android Virtual Device)? You need a physical device with a camera to test the code. You need to look deeper into the log file for the message relating directly to the fault, these two messages are misleading, see https://stackoverflow.com/questions/8968238/gralloc-goldfish634-emulator-without-gpu-emulation-detected and https://stackoverflow.com/questions/11266535/meaning-of-choreographer-messages-in-logcat.
Yury on December 17, 2014 at 5:43 am
Hey, great starter for the project, I’ve made some modifications and it’s working well!
However, I ran into an issue on some of the Android devices. When I tap on one the buttons that call SCAN activity I get an error “Unfortunately, Google Search has stopped”. Maybe you know the cause of this error?
This is what I get in the Eclipse:
12-17 13:38:33.641: V/InputMethodManager(2975): Starting input: view=com.android.internal.policy.impl.PhoneWindow$DecorView@414fcf78 12-17 13:38:33.642: V/InputMethodManager(2975): Starting input: tba=android.view.inputmethod.EditorInfo@41513e88 ic=null 12-17 13:38:33.722: D/jdwp(2975): processIncoming 12-17 13:38:33.722: D/jdwp(2975): handlePacket : cmd=0x1, cmdSet=0xB, len=0x13, id=0x2C5, flags=0x0, dataLen=0x8 12-17 13:38:33.723: D/jdwp(2975): processIncoming 12-17 13:38:33.723: D/jdwp(2975): handlePacket : cmd=0x1, cmdSet=0xB, len=0x13, id=0x2C6, flags=0x0, dataLen=0x8 12-17 13:38:42.290: I/SurfaceTextureClient(2975): [STC::queueBuffer] this:0x518e7480, api:2, last queue time elapsed :8651 ms 12-17 13:38:42.290: I/SurfaceTextureClient(2975): [0x518e7480] frames:2, duration:8.651000, fps:0.231168 12-17 13:38:42.301: V/Provider/Setting(2975): from db cache, name = sound_effects_enabled value = 0 12-17 13:38:42.352: D/ActivityThread(2975): ACT-AM_ON_PAUSE_CALLED ActivityRecord{414f56b8 token=android.os.BinderProxy@414f1c00 {biz.tekeye.scanbarcode/biz.tekeye.scanbarcode.Main}} 12-17 13:38:42.399: D/ActivityThread(2975): ACT-PAUSE_ACTIVITY handled : 1 / android.os.BinderProxy@414f1c00 12-17 13:38:43.429: D/ActivityThread(2975): SEND_RESULT handled : 0 / ResultData{token=android.os.BinderProxy@414f1c00 results[ResultInfo{who=null, request=0, result=0, data=null}]} 12-17 13:38:43.430: D/ActivityThread(2975): ACT-AM_ON_RESUME_CALLED ActivityRecord{414f56b8 token=android.os.BinderProxy@414f1c00 {biz.tekeye.scanbarcode/biz.tekeye.scanbarcode.Main}} 12-17 13:38:43.431: D/ActivityThread(2975): ACT-RESUME_ACTIVITY handled : 1 / android.os.BinderProxy@414f1c00 12-17 13:38:43.441: V/InputMethodManager(2975): onWindowFocus: null softInputMode=288 first=true flags=#8010100 12-17 13:38:43.441: V/InputMethodManager(2975): Starting input: view=com.android.internal.policy.impl.PhoneWindow$DecorView@414fcf78 12-17 13:38:43.442: V/InputMethodManager(2975): Starting input: tba=android.view.inputmethod.EditorInfo@415191a8 ic=null 12-17 13:38:43.443: V/InputMethodManager(2975): START INPUT: com.android.internal.policy.impl.PhoneWindow$DecorView@414fcf78 ic=null tba=android.view.inputmethod.EditorInfo@415191a8 controlFlags=#104 12-17 13:38:43.446: V/InputMethodManager(2975): Starting input: Bind result=InputBindResult{com.android.internal.view.IInputMethodSession$Stub$Proxy@41519d38 com.android.inputmethod.latin/.LatinIME #45} 12-17 13:38:56.759: I/SurfaceTextureClient(2975): [STC::queueBuffer] this:0x518e7480, api:2, last queue time elapsed :13250 ms 12-17 13:38:56.759: I/SurfaceTextureClient(2975): [0x518e7480] frames:3, duration:13.360000, fps:0.224543 12-17 13:38:56.985: V/Provider/Setting(2975): from settings cache , name = sound_effects_enabled value = 0 12-17 13:38:57.090: D/ActivityThread(2975): ACT-AM_ON_PAUSE_CALLED ActivityRecord{414f56b8 token=android.os.BinderProxy@414f1c00 {biz.tekeye.scanbarcode/biz.tekeye.scanbarcode.Main}} 12-17 13:38:57.133: D/ActivityThread(2975): ACT-PAUSE_ACTIVITY handled : 1 / android.os.BinderProxy@414f1c00 12-17 13:38:58.317: D/ActivityThread(2975): ACT-DESTROY_ACTIVITY handled : 1 / android.os.BinderProxy@414f1c00
Yury on December 17, 2014 at 6:44 am
I resolved the issue, some devices did not have the zxing library and were calling google search service by default. I installed barcode scanner app from zxing and now the application works on all of the devices.
Author:Daniel S. Fowler Published: Updated: