Change the frame position and size of the Zxing Barcode scanning library for Android.
Most of us tried the Zxing Qr-code library for android and maybe you wanted to change the barcode view frame size or position in your layout.
Please follow the coming sample:
You can clone the repo from here:
1# Implement the library with custom scanner activity:
If you used this library before I’m sure you know how to implement the width and height for the barcodeView frames but for now let’s do it.
First, we will implement the dependencies for our project.
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
After that, we will implement the custom activity for the barcode scanner, for the activity_custom_scanner.xml file.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CustomScannerActivity">
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/zxing_barcode_scanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:zxing_scanner_layout="@layout/custom_barcode_scanner">
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
</RelativeLayout>
and we will implement the custom_barcode_scanner.xml file that contains the barcode view and viewfinder.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.journeyapps.barcodescanner.BarcodeView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_barcode_surface"/>
<com.journeyapps.barcodescanner.ViewfinderView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_viewfinder_view"
app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
app:zxing_result_view="@color/zxing_custom_result_view"
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
app:zxing_viewfinder_laser_visibility="true"
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>
</merge>
and in CustomScannerActivity class
We will create a new instance of CaptureManager
private lateinit var capture: CaptureManager
and initialize it
private fun initializeQrScanner(savedInstanceState: Bundle?) = with(binding) {
capture = CaptureManager(this@CustomScannerActivity, zxingBarcodeScanner)
capture.initializeFromIntent(intent, savedInstanceState)
capture.setShowMissingCameraPermissionDialog(false)
capture.decode()
}
and will tie it with the activity life cycle
override fun onResume() {
super.onResume()
capture.onResume()
}
override fun onPause() {
super.onPause()
capture.onPause()
}
override fun onDestroy() {
super.onDestroy()
capture.onDestroy()
}
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
super.onSaveInstanceState(outState, outPersistentState)
capture.onSaveInstanceState(outState)
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
return binding.zxingBarcodeScanner.onKeyDown(keyCode, event) || super.onKeyDown(
keyCode,
event
)
}
and will add the permission result for camera permission
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String?>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
capture.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
for now our custom activity is implemented. Now we will just create a new ScanOption instance to launch the library and add the custom activity instead of the default one.
So in the activity_main.xml, I will add a button to launch the scanner
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/launchScanner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/launch_the_scanner"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
and for the implementation of Main_Activity.
private fun launchScanner() {
val options = ScanOptions()
.setOrientationLocked(false)
.setCaptureActivity(CustomScannerActivity::class.java)
.setCameraId(0)
.setBeepEnabled(false)
.setBarcodeImageEnabled(true)
.setDesiredBarcodeFormats(ScanOptions.QR_CODE)
barcodeLauncher.launch(options)
}
in the above code, we implement our CustomScannerActivity to use it instead of the default one and implement the launcher to wait for the results.
private val barcodeLauncher = registerForActivityResult(
ScanContract()
) { result: ScanIntentResult ->
if (!result.contents.isNullOrEmpty()) {
Toast.makeText(this, "Scan Result: ${result.contents}", Toast.LENGTH_SHORT).show()
}else{
// CANCELED
}
}
after this implementation, this will be the result
2# Change Frame Size:
For now, everything is working correctly but what if we need to change the sizing of the scanner frame?
Fortunately, they added a very easy way to change the frame sizing by adding these two lines in BarodeView.
app:zxing_framing_rect_width="100dp"
app:zxing_framing_rect_height="50dp"
So the frame size will change to be like this
3# Change Frame Position:
Unfortunately, this feature has not been implemented in the library until now so we will implement it ourselves 🤓.
First of all, we will create a new class that extends the BarcodeView class.
In this sample, we will try to change the position to the top but you can change it as you like.
class TopRectBarcodeView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : BarcodeView(context, attrs, defStyleAttr) { }
Here we create the TopRectBarcodeView class that extends BarcodeView class and now we will override the getFramingRectSize method and make some changes to it.
override fun getFramingRectSize(): Size {
return Size(Resources.getSystem().displayMetrics.widthPixels, dpToPx(212))
}
In the above code we implement the frame size programmatically so that in width we get the system's actual width and in height, we implement the height we need for our purpose.
“ But what is the dpToPx function?
You can specify the height or width in DP dimension just in pixel so here we implement a new function that converts the DP dimension to Pixel.
private fun dpToPx(dp: Int): Int {
return ((dp * Resources.getSystem().displayMetrics.density).roundToInt())
}
So for now implement our frame size programmatically and convert the DP to PX, How we will specify the position of the frame?
Now we reach the last function in this horrible class is calculateFramingRect where we will draw the frame rectangle programmatically so here we override it and change the default RECT.
before implementing the function we need to understand how the shape will draw in the below image we must add the left and top in the same destination from X and Y and also the right and bottom.
So we need to specify the four directions to can draw the frame RECT shape.
In our sample, we have an image with borders that we need to show in the frame as the border of it with a height of 212 DP so for this reason, I add the actual height of the frame to the same.
So here we implement the image border in imageView in the activity_custom_scanner.xml to be like
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CustomScannerActivity">
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/zxing_barcode_scanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:zxing_scanner_layout="@layout/custom_barcode_scanner"/>
<ImageView
android:id="@+id/barcodeBorderImage"
android:layout_width="0dp"
android:layout_height="212dp"
android:layout_marginStart="22dp"
android:layout_marginTop="194dp"
android:layout_marginEnd="22dp"
android:elevation="8dp"
android:scaleType="fitXY"
android:src="@drawable/ic_border"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
“ and in custom_barcode_scanner.xml we will delete the frame size we implement before because we implement it in the TopRectBarcodeView and change the BarcodeView to our custom one so the custom_barcode_Scanner.xml will be like that.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.mhmdawad.zxingbarcodescanner.TopRectBarcodeView
android:id="@+id/zxing_barcode_surface"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.journeyapps.barcodescanner.ViewfinderView
android:id="@+id/zxing_viewfinder_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
app:zxing_result_view="@color/zxing_custom_result_view"
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
app:zxing_viewfinder_laser_visibility="true"
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask" />
</merge>
everything is implemented just missed the important function calculateFramingRect
override fun calculateFramingRect(
container: Rect?,
surface: Rect?
): Rect {
// create new rect instance that hold the container.
val intersection = Rect(container)
// specify the position of left direction.
intersection.left = dpToPx(26)
// specify the position of top direction.
intersection.top = dpToPx(198)
// specify the position of right direction.
intersection.right =
framingRectSize.width - dpToPx(26)
// specify the position of bottom direction.
intersection.bottom =
framingRectSize.height + dpToPx(190)
return intersection
}
In the above code, we specify the four directions but you can see that we add DP’s more than the margin in the XML layout that just to hide the frame border under the border picture.
and we’re done! Now hit the Run App Button and wait till it installs the app and WALLA!
our custom barcode view will fit all screens and now we can change the frame position how to fit our purpose.
The GitHub link for the entire code
Thank You For Reading :)