Android Barcode / QR Code Scanner using Google Mobile Vision – Building Movie Tickets App
Saat ini Barcode dan QR Codes banyak digunakan di banyak aplikasi mobile. Dalam QR Code Anda bisa menyimpan informasi seperti teks, sms, email, url, image, audio dan beberapa format lainnya. Di Android Anda bisa mengekstrak informasi yang tersimpan di barcode dengan menggunakan Google Vision Library. Meskipun ada banyak perpustakaan lain yang tersedia, perpustakaan penglihatan google paling baik dipertimbangkan karena tidak hanya menyediakan pembacaan kode batang tetapi juga memiliki fitur lain seperti deteksi wajah, pendeteksian teks.
Pada artikel ini kita akan belajar bagaimana menggunakan perpustakaan google vision dengan membuat aplikasi pemindaian tiket film sederhana.
Pada artikel ini kita akan belajar bagaimana menggunakan perpustakaan google vision dengan membuat aplikasi pemindaian tiket film sederhana.
- Google Mobile Vision API
API Google Mobile Vision membantu menemukan objek dalam gambar atau video. Ini menyediakan fungsionalitas seperti deteksi wajah, deteksi teks dan deteksi barcode. Semua fungsi ini bisa digunakan secara terpisah atau digabungkan bersama.
Artikel ini bertujuan untuk menjelaskan deteksi barcode dengan skenario use case realtime. Kita bisa melihat banyak aplikasi pemindaian barcode yang digunakan di supermarket, bioskop dan hotel yang memindai kode batang dan memberi informasi yang diinginkan kepada pengguna. Pada artikel ini kami akan mencoba membuat aplikasi pemindai tiket film sederhana yang deteksi barcode / qrcode dan menampilkan informasi film untuk memesan tiket.
The google vision library adalah bagian dari layanan bermain dan dapat ditambahkan ke build.gradle.
compile 'com.google.android.gms:play-services-vision:11.0.2'
- Barcode Scanner Library
Google menyediakan tutorial sederhana untuk mencoba perpustakaan deteksi barcode dengan gambar bitmap sederhana. Tapi ketika harus memindai kamera realtime untuk barcode, hal ini menjadi sulit untuk diterapkan karena kita perlu melakukan deteksi barcode pada video kamera. - How to Use the Barcode library
Ikuti langkah-langkah sederhana berikut untuk menyertakan library barcode / qrcode di proyek Anda.
1. Tambahkan pembaca barcode androidhive dan perpustakaan penglihatan google ke file build.gradle aplikasi Anda.Artikel ini ditulis menggunakan Android Studio 3.0 Canary 9. Jika sudah tidak berlaku lagi, bisa diganti dengan keadaan aktualnya
dependencies { // barcode reader library implementation 'info.androidhive:barcode-reader:1.1.5' // google vision library implementation 'com.google.android.gms:play-services-vision:11.0.2' }
2. Tambahkan fragmen kamera barcode ke aktivitas atau fragmen Anda.
<fragment android:id="@+id/barcode_scanner" android:name="info.androidhive.barcode.BarcodeReader" android:layout_width="match_parent" android:layout_height="match_parent" app:auto_focus="true" app:use_flash="false" />
3. Terapkan Aktivitas Anda dari BarcodeReader.BarcodeReaderListener dan timpa metode yang diperlukan.
import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.SparseArray; import com.google.android.gms.vision.barcode.Barcode; import java.util.List; import info.androidhive.barcode.BarcodeReader; public class MainActivity extends AppCompatActivity implements BarcodeReader.BarcodeReaderListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scan); } @Override public void onScanned(Barcode barcode) { // single barcode scanned } @Override public void onScannedMultiple(List<Barcode> list) { // multiple barcodes scanned } @Override public void onBitmapScanned(SparseArray<Barcode> sparseArray) { // barcode scanned from bitmap image } @Override public void onScanError(String s) { // scan error } @Override public void onCameraPermissionDenied() { // camera permission denied } }
4. Jalankan proyek Anda dan cobalah memindai kode batang atau qrcode. Hasil pindaian akan dikembalikan pada metode onScanned () atau onScannedMultiple ().
3.1. Adding Scanning Overlay Indicator LineKita dapat melihat semua aplikasi pemindai umumnya menambahkan garis indikator pada hamparan kamera untuk menunjukkan kemajuan pemindaian yang sedang berlangsung. Untuk mencapai hal ini, saya telah menambahkan kelas yang dapat digunakan kembali di perpustakaan yang sama yang dapat ditambahkan ke layar kamera.
Untuk menambahkan baris pemindaian animasi, tambahkan kode info.androidhive.barcode.ScannerOverlay ke aktivitas yang sama tumpang tindih dengan fragmen kamera.
<info.androidhive.barcode.ScannerOverlay android:layout_width="match_parent" android:layout_height="match_parent" android:background="#44000000" app:line_color="#7323DC" app:line_speed="6" app:line_width="4" app:square_height="200" app:square_width="200"/>
Perpustakaan juga berisi beberapa fungsi berguna lainnya seperti lampu kilat otomatis, bunyi bip dll, Informasi terperinci tentang perpustakaan dapat ditemukan di halaman Github-nya.
- Membuat Proyek Baru - Membangun Aplikasi Pemindaian Tiket Sederhana
Karena perpustakaan pemindaian barcode sudah tersedia, kita akan melihat bagaimana menggunakan perpustakaan dengan mempertimbangkan skenario penggunaan nyata. Aplikasi yang akan kami bangun tidak hanya menjelaskan pemindaian kode batang, namun juga mencakup pengembangan UI kompleks, membuat REST api memanggil untuk mengambil film json dan menulis kelas tampilan khusus.
Secara keseluruhan aplikasi berisi tiga layar. Yang pertama adalah splash screen, kedua adalah barcode scanning dan yang terakhir adalah untuk menampilkan informasi tiket film.
Berikut adalah tangkapan layar aplikasi.
4.1 REST API
Untuk membangun aplikasi ini, kita memerlukan REST API untuk mencari melalui database film dengan kode batang film. Saya telah menulis sebuah restorasi sederhana untuk mencari film dengan kode batang. Ini mengambil nilai kode bar standar sebagai parameter kueri dan mencarinya di db. Berikut adalah beberapa contoh kode batang untuk pengujian.
1. dn_barcode.jpg
2. spiderman_barcode.jpg
3. wonderwoman_barcode.jpg
4. dunkirk_barcode.jpg
Search Movie:
Minta GET permintaan dengan nilai kode batangnya dibaca.
https://api.androidhive.info/barcodes/search.php?code=dunkirk
Movie Result:
Film json yang cocok akan diberikan dalam responnya.
{ "name": "Dunkirk", "poster": "https://api.androidhive.info/barcodes/dunkirk.jpg", "duration": "1hr 46min", "rating": 4.6, "released": true, "genre": "Action", "price": "₹200", "director": "Christopher Nolan" }
Sekarang kita memiliki semua informasi yang diperlukan bersama kita. Mari kita mulai dengan membuat proyek baru di Android Studio.Catatan: Proyek ini dikembangkan menggunakan Android Studio 3.0 Canary 9
1. Buat proyek baru di Android Studio dari File ⇒ New Project dan isi rincian proyek. Saya memberi nama proyek sebagai MovieTickets dan nama paketnya sebagai info.androidhive.movietickets
2. Buka app build.gradle dan tambahkan barcode dan google vision dependencies. Saya juga menambahkan Glide, Volley dan Gson perpustakaan karena mereka diminta untuk membuat panggilan http, json parsing dan tampilan gambar.
dependencies { implementation 'com.google.android.gms:play-services-vision:11.0.2' // vision barcode scanner implementation 'info.androidhive:barcode-reader:1.1.2' // glide image library implementation 'com.github.bumptech.glide:glide:4.0.0-RC1' annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0-RC1' implementation 'com.android.volley:volley:1.0.0' implementation 'com.google.code.gson:gson:2.6.2' }
3. Tambahkan string, dimen, color resources ke masing-masing file di bawah direktori res.
string.xml
<resources> <string name="app_name">Movie Tickets</string> <string name="title_activity_ticket">Book Ticket</string> <string name="lbl_duration">DURATION</string> <string name="lbl_genre">GENRE</string> <string name="lbl_rating">RATING</string> <string name="lbl_price">PRICE</string> <string name="btn_buy_now">BUY NOW</string> <string name="btn_coming_soon">COMING SOON</string> <string name="msg_no_ticket_found">No ticket found. Try scanning the QR Codes from http://api.androidhive.info/qrcodes/</string> </resources>
dimen.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="dimen_20">20dp</dimen> <dimen name="dimen_10">10dp</dimen> <dimen name="activity_margin">16dp</dimen> <dimen name="lbl_directory">14dp</dimen> <dimen name="lbl_movie_name">28dp</dimen> <dimen name="img_poster_height">220dp</dimen> <dimen name="dimen_40">40dp</dimen> </resources>
color.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#6d0094</color> <color name="colorPrimaryDark">#6d0094</color> <color name="colorAccent">#ff2068</color> <color name="colorAccentSecondary">#ad1a7f</color> <color name="viewBg">#f8f8f8</color> <color name="btn_disabled">#999</color> <color name="lbl_value">#222222</color> </resources>
4. Buat kelas bernama MyApplication.java dan tambahkan kode di bawah ini. Disini kita buat contoh volley singleton.Volley tidak disarankan untuk membuat panggilan http, tapi untuk membuat integrasi sederhana, ini dipertimbangkan dalam tutorial ini. Pertimbangkan untuk menggunakan Retrofit di aplikasi produksi Anda.
MyAplication.java
import android.app.Application; import android.text.TextUtils; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; /** * Created by ravi on 31/07/17. */ public class MyApplication extends Application { public static final String TAG = MyApplication.class .getSimpleName(); private RequestQueue mRequestQueue; private static MyApplication mInstance; @Override public void onCreate() { super.onCreate(); mInstance = this; } public static synchronized MyApplication getInstance() { return mInstance; } public RequestQueue getRequestQueue() { if (mRequestQueue == null) { mRequestQueue = Volley.newRequestQueue(getApplicationContext()); } return mRequestQueue; } public <T> void addToRequestQueue(Request<T> req, String tag) { // set the default tag if tag is empty req.setTag(TextUtils.isEmpty(tag) ? TAG : tag); getRequestQueue().add(req); } public <T> void addToRequestQueue(Request<T> req) { req.setTag(TAG); getRequestQueue().add(req); } public void cancelPendingRequests(Object tag) { if (mRequestQueue != null) { mRequestQueue.cancelAll(tag); } } }
5. Buka AndroidManifest.xml dan tambahkan kelas MyApplication ke tag <applicaton>. Tambahkan juga izin INTERNET karena kami perlu melakukan panggilan http.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="info.androidhive.movietickets"> <uses-permission android:name="android.permission.INTERNET" /> <application android:name=".MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:screenOrientation="portrait" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ScanActivity" android:screenOrientation="portrait" /> <activity android:name=".TicketActivity" android:label="@string/title_activity_ticket" android:screenOrientation="portrait" android:theme="@style/AppTheme.NoActionBar"></activity> </application> </manifest>
6. Pada folder res ⇒ drawable buat xml drawable yang diberi nama bg_gradient.xml Yang menarik ini memberikan latar belakang gradien untuk dilihat.
bg_gradient.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <gradient android:angle="135" android:centerColor="@color/colorAccentSecondary" android:endColor="@color/colorPrimary" android:startColor="@color/colorAccent" android:type="linear" /> <corners android:radius="0dp" /> </shape>
4.2 Menambahkan Landing Screen
Layar pendaratan akan memiliki beberapa bidang teks dan tombol untuk membuka kamera pemindai. Hal menarik yang akan Anda pelajari di sini adalah memberikan latar belakang aktivitas yang gradien.
7. Buka file layout aktivitas utama anda (activity_main.xml) tambahkan layout di bawah ini. Disini kita menambahkan tombol untuk memulai aktivitas pemotretan.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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:background="@drawable/bg_gradient" tools:context="info.androidhive.movietickets.MainActivity"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerInParent="true" android:gravity="center" android:orientation="vertical" android:paddingLeft="40dp" android:paddingRight="40dp"> <ImageView android:id="@+id/icon" android:layout_width="100dp" android:layout_height="100dp" android:layout_centerHorizontal="true" android:clickable="true" android:foreground="?attr/selectableItemBackground" android:src="@drawable/qrcode" android:tint="@android:color/white" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:fontFamily="sans-serif-light" android:gravity="center" android:text="Scan the QR code on the poster and book your movie tickets" android:textColor="@android:color/white" android:textSize="16dp" /> </LinearLayout> <Button android:id="@+id/btn_scan" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="40dp" android:background="@android:color/transparent" android:foreground="?attr/selectableItemBackground" android:paddingLeft="20dp" android:paddingRight="20dp" android:fontFamily="sans-serif-medium" android:text="Scan QR Code" android:textColor="@android:color/white" android:textSize="18sp" /> </RelativeLayout>
8. Buka aktivitas utama dan lakukan perubahan yang diperlukan di bawah ini. Dalam hal ini metode transparentToolbar () membuat toolbar transparan. Pendengar klik tombol akan meluncurkan aktivitas pemindai yang akan segera kita buat.
MainActivity.java
import android.app.Activity; import android.content.Intent; import android.graphics.Color; import android.os.Build; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.SparseArray; import android.view.View; import android.view.Window; import android.view.WindowManager; import com.google.android.gms.vision.barcode.Barcode; import java.util.List; import info.androidhive.barcode.BarcodeReader; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // making toolbar transparent transparentToolbar(); setContentView(R.layout.activity_main); findViewById(R.id.btn_scan).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(MainActivity.this, ScanActivity.class)); } }); } private void transparentToolbar() { if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) { setWindowFlag(this, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, true); } if (Build.VERSION.SDK_INT >= 19) { getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } if (Build.VERSION.SDK_INT >= 21) { setWindowFlag(this, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, false); getWindow().setStatusBarColor(Color.TRANSPARENT); } } private void setWindowFlag(Activity activity, final int bits, boolean on) { Window win = activity.getWindow(); WindowManager.LayoutParams winParams = win.getAttributes(); if (on) { winParams.flags |= bits; } else { winParams.flags &= ~bits; } win.setAttributes(winParams); } }
Jika Anda menjalankan aplikasi, Anda dapat melihat layar pendaratan seperti di bawah ini.
4.3 Menambahkan Layar Scan Tiket
Sekarang kita sudah siapkan layar pendaratan. Ayo buat aktivitas pemindai.
9. Buat aktivitas baru dari File ⇒ New ⇒ Activity ⇒ Empty Activity dan beri nama ScanActivity.java
10. Buka file layout aktivitas ticket (activity_scan.xml) dan tambahkan fragmen Barcode Reader seperti di bawah ini. Di sini kita juga menambahkan tampilan animasi indikator garis pemindai.
activity_scan.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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="info.androidhive.movietickets.ScanActivity"> <fragment android:id="@+id/barcode_scanner" android:name="info.androidhive.barcode.BarcodeReader" android:layout_width="match_parent" android:layout_height="match_parent" app:auto_focus="true" app:use_flash="false" /> <info.androidhive.barcode.ScannerOverlay android:layout_width="match_parent" android:layout_height="match_parent" android:background="#44000000" app:line_color="#7323DC" app:line_speed="6" app:line_width="4" app:square_height="200" app:square_width="200" /> </RelativeLayout>
11. Buka ScanActivity.java dan lakukan perubahan yang diperlukan di bawah ini.
> Terapkan aktivitas dari BarcodeReader.BarcodeReaderListener
> Ganti metode callback onScanned (), onScannedMultiple () dan callback lainnya.
> barcodeReader.playBeep () memainkan suara bip pada kode batang dibaca.
> Luncurkan TicketActivity setelah kode batang dipindai dengan melewatkan nilai kode bar dengan maksud.
ScanActivity.java
package info.androidhive.movietickets; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.SparseArray; import android.widget.Toast; import com.google.android.gms.vision.barcode.Barcode; import java.util.List; import info.androidhive.barcode.BarcodeReader; public class ScanActivity extends AppCompatActivity implements BarcodeReader.BarcodeReaderListener { BarcodeReader barcodeReader; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scan); // get the barcode reader instance barcodeReader = (BarcodeReader) getSupportFragmentManager().findFragmentById(R.id.barcode_scanner); } @Override public void onScanned(Barcode barcode) { // playing barcode reader beep sound barcodeReader.playBeep(); // ticket details activity by passing barcode Intent intent = new Intent(ScanActivity.this, TicketActivity.class); intent.putExtra("code", barcode.displayValue); startActivity(intent); } @Override public void onScannedMultiple(List<Barcode> list) { } @Override public void onBitmapScanned(SparseArray<Barcode> sparseArray) { } @Override public void onCameraPermissionDenied() { finish(); } @Override public void onScanError(String s) { Toast.makeText(getApplicationContext(), "Error occurred while scanning " + s, Toast.LENGTH_SHORT).show(); } }
Jalankan aplikasi dan coba pindai kode batang apa saja. Kode batang yang dipindai akan dikembalikan pada metode onScanned ().
4.4 Menambahkan Layar Hasil Pemindaian TiketLayar selanjutnya akan menjadi tiket hasil aktivitas. Di layar ini rincian film akan ditampilkan dengan mengirim pindaian pindaian untuk mencari titik akhir dan mendapatkan hasilnya.
Jika Anda mengamati disainnya, saya telah menempatkan detail film pada tampilan tiket yang akan menancapkan lubang di tikungan. Untuk mencapai hal ini saya telah membuat custom view dan membuat lubang menggunakan penghapus.
4.4.1 Preparing Ticket View with Punching Holes
12. Buat kelas bernama TicketView.java. Ini adalah kelas pandangan biasa di mana Kanvas digunakan untuk membuat tampilan dengan lubang transparan di sudutnya.
TicketView.java
import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import android.widget.LinearLayout; /** * Created by ravi tamada on 29/07/17. * Ticket view creates view with view punches making circular holes * in the view */ public class TicketView extends LinearLayout { private Bitmap bm; private Canvas cv; private Paint eraser; private int holesBottomMargin = 70; private int holeRadius = 40; public TicketView(Context context) { super(context); Init(); } public TicketView(Context context, AttributeSet attrs) { super(context, attrs); Init(); } public TicketView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); Init(); } private void Init() { eraser = new Paint(); eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); eraser.setAntiAlias(true); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (w != oldw || h != oldh) { bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); cv = new Canvas(bm); } super.onSizeChanged(w, h, oldw, oldh); } @Override protected void onDraw(Canvas canvas) { int w = getWidth(); int h = getHeight(); bm.eraseColor(Color.TRANSPARENT); // set the view background color cv.drawColor(Color.WHITE); // drawing footer square contains the buy now button Paint paint = new Paint(); paint.setARGB(255, 250, 250, 250); paint.setStrokeWidth(0); paint.setStyle(Paint.Style.FILL); cv.drawRect(0, h, w, h - pxFromDp(getContext(), holesBottomMargin), paint); // adding punching holes on the ticket by erasing them cv.drawCircle(0, 0, holeRadius, eraser); // top-left hole cv.drawCircle(w / 2, 0, holeRadius, eraser); // top-middle hole cv.drawCircle(w, 0, holeRadius, eraser); // top-right cv.drawCircle(0, h - pxFromDp(getContext(), holesBottomMargin), holeRadius, eraser); // bottom-left hole cv.drawCircle(w, h - pxFromDp(getContext(), holesBottomMargin), holeRadius, eraser); // bottom right hole // drawing the image canvas.drawBitmap(bm, 0, 0, null); // drawing dashed lines at the bottom Path mPath = new Path(); mPath.moveTo(holeRadius, h - pxFromDp(getContext(), holesBottomMargin)); mPath.quadTo(w - holeRadius, h - pxFromDp(getContext(), holesBottomMargin), w - holeRadius, h - pxFromDp(getContext(), holesBottomMargin)); // dashed line Paint dashed = new Paint(); dashed.setARGB(255, 200, 200, 200); dashed.setStyle(Paint.Style.STROKE); dashed.setStrokeWidth(2); dashed.setPathEffect(new DashPathEffect(new float[]{10, 5}, 0)); canvas.drawPath(mPath, dashed); super.onDraw(canvas); } public static float pxFromDp(final Context context, final float dp) { return dp * context.getResources().getDisplayMetrics().density; } }
13. Buat kelas baru bernama TicketResultActivity.java dengan menavigasi ke File ⇒ New ⇒ Activity ⇒ Empty Activity.
14. Buka tata letak aktivitas hasil tiket (activity_ticket_result.xml) dan tambahkan tata letak di bawah ini.
activity_ticket_result.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/bg_gradient" android:descendantFocusability="beforeDescendants" android:fitsSystemWindows="true" tools:context=".TicketResultActivity"> <android.support.design.widget.AppBarLayout foreground="?android:windowContentOverlay" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?actionBarSize" android:background="@color/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay"></android.support.v7.widget.Toolbar> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_ticket_details"/> </android.support.design.widget.CoordinatorLayout>
content_ticket_detail.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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:background="@drawable/bg_gradient" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".TicketResultActivity"> <TextView android:id="@+id/txt_error" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerInParent="true" android:gravity="center_horizontal" android:text="@string/msg_no_ticket_found" android:textColor="@android:color/white" android:padding="@dimen/dimen_20" android:textSize="16dp" android:visibility="gone" /> <info.androidhive.movietickets.TicketView android:id="@+id/layout_ticket" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="@dimen/dimen_20" android:layout_marginRight="@dimen/dimen_20" android:layout_marginTop="@dimen/dimen_20" android:background="@android:color/transparent" android:orientation="vertical" android:paddingTop="@dimen/dimen_10" android:visibility="gone"> <TextView android:id="@+id/director" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:fontFamily="sans-serif-condensed" android:paddingLeft="@dimen/activity_margin" android:paddingRight="@dimen/activity_margin" android:paddingTop="@dimen/activity_margin" android:textAllCaps="true" android:textSize="@dimen/lbl_directory" /> <TextView android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-condensed" android:paddingLeft="16dp" android:paddingRight="16dp" android:textAllCaps="true" android:textColor="#111" android:maxLines="1" android:ellipsize="end" android:textSize="@dimen/lbl_movie_name" /> <ImageView android:id="@+id/poster" android:layout_width="match_parent" android:layout_height="@dimen/img_poster_height" android:layout_marginBottom="@dimen/activity_margin" android:layout_marginTop="@dimen/activity_margin" android:scaleType="centerCrop" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:paddingLeft="@dimen/activity_margin" android:paddingRight="@dimen/activity_margin" android:weightSum="2"> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:fontFamily="sans-serif-condensed" android:text="@string/lbl_duration" android:textSize="12dp" /> <TextView android:id="@+id/duration" android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-condensed" android:textColor="@color/lbl_value" android:textSize="22dp" /> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:fontFamily="sans-serif-condensed" android:text="@string/lbl_genre" android:textSize="12dp" /> <TextView android:id="@+id/genre" android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-condensed" android:textAllCaps="true" android:textColor="@color/lbl_value" android:textSize="22dp" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:orientation="horizontal" android:paddingLeft="16dp" android:paddingRight="16dp" android:weightSum="2"> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:fontFamily="sans-serif-condensed" android:text="@string/lbl_rating" android:textSize="12dp" /> <TextView android:id="@+id/rating" android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-condensed" android:textColor="@color/lbl_value" android:textSize="22dp" /> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:fontFamily="sans-serif-condensed" android:text="@string/lbl_price" android:textSize="12dp" /> <TextView android:id="@+id/price" android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-condensed" android:textAllCaps="true" android:textColor="@color/lbl_value" android:textSize="22dp" /> </LinearLayout> </LinearLayout> </info.androidhive.movietickets.TicketView> <Button android:id="@+id/btn_buy" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="10dp" android:background="@android:color/transparent" android:fontFamily="sans-serif-condensed" android:foreground="?attr/selectableItemBackground" android:paddingLeft="@dimen/activity_margin" android:paddingRight="@dimen/activity_margin" android:textColor="@color/colorPrimary" android:textSize="26dp" /> <ProgressBar android:id="@+id/progressBar" android:layout_width="@dimen/dimen_40" android:layout_height="@dimen/dimen_40" android:layout_centerInParent="true" android:indeterminateTint="@android:color/white" android:indeterminateTintMode="src_atop" android:visibility="visible" /> </RelativeLayout>
15. Buka masing-masing TicketResultActivity.java dan ubah kodenya seperti di bawah ini.
> searchBarcode () membuat volley http call untuk mencari endpoint dengan melewatkan kode barcode.
> renderMovie () mem-parsing json dan membuat rincian film di layar.
TicketResultActivity.java
import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.JsonObjectRequest; import com.bumptech.glide.Glide; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.annotations.SerializedName; import org.json.JSONObject; public class TicketResultActivity extends AppCompatActivity { private static final String TAG = TicketResultActivity.class.getSimpleName(); // url to search barcode private static final String URL = "https://api.androidhive.info/barcodes/search.php?code="; private TextView txtName, txtDuration, txtDirector, txtGenre, txtRating, txtPrice, txtError; private ImageView imgPoster; private Button btnBuy; private ProgressBar progressBar; private TicketView ticketView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_ticket_result); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); txtName = findViewById(R.id.name); txtDirector = findViewById(R.id.director); txtDuration = findViewById(R.id.duration); txtPrice = findViewById(R.id.price); txtRating = findViewById(R.id.rating); imgPoster = findViewById(R.id.poster); txtGenre = findViewById(R.id.genre); btnBuy = findViewById(R.id.btn_buy); imgPoster = findViewById(R.id.poster); txtError = findViewById(R.id.txt_error); ticketView = findViewById(R.id.layout_ticket); progressBar = findViewById(R.id.progressBar); String barcode = getIntent().getStringExtra("code"); // close the activity in case of empty barcode if (TextUtils.isEmpty(barcode)) { Toast.makeText(getApplicationContext(), "Barcode is empty!", Toast.LENGTH_LONG).show(); finish(); } // search the barcode searchBarcode(barcode); } /** * Searches the barcode by making http call * Request was made using Volley network library but the library is * not suggested in production, consider using Retrofit */ private void searchBarcode(String barcode) { // making volley's json request JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET, URL + barcode, null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.e(TAG, "Ticket response: " + response.toString()); // check for success status if (!response.has("error")) { // received movie response renderMovie(response); } else { // no movie found showNoTicket(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e(TAG, "Error: " + error.getMessage()); showNoTicket(); } }); MyApplication.getInstance().addToRequestQueue(jsonObjReq); } private void showNoTicket() { txtError.setVisibility(View.VISIBLE); ticketView.setVisibility(View.GONE); progressBar.setVisibility(View.GONE); } /** * Rendering movie details on the ticket */ private void renderMovie(JSONObject response) { try { // converting json to movie object Movie movie = new Gson().fromJson(response.toString(), Movie.class); if (movie != null) { txtName.setText(movie.getName()); txtDirector.setText(movie.getDirector()); txtDuration.setText(movie.getDuration()); txtGenre.setText(movie.getGenre()); txtRating.setText("" + movie.getRating()); txtPrice.setText(movie.getPrice()); Glide.with(this).load(movie.getPoster()).into(imgPoster); if (movie.isReleased()) { btnBuy.setText(getString(R.string.btn_buy_now)); btnBuy.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary)); } else { btnBuy.setText(getString(R.string.btn_coming_soon)); btnBuy.setTextColor(ContextCompat.getColor(this, R.color.btn_disabled)); } ticketView.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); } else { // movie not found showNoTicket(); } } catch (JsonSyntaxException e) { Log.e(TAG, "JSON Exception: " + e.getMessage()); showNoTicket(); Toast.makeText(getApplicationContext(), "Error occurred. Check your LogCat for full report", Toast.LENGTH_SHORT).show(); } catch (Exception e) { // exception showNoTicket(); Toast.makeText(getApplicationContext(), "Error occurred. Check your LogCat for full report", Toast.LENGTH_SHORT).show(); } } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { finish(); } return super.onOptionsItemSelected(item); } private class Movie { String name; String director; String poster; String duration; String genre; String price; float rating; @SerializedName("released") boolean isReleased; public String getName() { return name; } public String getDirector() { return director; } public String getPoster() { return poster; } public String getDuration() { return duration; } public String getGenre() { return genre; } public String getPrice() { return price; } public float getRating() { return rating; } public boolean isReleased() { return isReleased; } } }
Sekarang jika Anda menjalankan aplikasi dan memindai kode qr yang ada dalam artikel ini, hasilnya akan ditampilkan seperti di bawah ini.
- Testing the AppAplikasi dapat memindai barcode apa pun namun informasi tiket film
akan ditampilkan hanya bila kode qr dipindai yang disediakan dalam
artikel ini.
Jalankan aplikasi dan coba pindai kode qr di bawah ini. Anda harus bisa melihat informasi film yang tepat seperti yang ditunjukkan dalam demo.
1. dn_barcode.jpg
2. spiderman_barcode.jpg
3. wonderwoman_barcode.jpg
4. dunkirk_barcode.jpg
No comments