کانال بله, جهت پشتیبانی و اطلاع رسانی کانال بله, جهت پشتیبانی و اطلاع رسانی
عضویت

آموزش استفاده از بارکد و 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 را متوجه شوید، مراحل زیر را دنبال کنید.

  1. 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'
    }
    
  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" / >
    
  3. 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 } }
  4. پروژه خود را اجرا کرده و سعی کنید بارکد یا یک 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

صفحه اسکن بارکد و صفحه‌ای است که اطلاعات مربوط به بلیت سینما را نمایش می‌دهد.
در زیر می‌توانید اسکرین شات های مربوط به نرم افزار را مشاهده کنید.


دوره آموزش Android FireBase Socket

آموزش REST API در Android

برای ساخت این نرم افزار ، به یک REST api نیاز خواهیم داشت تا بتوانیم توسط بارکد فیلم، در دیتابیس فیلم‌ها به جستجو بپردازیم. برای جستجوی فیلم‌ها بر اساس بارکد، من برای نمونه یک rest api را نوشته‌ام. این api مقدار پیش تعریف شده بارکد را به عنوان پارامتر جستجو ( query param) می‌گیرد و در db به دنبال آن می‌گردد. در زیر می‌توانید برای آزمایش نمونه‌هایی از بارکدها را مشاهده کنید.

  1. dn_barcode.jpg
  2. spiderman_barcode.jpg
  3. wonderwoman_barcode.jpg
  4. 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. 1. برای ایجاد یک پروژه جدید در اندروید استودیو به file رفته و بر روی new project کلیک کنید و بعد از آن جزئیات مربوط به پروژه را پر کنید. من اسم پروژه خودم را MovieTickets و اسم پکیج را info.androidhive.movietickets گذاشته‌ام.
  2. 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. 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 >
    
  4. کلاسی با نام 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);
            }
        }
    }
    
  5. 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. 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. 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 >
    
  2. 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 FireBase Socket

آموزش اضافه کردن صفحه جهت اسکن بلیت در Android

حالا که صفحه‌ی landing مان آماده شده است. اجازه دهید scanner activity را ایجاد کنیم.

  1. 1. برای ایجاد یک activity جدید به File ⇒ New ⇒ Activity ⇒ Empty Activity بروید و اسم آن را ScanActivity.java بگذارید.
  2. 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 >
    
  3. 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 FireBase Socket

آموزش اضافه کردن صفحه‌ای برای نمایش نتیجه بلیت اسکن شده در 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;
    }
}
  1. به File ⇒ New ⇒ Activity ⇒ Empty Activity بروید و کلاسی به نام TicketResultActivity.java ایجاد کنید.
  2. فایل 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 >
    
  3. 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 FireBase Socket

برای مطالعه سرفصل آموزش جامع و عملی برنامه نویسی Android کلیک نمایید .

1397/05/30 6136 1268
رمز عبور : tahlildadeh.com یا www.tahlildadeh.com
نظرات شما

نظرات خود را ثبت کنید...