1. 程式人生 > >Android:實際運用Zxing整合二維碼掃描 及 自定義掃碼介面(demo原始碼)

Android:實際運用Zxing整合二維碼掃描 及 自定義掃碼介面(demo原始碼)

二維碼掃描,各大主流App必不可少的功能,而且google已將輪子替我們造好,直接拿來使用即可。以下是教學如何將Zxing開源庫整合到自己專案中,並且自定義掃碼介面,後期可根據自己的業務需求進行修改,最後補充了一點由此延伸的學習技能點。

一. 整合Zing開源庫到應用中

這裡寫圖片描述

這裡寫圖片描述

如上圖所示,我使用的整合方法是拷入jar包,然後拷貝相關類即可,具體的資源都已經在Demo中做好了,具體內容都可以直接參考Demo。(看到網上還有其它整合的方法,雖沒試過,各位也可以考慮)

  • app 掃描Activity
  • camera 攝像頭相關
  • decode 解析二維碼相關
  • encode 生成二維碼相關
  • util
    工具類
  • view 掃描預覽檢視ViewfinderView

二. 自定義掃碼介面

這裡寫圖片描述

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <RelativeLayout
        android:layout_width
="fill_parent" android:layout_height="fill_parent" >
<SurfaceView android:id="@+id/preview_view" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="center" /> <cn.edu.wsyu.hgh.zhiliao.component.zxing.view.ViewfinderView
android:id="@+id/viewfinder_view" android:layout_width="fill_parent" android:layout_height="fill_parent" />
</RelativeLayout> </FrameLayout>

如果整合Zxing成功後開啟掃描功能,你會發現掃描頁面如上圖所示,這是CaptureActivity的佈局,CaptureActivity主要來處理掃描介面的邏輯。佈局僅有一個SurfaceView,中間一個供掃描的矩形框ViewfinderView就目前而言,掃描的功能已經可以使用了,但是這種UI效果是市場上的app不符,所以需要自定義掃碼頁面,而理想實現效果如下圖所示:

這裡寫圖片描述

1. 需要實現的需求

對比以上兩張圖,總結出我們需要實現的需求

  • (1) 增加一個toolbar,內容填充為返回鍵和介面提示。
  • (2) 中間的掃描區域—–即Zxing為我們繪製的矩形的四個角上分別畫了8個綠色小矩形和一條不停上下移動的掃描線。
  • (3) 在最下方加了一個footer,增加了相簿、開燈兩個功能使得這個掃描功能更加完善。

經過以上分析可得,完成上述3個需求,就可以達到理想的UI效果了,接下來依次實現:

2. 實現頭部效果

這裡寫圖片描述

這個頭部實現實在很簡單,這裡不過多贅述,提供2種實現思路:

  • 1 使用Android提供控制元件Toolbar,在控制元件中再加一個TextView用來顯示“掃一掃”即可。
  • 2 直接加一個佈局View,佈局中一個ImageView、TextView完成

佈局完成後,在CaptureActivity中設定對應的點選事件,處理返回邏輯即可。

3. 實現掃描框效果

這裡寫圖片描述

這個掃描框是Zxing自帶的一個自定義View,名為ViewfinderView,而目前需要做的需求:

  • 1 掃碼框的四個角上,每個角有兩條邊,總共八條,繪製8條綠色矩形,完成邊框。
  • 2 在掃碼框中繪製一條不停上下移動的線。
  • 3 在掃描框下方新增一條提示語句

3.1 繪製掃描邊框

熟悉自定義View的人首先想到在ViewfinderView中的onDraw方法中完成繪製,首先來看它預設繪製的onDraw方法:

 @Override
    public void onDraw(Canvas canvas) {
       //獲得掃描框的矩形
        Rect frame = CameraManager.get().getFramingRect();
        if (frame == null) {
            return;
        }

        int width = canvas.getWidth();
        int height = canvas.getHeight();

        //繪製掃描框矩形
        // Draw the exterior (i.e. outside the framing rect) darkened
        paint.setColor(resultBitmap != null ? resultColor : maskColor);
        canvas.drawRect(0, 0, width, frame.top, paint);
        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,
                paint);
        canvas.drawRect(0, frame.bottom + 1, width, height, paint);

        Collection<ResultPoint> currentPossible = possibleResultPoints;
        Collection<ResultPoint> currentLast = lastPossibleResultPoints;
        if (currentPossible.isEmpty()) {
             lastPossibleResultPoints = null;
        } else {
        possibleResultPoints = new HashSet<ResultPoint>(5);
        lastPossibleResultPoints = currentPossible;
         paint.setAlpha(OPAQUE);
         paint.setColor(resultPointColor);
         for (ResultPoint point : currentPossible) {
              canvas.drawCircle(frame.left + point.getX(), frame.top
                     + point.getY(), 6.0f, paint);
                }
            }
          if (currentLast != null) {
               paint.setAlpha(OPAQUE / 2);
               paint.setColor(resultPointColor);
               for (ResultPoint point : currentLast) {
                    canvas.drawCircle(frame.left + point.getX(), frame.top
                            + point.getY(), 3.0f, paint);
                }
            }

          postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
                    frame.right, frame.bottom);
        }

