1. 程式人生 > >Github專案解析(十)-->幾行程式碼快速整合二維碼掃描庫

Github專案解析(十)-->幾行程式碼快速整合二維碼掃描庫

上一篇文章中我們講解了activity切換動畫相關的知識點,這裡的切換動畫指的是是activity跳轉時的動畫效果。在上一篇文章中我們總結了有五種方式實現activity切換時實現動畫效果。並且依次的介紹一下每種實現activity切換動畫效果的實現方式,以及效果,具體更多關於Activity切換動畫的知識點可參考我的:Github專案解析(九)–>實現Activity跳轉動畫的五種方式

而本文將講解一下我最近寫的一個快速整合二維碼掃描庫,這裡需要說明的是其核心的實現掃描的功能,是通過呼叫ZXing庫實現的。內部App中使用到了二維碼掃描功能,但是網上找了一些關於二維碼掃描的例子,只是我在整合的時候發現通過android studio整合zxing二維碼庫不是特別方便,由於我就有了將其製作成標準庫的想法,也就有了本文即快速整合二維碼掃描庫。

使用說明

  • 可開啟預設二維碼掃描頁面

  • 支援對圖片Bitmap的掃描功能

  • 支援對UI的定製化操作

  • 支援對條形碼的掃描功能

  • 支援生成二維碼操作

  • 支援控制閃光燈開關

使用方式:

  • 整合預設的二維碼掃描頁面

在具體介紹該掃描庫之前我們先看一下其具體的使用方式,看看是不是幾行程式碼就可以整合二維碼掃描的功能。

  • 在module的build.gradle中執行compile操作
compile 'cn.yipianfengye.android:zxing-library:2.2'
  • 在Application中執行初始化操作
@Override
    public void
onCreate() { super.onCreate(); ZXingLibrary.initDisplayOpinion(this); }
  • 在程式碼中執行開啟掃描二維碼介面操作
/**
         * 開啟預設二維碼掃描介面
         */
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new
Intent(MainActivity.this, CaptureActivity.class); startActivityForResult(intent, REQUEST_CODE); } });

這裡的REQUEST_CODE是我們定義的int型常量。

  • 在Activity的onActivityResult方法中接收掃描結果
/**
         * 處理二維碼掃描結果
         */
        if (requestCode == REQUEST_CODE) {
            //處理掃描結果(在介面上顯示)
            if (null != data) {
                Bundle bundle = data.getExtras();
                if (bundle == null) {
                    return;
                }
                if (bundle.getInt(CodeUtils.RESULT_TYPE) == CodeUtils.RESULT_SUCCESS) {
                    String result = bundle.getString(CodeUtils.RESULT_STRING);
                    Toast.makeText(this, "解析結果:" + result, Toast.LENGTH_LONG).show();
                } else if (bundle.getInt(CodeUtils.RESULT_TYPE) == CodeUtils.RESULT_FAILED) {
                    Toast.makeText(MainActivity.this, "解析二維碼失敗", Toast.LENGTH_LONG).show();
                }
            }
        }

怎麼樣是不是很簡單?下面我們可以來看一下具體的執行效果:

執行效果:

這裡寫圖片描述

但是這樣的話是不是太簡單了,如果我想選擇圖片解析呢?別急,對二維碼圖片的解析也是支援的

  • 整合對二維碼圖片的解析功能

  • 呼叫系統API開啟相簿

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_IMAGE);
  • 在Activity的onActivityResult方法中獲取使用者選中的圖片並呼叫二維碼圖片解析API
