QR Code Scanner di Android dengan ZXing

Yudi Setiawan
Nusanet Developers
Published in
6 min readJun 22, 2018
Photo by Jessica Lewis from Pexels

Pengenalan

Untuk membaca barcode Anda harus menggunakan alat khusus yang bernama Barcode Reader. Umumnya barcode reader dipakai pada toko-toko supermarket untuk menentukan berapa total harga yang harus dibayar oleh konsumen. Seiring berevolusinya teknologi sekarang dengan berbekal smartphone sudah bisa menggantikan fungsi dari barcode reader. Dari segi fungsinya jelas barcode reader tetap dipakai di toko-toko supermarket karena, dari segi bentuk fisik dan fungsi utamanya yang jelas untuk melakukan pembacaan barcode. Sedangkan, smartphone memiliki fungsi lain selain untuk membaca barcode. Selain itu, dari segi harga juga lebih murah perangkat barcode reader dibandingkan dengan smartphone. Namun, lain cerita untuk di pergudangan dimana, beberapa pergudangan di Indonesia sudah ada yang pakai teknologi barcode reader sehingga pencatatan barang-barang di pergudangan lebih mudah dan praktis ketimbang harus mencatat barang-barang tersebut secara tertulis.

Apa itu ZXing?

Dikutip dari situs github-nya bahwa ZXing adalah sebagai berikut.

ZXing (“Zebra crossing”) is an open-source, multi-format 1D/2D barcode image processing library implemented in Java, with ports to other languages.

Jadi, ZXing merupakan pustaka kode terbuka untuk menangani fungsi-fungsi barcode scanner. Untuk penggunaannya di Android terdapat banyak pustaka-pustaka lain yang bisa Anda gunakan dan salah satunya yang bisa Anda gunakan adalah sebagai berikut.

Developer tersebut telah menulis ulang fungsi-fungsi utama ZXing agar lebih mudah digunakan di Android.

Memulai Contoh Projek

Buat projek di Android Studio dengan nama Scannerku dan pastikan Anda meng-checklist Kotlin sebagai bahasa pemrogramannya.

Konfigurasi build.gradle

Berdasarkan dari situs github-nya https://github.com/dm77/barcodescanner kita harus menambahkan dependency berikut pada file build.gradle.

implementation 'me.dm7.barcodescanner:zxing:1.9.8'

Apakah Anda mendapatkan pesan error berikut setelah melakukan sync gradle.

Konflik dependency build.gradle

Pada gambar diatas, pesan error tersebut terjadi dikarenakan adanya konflik antara dependency yang Anda masukkan. Dimana, konflik dependency diatas terjadi karena ada konflik antara dependency com.android.support:appcompat-v7:27.1.1 yang telah memakai versi terbaru sedangkan, dependency me.dm7.barcodescanner:zxing:1.9.8 ada memakai dependency bawaannya sendiri namun, dengan versi yang berbeda yang dari punya Anda sehingga terjadilah konflik tersebut. Jika Anda sorot kursor Anda ke pesan error-nya maka, Android Studio akan memberitahu dependency apa saja yang konflik. Contoh, diatas yang konflik itu adalah com.android.support:support-media-compat yang masih memakai versi 25.3.1. Untuk solusinya ada 2 yaitu, sebagai berikut.

  1. Anda paksa dependency yang konflik tersebut ikut dengan versi Anda caranya ialah dengan menambahkan dependency konflik tersebut ke file build.gradle
  2. Yang satu lagi ialah Anda membuat pengecualian di dependency parent-nya yang menyebabkan konflik tadi. Pada contoh diatas yang menyebabkan konflik adalah dependency me.dm7.barcodescanner:zxing:1.9.8 dan untuk membuat pengecualiannya tinggal Anda tambahkan keyword exclude seperti berikut dimana, maksud dari syntax tersebut adalah mengecualikan dependency dari com.android.support milik si me.dm7.barcodescanner:zxing:1.9.8 tidak ikut pada projek Anda.
implementation('me.dm7.barcodescanner:zxing:1.9.8') {
exclude group: 'com.android.support'
}

Permissions

Untuk menggunakan fitur kamera Anda harus menambahkan permission kamera di AndroidManifest.xml seperti berikut.

<uses-permission android:name="android.permission.CAMERA" />

Layout Utama

Silakan buka file activity_main.xml dan isi dengan source code berikut.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
android:padding="16dp"
tools:context=".MainActivity">

<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.8" />

<TextView
android:id="@+id/text_view_qr_code_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="QR Code Value"
android:textSize="18sp"
android:gravity="center_horizontal"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/guideline" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button_reset"
android:text="Reset"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_view_qr_code_value"
/>

<FrameLayout
android:id="@+id/frame_layout_camera"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Dan output dari source code layout diatas adalah seperti berikut.

Design Layout Utama

Penambahan Fungsi Utama

Selanjutnya, Anda buka file MainActivity.kt dan isi dengan source code berikut.

package com.ysn.scannerku

import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import com.google.zxing.Result
import kotlinx.android.synthetic.main.activity_main.*
import me.dm7.barcodescanner.zxing.ZXingScannerView

class MainActivity : AppCompatActivity(), ZXingScannerView.ResultHandler, View.OnClickListener {

private lateinit var mScannerView: ZXingScannerView
private var isCaptured = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initScannerView()
initDefaultView()
button_reset.setOnClickListener(this)
}