初步瞭解後,需要使用canvas來繪製8個小矩形,程式碼不難如下

paint.setColor(Color.BLUE);
            canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,
                    frame.top + CORNER_WIDTH, paint);
            canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH,
                    frame.top + ScreenRate, paint);
            canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,
                    frame.top + CORNER_WIDTH, paint);
            canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right,
                    frame.top + ScreenRate, paint);
            canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left
                    + ScreenRate, frame.bottom, paint);
            canvas.drawRect(frame.left, frame.bottom - ScreenRate, frame.left
                    + CORNER_WIDTH, frame.bottom, paint);
            canvas.drawRect(frame.right - ScreenRate, frame.bottom
                    - CORNER_WIDTH, frame.right, frame.bottom, paint);
            canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom
                    - ScreenRate, frame.right, frame.bottom, paint);

這值得注意的是ScreenRate,即小矩形的長度,這裡並沒有把它固定化,而是將理想值乘以螢幕密度,來更好地適配不同大小螢幕:

            density = context.getResources().getDisplayMetrics().density;
            ScreenRate = (int) (15 * density);

3.2 繪製移動的線

  //定義好掃描線每秒移動端的畫素
            slideTop += SPEEN_DISTANCE;
            //判斷掃描線的長度是否大於掃描框底部,若大於則重新回到掃描框頭部再進行掃描
            if (slideTop >= frame.bottom) {
                slideTop = frame.top;
            }

            //動態定義出掃描線矩形
            Rect lineRect = new Rect();
            lineRect.left = frame.left;
            lineRect.right = frame.right;
            lineRect.top = slideTop;
            lineRect.bottom = slideTop + 18;
            //繪製上去
            canvas.drawBitmap(((BitmapDrawable) (getResources()
                            .getDrawable(R.drawable.fle))).getBitmap(), null, lineRect,
                    paint);

3.3 繪製文字

  paint.setColor(Color.WHITE);
            paint.setTextSize(TEXT_SIZE * density);
            paint.setAlpha(0x40);
            paint.setTypeface(Typeface.create("System", Typeface.BOLD));
            String text = getResources().getString(R.string.scan_text);
            float textWidth = paint.measureText(text);

            canvas.drawText(
                    text,
                    (width - textWidth) / 2,
                    (float) (frame.bottom + (float) TEXT_PADDING_TOP * density),
                    paint);

三. 掃碼功能完善

1. 掃碼介面功能完善

以上介面完成後,需要在CaptureActivity中稍加完善幾個按鈕(開閃光燈、相簿)的功能:
之所以選擇Zxing開源庫的一大原因是因為它將所有的方法都封裝完善,只需我們呼叫即可,例如開啟或關閉閃光燈。

     @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.flash_btn:// 閃光燈開關
                if (isTorchOn) {
                    isTorchOn = false;
                    flash_btn.setText("關燈");
                    CameraManager.start();
                } else {
                    isTorchOn = true;
                    flash_btn.setText("開燈");
                    CameraManager.stop();
                }
                break;
            case R.id.photo_btn: {// 選擇相簿
                selectPhoto();
            }
            break;
            case R.id.button_back: {// 返回
                finish();
            }
            break;
            default:
                break;
        }
    }

2 主頁跳轉到掃碼介面

MainActivity

    private static final int REQUEST_QRCODE = 0x01;

    Intent intent = new Intent(mContext, CaptureActivity.class);
    startActivityForResult(intent, REQUEST_QRCODE);

以上程式碼非常簡單,假設是在MainActivity中進行操作,點選對應按鈕進入到CaptureActivity中,唯一需要注意的是跳轉頁面使用的方法是startActivityForResult,因為我們需要獲取到掃描後的資料,然後做下一步的處理。

3二維碼生成

