مشخصات مقاله
-
1268
-
0.0
-
6136
-
0
-
0
آموزش استفاده از بارکد و QR code scanner دراندروید با کمک Google Mobile Vision در Android
آموزش استفاده از بارکد و QR code scanner دراندروید با کمک Google Mobile Vision در Android
امروزه بارکدها و QR code ها کاربرد گستردهای در بسیاری از نرم افزارهای موبایل دارند. شما میتوانید در یک QR code اطلاعاتی مانند : متن، sms، ایمیل، لینک url ، عکس، صدا و فرمتهای دیگر را ذخیره کنید. میتوان در اندروید با استفاده از کتابخانه Google Mobile Vision اطلاعات ذخیره شده در بارکدها را استخراج کرد. با وجود اینکه کتابخانههای زیاد دیگری برای انجام این کار وجود دارند، اما این کتابخانه از جمله بهترین گزینهها است ، به این دلیل که نه تنها میتوان با کمک آن به خواندن بارکدها پرداخت بلکه از ویژگیهای دیگری مانند شناسایی چهره نیز بهره برد.
در این نوشته قصد داریم با ایجاد کردن یک نرم افزار ساده اسکن بلیت سینما، چگونگی استفاده از کتابخانه google vision را بیاموزیم .
آموزش Google Mobile Vision API در Android
این API به شما در پیدا کردن اشیا در تصاویر یا ویدئوها کمک میکند. Google Mobile Vision API از قابلیتهایی مانند : شناسایی چهره، شناسایی متن و شناسایی بارکد بهره میبرد. تمام این قابلیتها را میتوان به صورت جداگانه یا در کنار هم استفاده کرد.
هدف این نوشتار توضیح شناسایی بارکد با استفاده از یک مثال موردی real time میباشد. تا به حال در مکانهایی مانند سوپرمارکتها، تئاتر و هتل نرم افزارهای اسکن بارکد زیادی دیدهایم که میتوانند اطلاعات مورد نظر کاربر را ارائه کنند. در این مقاله قصد داریم نرم افزار اسکن بلیت سینمایی را بسازیم به گونهای که این نرم افزار یک بارکد یا QR Code را اسکن کند و در نهایت برای خریداری بلیت سینما، اطلاعات مربوط به آن بلیت را نمایش دهد.
کتابخانه google vision بخشی از خدمات مربوط به google play service است و میتوانید آن را در build.gradle پروژه خودتان اضافه کنید.
compile 'com.google.android.gms:play-services-vision:11.0.2'
آموزش Barcode Scanner Library در Android
گوگل ، آموزش سادهای را با کمک یک تصویر ساده bitmap برای آشنا شدن با barcode scanning library فراهم کرده است. اما زمانی که بخواهیم به صورت realtime بازخورد دوربینی را اسکن کنیم ،کار یک مقدار سخت میشود، زیرا در این صورت به شناسایی بارکد توسط دوربین نیاز خواهیم داشت.
من با منشعب کردن (forking) نمونه کار google vision ، کتابخانه Barcode Scanner سادهای را توسعه دادهام. برخی از باگهای نرم افزاری این کتابخانه برطرف شده و برای مواقعی که بارکدی اسکن میشود، توابع callback ای در نظر گرفته شدهاند. همچنین از جمله دیگر قابلیتهای این کتابخانه میتوان به وجود یک overlay scanning line indicator اشاره کرد. از این اندیکاتور میتوانید در نرم افزارهای خودتان استفاده کنید.
برای اینکه روش استفاده از کتابخانهی مربوط به بارکد و QR Code را متوجه شوید، مراحل زیر را دنبال کنید.
-
androidhive barcode reader و google vision library زیر را به نرم افزار خودتان اضافه کنید.
این مقاله با استفاده از Android Studio 3.0 Canary 9 نوشته شده و implementation کنار گذاشته شده جای خود را به compile داده است.
build.gradle 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' } -
جزء مربوط به barcode camera را به جزء یا activity خود اضافه کنید.
< 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" / > -
activity خودتان را از BarcodeReader.BarcodeReaderListener پیادهسازی کرده و متدهای لازم را override کنید.
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 } } - پروژه خود را اجرا کرده و سعی کنید بارکد یا یک QR Code را اسکن کنید. نتیجه اسکن شده به شکل متدهای onscanned() یا onScannedMultiple() برگشت خواهد خورد.
آموزش اضافه کردن قابلیت اسکن کردن با استفاده از خط شاخص در overlay دوربین ( Overlay Indicator Line) در Android
عموماً تمام نرم افزارهای اسکن بارکد برای اینکه نشان دهند که فرایند اسکن کردن تا چه حد پیشرفت داشته، از یک خط شاخص یا indicator line بهره میبرند. من برای اینکه بتوانم به این قابلیت دست پیدا کنم، کلاسی را در همین کتابخانه ایجاد کردهام به گونهای که این کلاس قابلیت استفاده مجدد را دارد و میتوان آن را به صفحه دوربین اضافه کرد.
برای اضافه کردن یک خط اسکن انیمیشنی، info.androidhive.barcode.ScannerOverlay را به همان فعالیتی اضافه کنید که فرگمنت یا جزء camera را همپوشانی کرده است.
< 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" / >
این کتابخانه قابلیتهای دیگری مانند فلش خودکار دوربین و صدای beep را نیز دارا است. برای اطلاعات دقیقتر در رابطه با این کتاب خانه به صفحه Github آن مراجعه کنید.
آموزش ایجاد پروژه جدید - ساخت نرم افزار اسکن بلیت در Android
با توجه به اینکه Barcode Scanner Library در حال حاضر موجود است، میخواهیم بدانیم که چگونه میتوان با در نظر گرفتن یک مثال واقعی، از این کتاب خانه استفاده کرد. این برنامهای که قرار است بسازیم، نه تنها اسکن بارکد را مشخص میکند، بلکه مباحثی مانند ساختن رابط کاربری پیچیده، فراخوانی REST api برای دریافت فرمت json فیلم و نوشتن کلاسهای شخصی view را پوشش میدهد.
در مجموع این نرم افزار شامل سه صفحه میشود:
صفحه splash
صفحه اسکن بارکد و صفحهای است که اطلاعات مربوط به بلیت سینما را نمایش میدهد.
در زیر میتوانید اسکرین شات های مربوط به نرم افزار را مشاهده کنید.
آموزش REST API در Android
برای ساخت این نرم افزار ، به یک REST api نیاز خواهیم داشت تا بتوانیم توسط بارکد فیلم، در دیتابیس فیلمها به جستجو بپردازیم. برای جستجوی فیلمها بر اساس بارکد، من برای نمونه یک rest api را نوشتهام. این api مقدار پیش تعریف شده بارکد را به عنوان پارامتر جستجو ( query param) میگیرد و در db به دنبال آن میگردد. در زیر میتوانید برای آزمایش نمونههایی از بارکدها را مشاهده کنید.
- dn_barcode.jpg
- spiderman_barcode.jpg
- wonderwoman_barcode.jpg
- dunkirk_barcode.jpg
جستجو کردن فیلم :
با استفاده از مقدار read بارکد، درخواست get را ایجاد کنید.
https://api.androidhive.info/barcodes/search.php?code=dunkirk
نتیجه فیلم :
در واکنش به درخواست بالا، json مطابق فیلم ارائه میشود.
{
"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"
}
حالا به تمام اطلاعاتی که نیاز داریم دسترسی داریم. بیاید کارمان را با ایجاد یک پروژه جدید در اندروید استودیو شروع کنیم.
این پروژه با استفاده از Android Studio 3.0 Canary 9 نوشته شده است.
- 1. برای ایجاد یک پروژه جدید در اندروید استودیو به file رفته و بر روی new project کلیک کنید و بعد از آن جزئیات مربوط به پروژه را پر کنید. من اسم پروژه خودم را MovieTickets و اسم پکیج را info.androidhive.movietickets گذاشتهام.
-
2. build.gradle نرم افزار را باز کنید و وابستگیهای مربوط به barcode و google vision را در آن اضافه کنید. برای اینکه بتوانیم http ها را فراخوانی، json را تجزیه (Parse) کنیم و تصاویر را نمایش دهیم؛ باید کتابخانههای Glide ، Volley و Gson را اضافه کنیم.
app/build.gradle
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. منابع color,dimen و string زیر را در دایرکتوری res به فایلهای مربوط به خودشان اضافه کنید.
strings.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/QR Codes/< /string > < /resources >
dimens.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 >
colors.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 > -
کلاسی با نام MyApplication.java را ایجاد کرده و کد زیر را در آن اضافه کنید. در اینجا ما یک volley تک شیء ( volley singleton instance) را ایجاد میکنیم.
volley برای فراخوانی http مناسب نیست، بلکه در این آموزش برای ایجاد نمونه از یکپارچه سازی لحاظ شده است. برای نرم افزارهای تولید شده خود Retrofit را لحاظ کنید.
MyApplication.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); } } } -
AndroidManifest.xml را باز کنید و کلاس MyApplication را به تگ < applicaton > اضافه کنید. permisson مربوط به اینترنت را هم اضافه کنید چون به فراخوانی 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. در پوشه res ⇒ drawable ، یک xml drawable به نام bg_gradient.xml را اضافه کنید، این drawable ، پس زمینه متغیری به view میدهد.
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 >
آموزش اضافه کردن landing screen در Android
این صفحه شامل تعدادی کادر متن همچنین یک دکمه برای باز کردن دوربین اسکنر خواهد بود. نکته جالبی که در اینجا خواهید آموخت، ایجاد بک گراند متغیر در activity است.
-
1. فایل layout مربوط به main activity خود (activity_main.xml) را باز کنید و Layout زیر را در آن اضافه کنید. در اینجا برای اجرا scanner activity ، دکمهای را لحاظ میکنیم.
< ?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/QR Code" 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 > -
main activity را باز کنید و تغییر ضروری زیر را در آن اعمال کنید. در این activity ، متد transparentToolbar() باعث شفاف شدن نوار ابزار میشود. button click listener وظیفه اجرای scanner activity را برعهده دارد. به زودی این activity را ایجاد خواهیم کرد.
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); } }
در صورتی که نرم افزار را اجرا کنید، صغحه ی لندینگ به صورت زیر برایتان نماش داده میشود.
آموزش اضافه کردن صفحه جهت اسکن بلیت در Android
حالا که صفحهی landing مان آماده شده است. اجازه دهید scanner activity را ایجاد کنیم.
- 1. برای ایجاد یک activity جدید به File ⇒ New ⇒ Activity ⇒ Empty Activity بروید و اسم آن را ScanActivity.java بگذارید.
-
2. فایل layout مربوط به ticket activity خود (activity_scan.xml) را باز کنید و barcode reader را در فرگمنت زیر اضافه کنید.
در این بخش قصد داریم view انیمیشنی مربوط به خط شاخص اسکنر را اضافه کنیم.
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 > -
ScanActivity.java را باز کنید و تغییر ضروری زیر را در آن اعمال کنید.
activity موجود در BarcodeReader.BarcodeReaderListener را پیاده سازی کنید.
متدهای کال بک onScanned(), onScannedMultiple() و دیگر callback ها را override کنید.
barcodeReader.playBeep() در زمانی که بارکد خوانده میشود، صدای beep تولید میکند.
با عبور دادن مقدار barcode به intent ، TicketActivity را اجرا کنید.
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(); } }
نرم افزار را اجرا کرده و سعی کنید یک بارکد را اسکن کنید. نتیجه اسکن شده در متد onscanned() یا برگشت خواهد خورد.
آموزش اضافه کردن صفحهای برای نمایش نتیجه بلیت اسکن شده در Android
صفحه بعد، صفحه مربوط به activity نتایج بلیت خواهد بود. در این صفحه نتایج فیلم با ارسال QR Code اسکن شده به نقطه نهایی جستجو (search endpoint) نمایش داده میشوند.
اگر به طراحی نرم افزار دقت کنید، من جزییات فیلم را در جایی از view بلیت قرار دادهام که قرار است در گوشههای آن سوراخهایی پانچ شود. من برای انجام این کار ، یک custom view ایجاد کردم و با استفاده از eraser سوراخها را ایجاد کردم.
آموزش آماده کردن view بلیت با استفاده از سوراخهای پانچ شده در Android
کلاسی با نام TicketView.java را ایجاد کنید. این کلاس، view سادهای است به گونهای که Canvas موجود در آن در حالی برای render کردن view استفاده میشود که سوراخهای موجود در گوشهها حالت شفافی داشته باشند.
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;
}
}
- به File ⇒ New ⇒ Activity ⇒ Empty Activity بروید و کلاسی به نام TicketResultActivity.java ایجاد کنید.
-
فایل layout مربوط به ticket result activity خود (activity_ticket_result.xml) را باز کنید و Layout زیر را در آن اضافه کنید.
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_details.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 > -
TicketResultActivity.java مربوطه را باز کنید و کد آن را مانند زیر اصلاح کنید.
> searchBarcode() با عبور دادن بارکد اسکن شده برای جستجوی endpoint ، باعث فراخوانی شدن volley http میشود.
>renderMovie() jsonرا تجزیه کرده و جزییات فیلم را در صفحه rendersمیکند.
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; } } }
حالا اگر شما برنامه را اجرا کنید و qr code های موجود در این مقاله را با آن اسکن کنید، نتیجه مانند زیر نمایش داده میشود.
برای مطالعه سرفصل آموزش جامع و عملی برنامه نویسی Android کلیک نمایید .