if (requestCode == REQUEST_IMAGE) {
            if (data != null) {
                Uri uri = data.getData();
                ContentResolver cr = getContentResolver();
                try {
                    Bitmap mBitmap = MediaStore.Images.Media.getBitmap(cr, uri);//顯得到bitmap圖片

                    CodeUtils.analyzeBitmap(mBitmap, new CodeUtils.AnalyzeCallback() {
                        @Override
                        public void onAnalyzeSuccess(Bitmap mBitmap, String result) {
                            Toast.makeText(MainActivity.this, "解析結果:" + result, Toast.LENGTH_LONG).show();
                        }

                        @Override
                        public void onAnalyzeFailed() {
                            Toast.makeText(MainActivity.this, "解析二維碼失敗", Toast.LENGTH_LONG).show();
                        }
                    });

                    if (mBitmap != null) {
                        mBitmap.recycle();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

執行效果

這裡寫圖片描述

有了預設的二維碼掃描介面,也有了對二維碼圖片的解析,可能有的同學會說如果我想定製化顯示UI怎麼辦呢?沒關係也支援滴。

  • 定製化顯示掃描UI

由於我們的掃描元件是通過Fragment實現的,所以能夠很輕鬆的實現掃描UI的定製化。

  • 在新的Activity中定義Layout佈局檔案
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_second"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/second_button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="取消"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginBottom="10dp"
        android:layout_gravity="bottom|center_horizontal"
        />

    <FrameLayout
        android:id="@+id/fl_my_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ></FrameLayout>

</FrameLayout>

啟動id為fl_my_container的FrameLayout就是我們需要替換的掃描元件,也就是說我們會將我們定義的掃描Fragment替換到id為fl_my_container的FrameLayout的位置。而上面的button是我們新增的一個額外的控制元件,在這裡你可以新增任意的控制元件,各種UI效果等。具體可以看下面在Activity的初始化過程。

  • 在Activity中執行Fragment的初始化操作
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        /**
         * 執行掃面Fragment的初始化操作
         */
        CaptureFragment captureFragment = new CaptureFragment();
        // 為二維碼掃描介面設定定製化介面
        CodeUtils.setFragmentArgs(captureFragment, R.layout.my_camera);

        captureFragment.setAnalyzeCallback(analyzeCallback);
        /**
         * 替換我們的掃描控制元件
         */ getSupportFragmentManager().beginTransaction().replace(R.id.fl_my_container, captureFragment).commit();
    }

其中analyzeCallback是我們定義的掃描回撥函式,其具體的定義:

/**
     * 二維碼解析回撥函式
     */
    CodeUtils.AnalyzeCallback analyzeCallback = new CodeUtils.AnalyzeCallback() {
        @Override
        public void onAnalyzeSuccess(Bitmap mBitmap, String result) {
            Intent resultIntent = new Intent();
            Bundle bundle = new Bundle();
            bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_SUCCESS);
            bundle.putString(CodeUtils.RESULT_STRING, result);
            resultIntent.putExtras(bundle);
            SecondActivity.this.setResult(RESULT_OK, resultIntent);
            SecondActivity.this.finish();
        }

        @Override
        public void onAnalyzeFailed() {
            Intent resultIntent = new Intent();
            Bundle bundle = new Bundle();
            bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_FAILED);
            bundle.putString(CodeUtils.RESULT_STRING, "");
            resultIntent.putExtras(bundle);
            SecondActivity.this.setResult(RESULT_OK, resultIntent);
            SecondActivity.this.finish();
        }
    };

仔細看的話,你會發現我們呼叫了CondeUtils.setFragmentArgs方法,該方法主要用於修改掃描介面掃描框與透明框相對位置的,與若不呼叫的話,其會顯示預設的元件效果,而如果呼叫該方法的話,可以修改掃描框與透明框的相對位置等UI效果,我們可以看一下my_camera佈局檔案的實現。

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

    <SurfaceView
        android:id="@+id/preview_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

    <com.uuzuche.lib_zxing.view.ViewfinderView
        android:id="@+id/viewfinder_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:inner_width="200dp"
        app:inner_height="200dp"
        app:inner_margintop="150dp"
        app:inner_corner_color="@color/scan_corner_color"
        app:inner_corner_length="30dp"
        app:inner_corner_width="5dp"
        app:inner_scan_bitmap="@drawable/scan_image"
        app:inner_scan_speed="10"
        app:inner_scan_iscircle="false"
        />

</FrameLayout>

上面我們自定義的掃描控制元件的佈局檔案,下面我們看一下預設的掃描控制元件的佈局檔案:

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

    <SurfaceView
        android:id="@+id/preview_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

    <com.uuzuche.lib_zxing.view.ViewfinderView
        android:id="@+id/viewfinder_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

</FrameLayout>

可以發現其主要的區別就是在自定義的掃描控制元件中多了幾個自定義的掃描框屬性:

<declare-styleable name="innerrect">
        <attr name="inner_width" format="dimension"/><!-- 控制掃描框的寬度 -->
        <attr name="inner_height" format="dimension"/><!-- 控制掃描框的高度 -->
        <attr name="inner_margintop" format="dimension" /><!-- 控制掃描框距離頂部的距離 -->
        <attr name="inner_corner_color" format="color" /><!-- 控制掃描框四角的顏色 -->
        <attr name="inner_corner_length" format="dimension" /><!-- 控制掃描框四角的長度 -->
        <attr name="inner_corner_width" format="dimension" /><!-- 控制掃描框四角的寬度 -->
        <attr name="inner_scan_bitmap" format="reference" /><!-- 控制掃描圖 -->
        <attr name="inner_scan_speed" format="integer" /><!-- 控制掃描速度 -->
        <attr name="inner_scan_iscircle format="false""><!-- 控制小圓點是否展示 -->
    </declare-styleable>

通過以上幾個屬性我們就可以定製化的顯示我們的掃描UI了,比如定製化微信掃描UI:

執行效果

這裡寫圖片描述

當然瞭如果以上的以上,你還是對定製化UI方面不太滿意,可以直接下載我的專案,然後引入lib-zxing module作為你的module,直接修改其程式碼。

  • 生成二維碼圖片

  • 生成帶Logo的二維碼圖片:

/**
         * 生成二維碼圖片
         */
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String textContent = editText.getText().toString();
                if (TextUtils.isEmpty(textContent)) {
                    Toast.makeText(ThreeActivity.this, "您的輸入為空!", Toast.LENGTH_SHORT).show();
                    return;
                }
                editText.setText("");
                mBitmap = CodeUtils.createImage(textContent, 400, 400, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
                imageView.setImageBitmap(mBitmap);
            }
        });
  • 生成不帶logo的二維碼圖片