生成二維碼的方式:

   /**
     * 二維碼生成
     */
    private void qrCodeGenerated() {

        String str = "點個贊噻";

        DisplayMetrics metric = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metric);
        int width = metric.widthPixels;
        try {

            Bitmap qrCode = Utils.createQRCode(str, width / 2);
            qrcodeImg.setImageBitmap(qrCode);

        } catch (WriterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

str代表二維碼中的資料,主要依賴的是Util工具類的方法,其中還有二維碼中含Logo的方式,自己可修改,兩種方法如下:

    /**
     * 生成一個二維碼影象
     * 
     * @param url
     *            傳入的字串,通常是一個URL
     * @param widthAndHeight
     *            影象的寬高
     * @return
     */
    private static final int BLACK = 0xff000000;

    public static Bitmap createQRCode(String str, int widthAndHeight)
    /**
     * 生成一個有logo的二維碼影象
     * 
     * @param url
     *            傳入的字串,通常是一個URL
     * @param widthAndHeight
     *            影象的寬高
     * @return
     */

    public static Bitmap createQRCodeLogo(String str, int widthAndHeight,
                                          Bitmap bit)

4. 主頁獲取掃碼資料並處理

要想在MainActivity 中的 onActivityResult方法獲取到掃描的資料,必須要知道什麼時候將資料setResult進來的,獲取資料的Key值是什麼?

解析二維碼是個耗時的操作,必然在子執行緒中執行,所以我們直接檢視CaptureActivity對應的Handle——CaptureActivityHandle,通過類註釋可知,這個類負責處理所有掃碼相關的業務,既然是個Handle,那我們直接來看handleMessage方法:

 @Override
    public void handleMessage(Message message) {

        ......

       case R.id.decode_succeeded:// 解碼成功
            Log.d(TAG, "Got decode succeeded message");
            state = State.SUCCESS;
            Bundle bundle = message.getData();

            /****************************************************/
            Bitmap barcode = bundle == null ? null : (Bitmap) bundle
                    .getParcelable(DecodeThread.BARCODE_BITMAP);

            //將掃碼後得的資料返回給CaptureActivity去處理
            activity.handleDecode((Result) message.obj, barcode);
            break;

        ...... 
    }

handleMessage方法中考慮了好幾種情況,我們只需要瞭解這一種即可,將狀態標記為“成功”,將掃碼後得的資料返回給CaptureActivity去處理,所以最後還是回到CaptureActivityhandleDecode方法:

    /**
     * 處理掃描結果
     *
     * @param result
     * @param barcode
     */
    public void handleDecode(Result result, Bitmap barcode) {
        inactivityTimer.onActivity();
        playBeepSoundAndVibrate();
        String resultString = result.getText();
        if (resultString.equals("")) {
            Toast.makeText(this, "掃描失敗!", Toast.LENGTH_SHORT)
                    .show();
        } else {
            Intent data = new Intent();
            data.putExtra("SCAN_RESULT", resultString);
            setResult(RESULT_OK, data);
            finish();
        }
    }

主要邏輯為將獲取到掃碼後的資料放入到Intent中,結束當前掃碼頁面。重要的是:我們獲取到了Key值:SCAN_RESULT此時,我們可以在MainActivity中獲取到掃碼後的資料並處理它。以上只是舉一個例項,不僅這個Key值,還有其餘需要使用的都可在CaptureActivity找到,如下表格:

含義 Key resultCode
直接掃碼後獲得的資料 SCAN_RESULT Activity.RESULT_OK
相簿圖片掃碼後獲得的資料 LOCAL_PHOTO_RESULT 300
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case REQUEST_QRCODE:
                if (resultCode == Activity.RESULT_OK) {
                    //掃描後的業務邏輯
                    String code = data.getStringExtra("SCAN_RESULT");
                    if (code.contains("http") || code.contains("https")) {
                        //開啟連結
                        /*Intent intent = new Intent(this, AdBrowserActivity.class);
                        intent.putExtra(AdBrowserActivity.KEY_URL, code);
                        startActivity(intent);*/
                        Toast.makeText(this, code, Toast.LENGTH_SHORT).show();

                    } else {
                        Toast.makeText(this, code, Toast.LENGTH_SHORT).show();
                    }
                }else if(resultCode == 300){
                    //從本地相簿掃描後的業務邏輯
                    String code = data.getStringExtra("LOCAL_PHOTO_RESULT");
                    Toast.makeText(this, code, Toast.LENGTH_SHORT).show();
                }
                break;
        }

    }

以上程式碼通過Key值獲取到掃碼後的資料data.getStringExtra("SCAN_RESULT")。這裡多做了一個判斷,如果資料包含http或者https時,可以用瀏覽器開啟,這用使用者體驗會更好,當然,你也可以按照自己的想法去處理。