private fun initScannerView() {
mScannerView = ZXingScannerView(this)
mScannerView.setAutoFocus(true)
mScannerView.setResultHandler(this)
frame_layout_camera.addView(mScannerView)
}

override fun onStart() {
mScannerView.startCamera()
doRequestPermission()
super.onStart()
}

private fun doRequestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(Manifest.permission.CAMERA), 100)
}
}
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
100 -> {
initScannerView()
}
else -> {
/* nothing to do in here */
}
}
}

override fun onPause() {
mScannerView.stopCamera()
super.onPause()
}

private fun initDefaultView() {
text_view_qr_code_value.text = "QR Code Value"
button_reset.visibility = View.GONE
}

override fun handleResult(rawResult: Result?) {
text_view_qr_code_value.text = rawResult?.text
button_reset.visibility = View.VISIBLE
}

override fun onClick(view: View?) {
when (view?.id) {
R.id.button_reset -> {
mScannerView.resumeCameraPreview(this)
initDefaultView()
}
else -> {
/* nothing to do in here */
}
}
}

}

Penjelasan:

Untuk menambahkan fungsi scanner pada ZXing Anda harus menggunakan ZXingScannerView.ResultHandler dimana, callback ini berfungsi untuk menerima deteksi otomatis dari hasil scanner si ZXing. Selanjutnya, initialize object ZXingScannerView dan masukkan ke FrameLayout yang sudah Anda buat di xml-nya tadi. Kemudian, dikarenakan fitur ini menggunakan kamera jelas Anda memerlukan permission camera dimana, untuk Android versi M keatas itu memerlukan runtime permission sehingga pada kode diatas Anda ada menambahkan runtime permission. Kemudian, ketika method handleResult terpanggil maka, Anda ada melakukan setText pada text_view_qr_code_value dan menampilkan button_reset . Selanjutnya, pada button_reset Anda juga ada menambahkan onClickListener dimana, didalamnya ada fungsi untuk mereset kamera dan semua view-nya kembali ke awal. Dan terakhir, jangan lupa untuk menambahkan fungsi startCamera di method onStart dan stopCamera di method onPause .

Output

Berikut adalah output dari projek yang Anda buat.

Output Projek

Untuk projeknya bisa Anda lihat di github.

Custom View

“Lalu bagaimana jika Anda ingin mengubah layout scanner-nya?” Bisa dong. Berikut saya berikan Anda contoh untuk membuat layout scanner-nya bisa bergerak keatas bawah pada garis indikator scanner-nya. Silakan buat satu class baru dengan nama CustomViewFinderView.kt dan isi dengan source code berikut.

package com.ysn.scannerku

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import me.dm7.barcodescanner.core.ViewFinderView

class CustomViewFinderView constructor(context: Context) : ViewFinderView(context) {

private val paint = Paint()
private val SCANNER_ALPHA = intArrayOf(0, 64, 128, 192, 255, 192, 128, 64)
private var scannerAlpha: Int = 0
private var cntr = 0
private var goingup = false
private val POINT_SIZE = 10
private val ANIMATION_DELAY = 80L

init {
paint.color = Color.WHITE
paint.isAntiAlias = true
setSquareViewFinder(true)
setBorderColor(Color.parseColor("#35B781"))
setLaserColor(Color.parseColor("#35B781"))
setLaserEnabled(true)
}

override fun drawLaser(canvas: Canvas?) {
/*super.drawLaser(canvas)*/
paint.alpha = SCANNER_ALPHA[scannerAlpha]
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.size
var middle = framingRect.height() / 2 + framingRect.top
middle += cntr
if (cntr < framingRect.top - mBorderLineLength - 10 && !goingup) {
canvas?.drawRect((framingRect.left + 2).toFloat(), (middle - 1).toFloat(), (framingRect.right - 1).toFloat(), (middle + 2).toFloat(), mLaserPaint)
cntr += 4
}

if (cntr >= framingRect.top - mBorderLineLength - 10 && !goingup) goingup = true

if (cntr > -framingRect.top + mBorderLineLength + 10 && goingup) {
canvas?.drawRect((framingRect.left + 2).toFloat(), (middle - 1).toFloat(), (framingRect.right - 1).toFloat(), (middle + 2).toFloat(), mLaserPaint)
cntr -= 4
}

if (cntr <= -framingRect.top + mBorderLineLength + 10 && goingup) goingup = false

postInvalidateDelayed(ANIMATION_DELAY,
framingRect.left - POINT_SIZE,
framingRect.top - POINT_SIZE,
framingRect.right + POINT_SIZE,
framingRect.bottom + POINT_SIZE)
}

}

class tersebut Anda extends ke ViewFinderView milik ZXing sehingga Anda bisa mengubah layout-nya. Pada class yang barusan Anda buat ada meng-override method drawLaser dimana method ini berfungsi untuk menggambar laser pada layout-nya dan Anda ada mengubah pergerakan lasernya pada method tersebut. Selanjutnya, pada class MainActivity.kt silakan Anda ubah kode berikut

mScannerView = ZXingScannerView(this)

menjadi seperti berikut.

mScannerView = object : ZXingScannerView(this) {
override fun createViewFinderView(context: Context?): IViewFinder {
return CustomViewFinderView(context!!)
}
}

dimana, Anda meng-inisialisasikan object mScannerView dan menambahkan class CustomViewFinderView yang tadi Anda buat. Dan berikut adalah outputnya.

Output Custom View

--

--