QR Code Scanner di Android dengan ZXing
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.
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.
- Anda paksa dependency yang konflik tersebut ikut dengan versi Anda caranya ialah dengan menambahkan dependency konflik tersebut ke file build.gradle
- 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 keywordexclude
seperti berikut dimana, maksud dari syntax tersebut adalah mengecualikan dependency daricom.android.support
milik sime.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.
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.
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.