四. 拓展學習——閃光燈使用

在整合使用一個開源庫時,可是適當學習它的原始碼設計、封裝風格等等,例如Zxing中已實現的使用閃光燈,單獨將此程式碼拿出來,平常在開發中也會有使用到閃光燈的場景,大可以將此方法放到自己的工具庫中,使用的時候呼叫即可,此刻可以很好的參考參考大神封裝的方法:

CameraManager.java

    private Camera camera;
    public static void init(Context context) {
        if (cameraManager == null) {
            cameraManager = new CameraManager(context);
        }
    }

    /**
     * 通過設定Camera開啟閃光燈
     */
    public void turnLightOn() {
        if (camera == null) {
            return;
        }
        Parameters parameters = camera.getParameters();
        if (parameters == null) {
            return;
        }

        List<String> flashModes = parameters.getSupportedFlashModes();
        if (flashModes == null) {
            return;
        }
        String flashMode = parameters.getFlashMode();
        Log.i(TAG, "Flash mode: " + flashMode);
        Log.i(TAG, "Flash modes: " + flashModes);
        // 閃光燈關閉狀態
        if (!Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
            // Turn on the flash
            if (flashModes.contains(Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
                camera.setParameters(parameters);
                camera.startPreview();
            } else {
            }
        }
    }

    /**
     * 通過設定Camera關閉閃光燈
     * 
     * @param mCamera
     */
    public void turnLightOff() {
        if (camera == null) {
            return;
        }
        Parameters parameters = camera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        String flashMode = parameters.getFlashMode();
        // Check if camera flash exists
        if (flashModes == null) {
            return;
        }
        // 閃光燈開啟狀態
        if (!Parameters.FLASH_MODE_OFF.equals(flashMode)) {
            // Turn off the flash
            if (flashModes.contains(Parameters.FLASH_MODE_OFF)) {
                parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
                camera.setParameters(parameters);
            } else {
                Log.e(TAG, "FLASH_MODE_OFF not supported");
            }
        }
    }

最後附加一點自己的最近的想法:

最近這幾個月寫部落格寫的不像初學Android時那麼勤了,與同伴在做一個App,專案中用了不少開源庫,這個Zxing也是其中一個。寫完這篇部落格不由得捫心自問,這些東西僅僅停留在會使用的階段,不算是真正的學習,就像我這篇部落格,是一篇參考型文章,並非學習類文章,通篇看下來也不會有恍然大悟的感覺,作為筆者的我感覺也沒有深層次的學到什麼,正如很多大牛說過的:許多開源庫不是會用就夠了,我們還需要學習它的實現封裝等等,這才能使我們進步(雞湯奉上)……

所以接下來打算學習一些偏向底層的乾貨,緩解近期的迷茫。將自己的想法分享給各位志同道合的friends,在未來槓 Android的路上繼續努力,哈哈哈~

希望對你們有幫助 :)

相關推薦

Android實際運用Zxing整合掃描 定義介面demo原始碼

二維碼掃描,各大主流App必不可少的功能,而且google已將輪子替我們造好,直接拿來使用即可。以下是教學如何將Zxing開源庫整合到自己專案中,並且自定義掃碼介面,後期可根據自己的業務需求進行修改,最後補充了一點由此延伸的學習技能點。 一. 整合Zing

Android使用xml定義軟鍵盤效果原始碼

Android使用xml自定義軟鍵盤效果原理: 1,軟鍵盤其實是個控制元件,使用android.inputmethodserver.KeyboardView類定義。 2,主佈局中使用幀佈局,當我們需要顯示軟鍵盤時設定為可見,不需要時設定為不可見。 3,編寫

Android ZXing整合---簡易

1.匯入依賴 implementation 'cn.bingoogolapple:bga-qrcode-zxing:1.2.5' 2.寫佈局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:

【劍指offer】陣列中的查詢——複雜度為On+m——採用PHP寫法