/**
         * 生成不帶logo的二維碼圖片
         */
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String textContent = editText.getText().toString();
                if (TextUtils.isEmpty(textContent)) {
                    Toast.makeText(ThreeActivity.this, "您的輸入為空!", Toast.LENGTH_SHORT).show();
                    return;
                }
                editText.setText("");
                mBitmap = CodeUtils.createImage(textContent, 400, 400, null);
                imageView.setImageBitmap(mBitmap);
            }
        });
  • 執行效果

這裡寫圖片描述

  • 支援控制閃光燈
/**
 * 開啟閃光燈
 */
CodeUtils.isLightEnable(true);

/**
 * 關閉閃光燈
 */
 CodeUtils.isLightEnable(false);

建立二維碼庫的流程:

  • 建立一個android studio專案,並建立zxingLibrary庫;

這裡寫圖片描述

  • 在zxingLibrary庫中實現二維碼的掃描操作;

在實現過程中有以下幾個注意點:

(1)由於二維碼掃描介面是一個Activity,所以需要在AndroidManifest.xml中定義,而我為了減少對專案的依賴,選擇了在zxingLibrary中的AndroidManifest.xml中定義該Activity。

<application android:allowBackup="true"
        >

        <activity
            android:configChanges="orientation|keyboardHidden"
            android:name="com.uuzuche.lib_zxing.activity.CaptureActivity"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="stateAlwaysHidden"
            android:label="掃描二維碼"
           android:theme="@style/Theme.AppCompat.NoActionBar"
            ></activity>
    </application>

(2)由於掃描操作需要使用攝像頭等許可權,也在zxingLibrary中的AndroidManifest.xml中申請了部分許可權:

<uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />

    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />

(3)原來的專案中二維碼掃面的部分是作為主專案實現的,部分程式碼使用了switch(id)的操作,而這樣的程式碼在library中有問題,具體可參考:在Android library中不能使用switch-case語句訪問資源ID的原因分析及解決方案,所以我做了修改:

修改之前:

@Override
  public void handleMessage(Message message) {
    switch (message.what) {
      case R.id.decode:
        decode((byte[]) message.obj, message.arg1, message.arg2);
        break;
      case R.id.quit:
        Looper.myLooper().quit();
        break;
    }
  }

修改之後:

@Override
  public void handleMessage(Message message) {
    if (message.what == R.id.decode) {
      decode((byte[]) message.obj, message.arg1, message.arg2);
    } else if (message.what == R.id.quit) {
      Looper.myLooper().quit();
    }
  }
  • 將zxingLibrary庫上傳至Jcenter中

有同學反應掃描過程中,二維碼圖片有拉伸的現象,最新的compile已經改正,具體原因可參考:Zxing圖片拉伸解決 Android 二維碼掃描

總結:

以上就是我實現的這個快速繼承二維碼掃描庫的過程。主要優點就是可以通過android studio快速的繼承到我們的專案中,有興趣的同學可以到github上看一下具體實現。專案地址:android-zxingLibrary