I have received quite a few comments on my recent posts Android Barcode Scanner , Android Aadhaar Card Scanner and Android QR Code scanner about integrating the scanner inside a fragment instead of, directly in the activity.
So this post will cover full details of how to integrate barcode scanner into a fragment.
Step 1: create application and include dependency
We will use Zxing library for scanning the barcodes. There is an opensource project named ZXing Android Minimal which has embeded this library so we will use this project in our app.Create a blank project in android studio and add ZXing Android Minimal as dependency in your build.gradle file located in app folder.
// Added by raj
repositories {
mavenCentral()
maven {
url "http://dl.bintray.com/journeyapps/maven"
}
}
// Added by raj ends
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.3'
// Added by raj
// Supports Android 4.0.3 and later (API level 15)
compile 'com.journeyapps:zxing-android-embedded:2.0.1@aar'
// Supports Android 2.1 and later (API level 7), but not optimal for later Android versions.
// If you only plan on supporting Android 4.0.3 and up, you don't need to include this.
compile 'com.journeyapps:zxing-android-legacy:2.0.1@aar'
// Convenience library to launch the scanning and encoding Activities.
// It automatically picks the best scanning library from the above two, depending on the
// Android version and what is available.
compile 'com.journeyapps:zxing-android-integration:2.0.1@aar'
// Version 3.0.x of zxing core contains some code that is not compatible on Android 2.2 and earlier.
// This mostly affects encoding, but you should test if you plan to support these versions.
// Older versions e.g. 2.2 may also work if you need support for older Android versions.
compile 'com.google.zxing:core:3.0.1'
// Added by raj ends
}
Step 2 create scan fragment:
Create a new class in your project named ScanFragment extending Fragment.
public class ScanFragment extends Fragment {
}
We will use intent to launch the barcode scanner, so override the onCreate function of fragment and launch scan
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentIntegrator integrator = new IntentIntegrator(this.getActivity()).forSupportFragment(this);
// use forSupportFragment or forFragment method to use fragments instead of activity
integrator.setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES);
integrator.setPrompt(this.getString(R.string.scan_bar_code));
integrator.setResultDisplayDuration(0); // milliseconds to display result on screen after scan
integrator.setCameraId(0); // Use a specific camera of the device
integrator.initiateScan();
}
The intent will require the fragment to have onActivityResult method so that scan result can be returned. Add method onActivityResult to the fragment. I have also declared two string variables codeContent and codeFormat to hold scan data and format
/**
* function handle scan result
* @param requestCode scanned code
* @param resultCode result of scanned code
* @param intent intent
*/
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
//retrieve scan result
IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
codeContent = scanningResult.getContents();
codeFormat = scanningResult.getFormatName();
}
Step 3 Integrate fragment in activity:
After creating the scan fragment, we have to invoke this from activity. Create an activity class in your project named HomeActivity. Add a layout in the layout folder named activity_home.xml. Add a button in the layout to start the scan. Add two text views to display the scan data and barcode format. Also add a layout to add fragment programmatically.
<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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".HomeActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_blog_url"
android:text="@string/btn_scan_now"
android:layout_centerHorizontal="true"
android:onClick="scanNow"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:id="@+id/btn_scan_now" />
<TextView
android:id="@+id/scan_format"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:layout_below="@+id/btn_scan_now"
android:gravity="center_horizontal"
android:layout_marginBottom="@dimen/activity_vertical_margin" />
<TextView
android:id="@+id/scan_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:layout_below="@+id/scan_format"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:gravity="center_horizontal" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/scan_fragment"
android:orientation="horizontal"></LinearLayout>
</LinearLayout>
</RelativeLayout>
We have added scanNow function to the button in layout, now add this function to HomeActivity. This function will create and add the fragment to the current activity.
/**
* event handler for scan button
* @param view view of the activity
*/
public void scanNow(View view){
// add fragment
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ScanFragment scanFragment = new ScanFragment();
fragmentTransaction.add(R.id.scan_fragment,scanFragment);
fragmentTransaction.commit();
}
Step 4 Return scan data from fragment to activity:
The advantage of using barcode scanner in a fragment is to re-use it with any activity. However we still need a way to send scanned data back to the parent activity. We can add a public method in the activity and call this method from fragment but this will make our fragment dependent on the activity class. So to make our fragment not dependent on the activity class we will create an interface with method scanResultData. Our activity will implement this interface and override this method.
Create a new interface in your project folder named ScanResultReceiver
public interface ScanResultReceiver {
/**
* function to receive scanresult
* @param codeFormat format of the barcode scanned
* @param codeContent data of the barcode scanned
*/
public void scanResultData(String codeFormat, String codeContent);
public void scanResultData(NoScanResultException noScanData);
}
Now update HomeActivity to implement this interface and override scanResultData method. We will use the data received in this method to populate our text views.
@Override
public void scanResultData(String codeFormat, String codeContent){
// display it on screen
formatTxt.setText("FORMAT: " + codeFormat);
contentTxt.setText("CONTENT: " + codeContent);
}
Now update the ScanFragment to use this method and send scanned data back to activity. Updated onActivityResult method will look like:
/**
* function handle scan result
* @param requestCode scanned code
* @param resultCode result of scanned code
* @param intent intent
*/
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
//retrieve scan result
IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
ScanResultReceiver parentActivity = (ScanResultReceiver) this.getActivity();
if (scanningResult != null) {
//we have a result
codeContent = scanningResult.getContents();
codeFormat = scanningResult.getFormatName();
// send received data
parentActivity.scanResultData(codeFormat,codeContent);
}else{
// send exception
parentActivity.scanResultData(new NoScanResultException(noResultErrorMsg));
}
}
Step 5 Handle no scan data error:
There will be a case when we will not receive scan result in onActivityResult we need to handle this case and let the parent activity decide what to do. So I have created a custom exception NoScanResultException which will be returned back to the activity. I have used method overloading and added scanResultData twice in interface ScanResultReceiver
Exception class
public class NoScanResultException extends Exception {
public NoScanResultException() {}
public NoScanResultException(String msg) { super(msg); }
public NoScanResultException(Throwable cause) { super(cause); }
public NoScanResultException(String msg, Throwable cause) { super(msg, cause); }
}
Lets update HomeActivity to implement the second scanResultData. Just for simplicity I am displaying a toast with the error message.
@Override
public void scanResultData(NoScanResultException noScanData) {
Toast toast = Toast.makeText(this,noScanData.getMessage(), Toast.LENGTH_SHORT);
toast.show();
}
This is a very basic implementation of barcode scanner in fragment. As always the full source code can be found on my github profile under android-barcode-scanner-in-fragment
16 Responses to “Android Barcode Scanner in Fragment”
March 15, 2017
DonI am getting the following runtime error relating to camera… Do you have any suggestions? Thanks!
3-15 00:17:29.821 16782-16782/in.whomeninja.android_barcode_scanner I/com.google.zxing.client.android.camera.open.OpenCameraInterface: Opening camera #0
03-15 00:17:29.821 16782-16782/in.whomeninja.android_barcode_scanner W/CameraBase: An error occurred while connecting to camera: 0
03-15 00:17:29.821 16782-16782/in.whomeninja.android_barcode_scanner W/CaptureActivity: Unexpected error initializing camera
java.lang.RuntimeException: Fail to connect to camera service
at android.hardware.Camera.(Camera.java:568)
March 16, 2017
Rajinder DeolHi Don,
try to connect a real device to deploy and test. Emulators sometimes does not work for cameras.
Also please check the API version of your app in your build.gradle file check: https://github.com/rajdeol/android-barcode-scanner-in-fragment/blob/master/app/build.gradle
March 16, 2017
DonThanks for the quick reply, I have the app working now. I’m not exactly sure what was causing the issue originally but I was testing the app on a Samsung Tab A, not an emulator. I originally upgraded the Gradle build files and commented out the one dependency since this app will only run on lollipop and later. Ultimately I started with the clean slate, exactly as you zipped it and then slowly worked through updating the build. I ended up with everything working now. I did make other change and switched to a new version, com.journeyapps:zxing-android-embedded:3.4.0
The only issue I came across with that version is that integrator.setResultDisplayDuration(0) was removed from this build due to redundancy and coding issues. As far as I can tell, everything functions fine. Now I just need to integrate into the app I am building.
Thanks again.
March 17, 2017
Rajinder Deolglad to know that the issue is resolved, all the best for your app. Let me know if you need any help.
April 13, 2017
BozoThe problem I have is : scanner does not work in fragment and that was the point. It runs in full screen. It scans but I need it in fragment, not in full screen, since I need to place other things on the same screen. Actually the fragment id=”@+id/scan_fragment” is totally ignored and scanner runs in its own space somehow. It works exactly the same like when I run it with
fragmentTransaction.add (scanFragment, “”);
April 18, 2017
Rajinder DeolHi Bozo,
the default zxing implementation is not that extendable so it ignores ui changes. I have used another implementation in my post Android Barcode Scanner vertical orientation (portrait mode) which allow you to modify the view.
Have a look at it and let me know if it works.
April 22, 2017
DonDid you have any luck running the barcode within a fragment?
April 24, 2017
Rajinder DeolHi Don,
it works in fragment but in full screen mode, checkout my post Android Barcode Scanner in Fragment
October 6, 2017
ajayafter scaning barcode camera is not stop
October 7, 2017
Rajinder DeolHi Ajay,
are you getting any error in the error logs ? can you explain a bit more.
November 5, 2017
SantiagoHi, im trying to use your code but i cant pass this as parametre in this line
IntentIntegrator integrator = new IntentIntegrator(getActivity()).forFragment(this);
Please i hope that you can help me, regards from Colombia.
November 6, 2017
Rajinder DeolHi Santiago,
can you please share the error you are getting.
March 6, 2020
YongWook KimThank you brother!! This helped to complete the task. T_T
November 9, 2020
ThomasHello Rajinder,
I’ve written a little android app including SQLite and I want to integrate the barcode scanner.But I have some headache regarding the implemtation in a fragment. So I need some help (payment via paypal?). Please let me know, if you have a little bit time for me…
Thanks a lot and kind regards
Thomas
November 9, 2020
Rajinder DeolHi Thomas,
sure I can help, what is the issue you are facing ?
November 9, 2020
ThomasHello Rajinder, first thanks for your fast reply. The application is for a logistic company. On the main activity there is an EditView and a container, where the barcode scanner should be shown.When I click a button, the barcode scanner (ZXing) should be opened in a fragment, and this fragment should be shown in a container on the main activity. As soon as the barcode is recognised, the barcode should be written into the EditView on the main activity. If the bark on scanner can’t recognise the barcode, this would be a possibility to stop the process. Maybe it’s easier to explain, if I can send you a little video or some screenshots.Is there any email address, where I can send it to? Thanks a lot and best regards, Thomas