背景 今天偶然進入牛客網,看到《劍指offer》模組有演算法題,就開始試著答題   題目描述 在一個二維陣列中(每個一維陣列的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣

AndroidPopWindow — 對Android的底部彈窗、頂部彈窗選單定義介面的使用封裝

PopWindow 對Android的底部彈窗,頂部彈窗選單及自定義介面的使用封裝。   一,介紹 主要是用於在彈窗中顯示一些有限的選單項,也支援新增自定義View,有如下3中彈出方式: 1、底部彈出,主要參考預設風格是參考IOS的UIAlertContro

SpringMVC定義異常處理器 HandlerExceptionResolver接口

pin org ota admin pack property framework ase exception 自定義異常處理器和系統異常處理器的提升版可以實現相同的功能,但是使用的方法不同,自定義異常處理器可以不用在配置文件中配置name多東西,只需要一個異常處理器就可以

Android】_定義Adapter_學生註冊無資料庫

本文是完善前一篇學生註冊文章(https://blog.csdn.net/cungudafa/article/details/84780652)中:對`自定義ListView`增加`介面卡Adapter`實現對每個學生個體進行再編輯和刪除操作。

Android 定義view --圓形百分比進度條

注:本文由於是在學習過程中寫的,存在大量問題(overdraw onDraw new物件),請讀者們不要被誤導!!解決辦法見後面的部落格。 起因 最近公司專案有需求需要用到輕量級圖表如下

Unity + ZXing + 螢幕旋轉自動適應 + 定義介面

由於使用 ZXing 在unity進行二維碼識別功能的人較多,這邊我也試著使用了一下ZXing。發現如下幾個問題: ZXing僅僅只提供了二維碼和條形碼等的解碼,並沒有提供在unity中的預製件(也就是prefab)。這樣就會導致很多第一次使用zxing的盆

android定義環形統計圖帶動畫

一、測試截圖 二、實現原理  package com.freedomanlib; import java.util.Timer; import java.util.TimerTask; import android.annotation.SuppressLint;

Android定義ActionBar背景顏色原始碼分析

Android自定義ActionBar背景顏色 修改前後效果圖:   在style.xml里加一行程式碼搞定 <!-- Base application theme. --> <style name="AppTheme" parent="

【朝花夕拾】Android定義View篇之十一View的滑動,彈性滑動與定義PagerView

前言        由於手機螢幕尺寸有限,但是又經常需要在螢幕中顯示大量的內容,這就使得必須有部分內容顯示,部分內容隱藏。這就需要用一個Android中很重要的概念——滑動。滑動,顧名思義就是view從一個地方移動到另外一個地方,我們平時看到的

Android使用ZXing生成支持加入Logo圖案

over rmi api note sta size argb_8888 reat for循環 ZXing是谷歌的一個開源庫。能夠用來生成二維碼、掃描二維碼。本文所介紹的是第一部分。 首先上效果圖: ZXing相關各種文件官方下載地址:https://g

Android使用ZXing生成支援新增Logo圖案

ZXing是谷歌的一個開源庫,可以用來生成二維碼、掃描二維碼。本文所介紹的是第一部分。 首先上效果圖: 1.生成二維碼的工具類 /** * 二維碼生成工具類 */ public class QRCodeUtil { /** * 生成二維碼Bit

androidZxing實現功能的快速整合以及掃描介面的定製

Zxing二維碼庫是相當豐富。但是我們往往只需要裡面的掃碼識別以及生成二維碼的功能就可以了,所以這次用到了已經抽離出核銷程式碼的框架包 compile ‘com.journeyapps:zxing-android-embedded:3.3.0’,來快速整合開發

Android 基於google Zxing實現 條形碼掃描,仿微信掃描效果

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Xamarin.Android-用ZXing實現掃描以及連續掃描

一、前言 本文的內容有兩個基礎:ZXing.Net和ZXing.Net.Mobile ZXing.Net:ZXing的C#實現,主要封裝了各種二維碼的編碼、解碼等跨平臺的演算法 ZXing.Net.Mobile:對ZXing.Net在xamarin的應用進行了封裝,主要實現了攝像頭掃描、掃描view、掃

Android 基於Zxing掃描優化

最近公司專案App中要整合二維碼掃描來適應在戶外工作的時候,對碼頭集裝箱等上面貼的A4紙張列印的二維碼進行識別, 一般App二維碼整合後,能掃出來就不管了,但是我們在整合成功後,根據使用者反饋,在戶外的環境下,很多二維碼識別不了,或者識別速度慢,我們自己也是適用了一下,發現也確實是這樣. &nb

Android平臺利用Zxing生成與解析圖片中的

轉載請註明http://blog.csdn.net/houkai6/article/details/47102733 1. 生成二維碼 public final class EncodingHandler { private static final int BLACK

Android 基於google Zxing實現、條形碼掃描,仿微信掃描效果現在正做個掃描App、收藏

瞭解二維碼這個東西還是從微信中,當時微信推出二維碼掃描功能,自己感覺挺新穎的,從一張圖片中掃一下竟然能直接加好友,不可思議啊,那時候還不瞭解二維碼,呵呵,然後做專案的時候,老闆說要加上二維碼掃描功能,然後自己的屁顛屁顛的去百度,google啥的,發現很多朋友都