【轉】Android平台下利用zxing實現二維碼開發

來源:互聯網
上載者:User

標籤:

http://www.cnblogs.com/dolphin0520/p/3355728.html

  現在走在大街小巷都能看到二維碼,而且最近由於項目需要,所以研究了下二維碼開發的東西,開源的二維碼掃描庫主要有zxing和zbar,zbar在iPos平台上應用比較成熟,而在Android平台上主流還是用zxing庫,因此這裡主要講述如何利用zxing進行二維碼開發。

1.如何將zxing的Android源碼匯入工程。

  在匯入zxing的android源碼之前,先去官方下載zxing的源碼:http://code.google.com/p/zxing/downloads/list。

  我這裡下載的是1.6版本的,我實驗了幾個版本,發現2.0以後的版本實際用起來沒有1.6和1.7版本的好用,最後選擇了1.6版本。

  zxing 1.6源碼結構如下:

 

  其中android檔案夾就是android平台下的官方例子。

  在匯入之前先要對core檔案下的源碼進行編譯,得到核心包core.jar。

  編譯方法請參照:http://blog.163.com/[email protected]/blog/static/671560502011611111116747/

  然後就可以匯入android平台下的例子了,匯入方法如下:

  1)開啟Eclipse,建立android項目:(注意不要直接把android檔案夾拷到workspace下匯入,那樣會無法匯入)

  

  2)匯入核心包core.jar。

  3)修改strings.xml檔案。在匯入core.jar之後工程還是會有錯誤:

  

  出現這種錯誤可能是由於字元錯誤導致的,只需要把所有的%s 和%f改成 %1s和f   即可。

  修改完之後重新清理項目,此時已經沒有錯誤了:

  

   4)運行效果:

  

2.代碼簡化

  上面代碼中,很多功能我們在自己的項目中都用不到,因此需要對其進行簡化,至於如何簡化這裡就不贅述了,網上有很多教程。下面只給出簡化後的結果:

  

  如果只進行二維碼識別和二維碼產生的話,只需要上麵包中的檔案。其中CaptureActivity.java是拍照取景的類,camera包下面的類主要與照相機相關的類,decoding和encoding是解碼和編碼相關的類,view是取景框相關的類。

3.將簡化的zxing代碼嵌入自己的工程。

  在自己的工程中嵌入簡化的zxing代碼即可實現二維碼產生和識別功能。

  嵌入方法:

  1)將上述簡化的代碼拖到自己工程目錄下;

  2)將values檔案夾和raw檔案夾複製自己工程目錄下;

  3)建立CaptureActivity.java的布局檔案capture.xml。

<?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" >    <SurfaceView        android:id="@+id/preview_view"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center" />    <com.zxing.view.ViewfinderView        android:id="@+id/viewfinder_view"        android:layout_width="wrap_content"        android:layout_height="wrap_content" />    <RelativeLayout        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:layout_gravity="center"        android:orientation="vertical" >        <TextView            android:layout_width="fill_parent"            android:layout_height="wrap_content"            android:layout_alignParentTop="true"            android:layout_centerInParent="true"            android:gravity="center"            android:paddingBottom="10dp"            android:paddingTop="10dp"            android:text="Scan Barcode"            android:textColor="@android:color/white"            android:textSize="18sp"            android:textStyle="bold" />        <Button            android:id="@+id/btn_cancel_scan"            android:layout_width="230dp"            android:layout_height="40dp"            android:layout_alignParentBottom="true"            android:layout_centerInParent="true"            android:layout_marginBottom="75dp"            android:text="Cancel"            android:textSize="15sp"            android:textStyle="bold" />            </RelativeLayout></FrameLayout>

  3)匯入core.jar包

  4)修改AndrodMainfest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"      package="com.qrcode"      android:versionCode="1"      android:versionName="1.0">    <uses-sdk android:minSdkVersion="7" />        <uses-permission android:name="android.permission.VIBRATE" />  <!-- 震動許可權 -->    <uses-permission android:name="android.permission.CAMERA" />    <uses-feature android:name="android.hardware.camera" />        <!-- 使用照相機許可權 -->    <uses-feature android:name="android.hardware.camera.autofocus" />   <!-- 自動聚焦許可權 -->    <application android:icon="@drawable/icon" android:label="@string/app_name">        <activity android:name=".MainActivity"                  android:label="@string/app_name">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>                <!-- 隱藏鍵盤 --><!-- 全屏 -->        <activity            android:configChanges="orientation|keyboardHidden"                android:name="com.zxing.activity.CaptureActivity"            android:screenOrientation="portrait"            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"                android:windowSoftInputMode="stateAlwaysHidden" >        </activity>    </application></manifest>

  5)運行效果:

   

  在這裡識別二維碼之後是把結果返回到上一個activity顯示,所以在MainActivity中調用方法是:

Intent openCameraIntent = new Intent(MainActivity.this,CaptureActivity.class);startActivityForResult(openCameraIntent, 0);

  然後擷取結果在onActivityResult中進行:

@Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);            if (resultCode == RESULT_OK) {            Bundle bundle = data.getExtras();            String scanResult = bundle.getString("result");            resultTextView.setText(scanResult);        }    }

  如果要直接在CaptureActivity中彈出識別結果,可以更改CaptureActivity.java的handleDecode方法:

public void handleDecode(Result result, Bitmap barcode) {        inactivityTimer.onActivity();        playBeepSoundAndVibrate();        String resultString = result.getText();        //FIXME        if (resultString.equals("")) {            Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();        }else {//            System.out.println("Result:"+resultString);            Intent resultIntent = new Intent();            Bundle bundle = new Bundle();            bundle.putString("result", resultString);            resultIntent.putExtras(bundle);            this.setResult(RESULT_OK, resultIntent);        }        CaptureActivity.this.finish();    }

  上述代碼是得到結果之後傳給之前調用CaptureActivity的activity,所以如果要改的話只需要把else語句塊下面的語句和最後一句finish注釋掉,補上自己的代碼就可以了。比如可以改成:

public void handleDecode(Result result, Bitmap barcode) {        inactivityTimer.onActivity();        playBeepSoundAndVibrate();        final String resultString = result.getText();        //FIXME        if (resultString.equals("")) {            Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();        }else {//            System.out.println("Result:"+resultString);            /*Intent resultIntent = new Intent();            Bundle bundle = new Bundle();            bundle.putString("result", resultString);            resultIntent.putExtras(bundle);            this.setResult(RESULT_OK, resultIntent);*/                        AlertDialog resutlDialog = new AlertDialog.Builder(CaptureActivity.this).create();            resutlDialog.setTitle("掃描結果");            resutlDialog.setMessage(resultString);            resutlDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener()             {                @Override                public void onClick(DialogInterface dialog, int which)                 {                    dialog.dismiss();                }            });                resutlDialog.show();        }        //CaptureActivity.this.finish();    }

  我的工程源碼下載:http://files.cnblogs.com/dolphin0520/QRCode.rar

3.關於zxing源碼的若干問題。

  在實際使用過程中發現zxing源碼存在一些問題,下面逐一來說一下:

  1)豎屏問題。

  zxing給出的官方例子是橫屏的,但是對於手機實際上豎屏操作更加方便一點,如果要改成豎屏,需要修改以下幾個檔案:

  首先將AndrodMainfest.xml下CaptureActivity的配置改為豎屏:

android:screenOrientation="portrait"

  然後需要修改CameraConfigurationManager.java下的setDesiredCameraParameters方法,該方法直接使用

camera.setDisplayOrientation(90);

  來讓旋轉螢幕90度,這個辦法在android 2.2版本以後是可行的,但是2.2之前的版本並沒有提供這個介面,因此需要對sdk版本進行判斷:

if (Integer.parseInt(Build.VERSION.SDK) >=8)         setDisplayOrientation(camera, 90);        else     {             if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)         {                 parameters.set("orientation", "portrait");                 parameters.set("rotation", 90);             }             if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)         {                 parameters.set("orientation", "landscape");                 parameters.set("rotation", 90);             }      }   

  在修改為豎屏之後發現取景框會發生展開,需要進行以下幾點修改:

  在CameraManager.java中將:

rect.left = rect.left * cameraResolution.x / screenResolution.x;rect.right = rect.right * cameraResolution.x / screenResolution.x;rect.top = rect.top * cameraResolution.y / screenResolution.y;rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;

  修改為:

rect.left = rect.left * cameraResolution.y / screenResolution.x;rect.right = rect.right * cameraResolution.y / screenResolution.x;rect.top = rect.top * cameraResolution.x / screenResolution.y;rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

  然後將DecodeHandler類中的方法decode改為:

private void decode(byte[] data, int width, int height) {    long start = System.currentTimeMillis();    Result rawResult = null;        //modify here    byte[] rotatedData = new byte[data.length];    for (int y = 0; y < height; y++) {        for (int x = 0; x < width; x++)            rotatedData[x * height + height - y - 1] = data[x + y * width];    }    int tmp = width; // Here we are swapping, that‘s the difference to #11    width = height;    height = tmp;        PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));    try {      rawResult = multiFormatReader.decodeWithState(bitmap);    } catch (ReaderException re) {      // continue    } finally {      multiFormatReader.reset();    }    if (rawResult != null) {      long end = System.currentTimeMillis();      Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());      Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult);      Bundle bundle = new Bundle();      bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());      message.setData(bundle);      //Log.d(TAG, "Sending decode succeeded message...");      message.sendToTarget();    } else {      Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);      message.sendToTarget();    }  }

  再將CameraConfigurationManager類中的方法initFromCameraParameters修改為:

void initFromCameraParameters(Camera camera) {    Camera.Parameters parameters = camera.getParameters();    previewFormat = parameters.getPreviewFormat();    previewFormatString = parameters.get("preview-format");    Log.d(TAG, "Default preview format: " + previewFormat + ‘/‘ + previewFormatString);    WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);    Display display = manager.getDefaultDisplay();    screenResolution = new Point(display.getWidth(), display.getHeight());    Log.d(TAG, "Screen resolution: " + screenResolution);        Point screenResolutionForCamera = new Point();    screenResolutionForCamera.x = screenResolution.x;    screenResolutionForCamera.y = screenResolution.y;    // preview size is always something like 480*320, other 320*480    if (screenResolution.x < screenResolution.y) {    screenResolutionForCamera.x = screenResolution.y;    screenResolutionForCamera.y = screenResolution.x;    }    cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);    //cameraResolution = getCameraResolution(parameters, screenResolution);    Log.d(TAG, "Camera resolution: " + screenResolution);  }

  最後將PlanarYUVLuminanceSource.java中的renderCroppedGreyscaleBitmap方法改為:

public Bitmap renderCroppedGreyscaleBitmap() {        int width = getWidth();        int height = getHeight();        int[] pixels = new int[width * height];        byte[] yuv = yuvData;        int inputOffset = top * dataHeight + left;        for (int y = 0; y < height; y++) {          int outputOffset = y * width;          for (int x = 0; x < width; x++) {            int grey = yuv[inputOffset + x] & 0xff;            pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);          }          inputOffset += dataHeight;        }        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);        return bitmap;      }

  3)取景框的繪製

  取景框的繪製可以參照這篇博文:http://www.cnblogs.com/forrestsun/archive/2012/11/06/2757005.html

  4)閃光燈的開啟和關閉

  如果需要開啟和關閉閃光燈,在CameraManager.java中添加2個方法:

  public void openLight()   //開啟閃光燈    {        if(camera!=null)        {             Parameters parameter=camera.getParameters();               parameter.setFlashMode(Parameters.FLASH_MODE_TORCH);              camera.setParameters(parameter);        }    }        public void closeLight()  //關閉閃光燈    {        if(camera!=null)        {             Parameters parameter=camera.getParameters();               parameter.setFlashMode(Parameters.FLASH_MODE_OFF);              camera.setParameters(parameter);        }    }

  然後在CaptureActivity中的initCamera方法中開啟閃光燈:

CameraManager.get().openDriver(surfaceHolder);CameraManager.get().openLight();   //開閃光燈

  在需要的地方關閉閃光燈即可。

  5)連續掃描問題

  如果在識別二維碼成功之後,需要連續多次掃描二維碼,只需在掃描完成之後添加代碼(在CaptureActivity的dandleDecode方法中添加):

if(handler!=null)     //實現連續掃描   handler.restartPreviewAndDecode();

  比如:

public void handleDecode(Result result, Bitmap barcode) {        inactivityTimer.onActivity();        playBeepSoundAndVibrate();        final String resultString = result.getText();        //FIXME        if (resultString.equals("")) {            Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();        }else {//            System.out.println("Result:"+resultString);            /*Intent resultIntent = new Intent();            Bundle bundle = new Bundle();            bundle.putString("result", resultString);            resultIntent.putExtras(bundle);            this.setResult(RESULT_OK, resultIntent);*/                        AlertDialog resutlDialog = new AlertDialog.Builder(CaptureActivity.this).create();            resutlDialog.setTitle("掃描結果");            resutlDialog.setMessage(resultString);            resutlDialog.setButton(AlertDialog.BUTTON_POSITIVE, "開啟連結", new DialogInterface.OnClickListener()             {                @Override                public void onClick(DialogInterface dialog, int which)                 {                    dialog.dismiss();                    if(!isLegalUrl(resultString))   //如果url不合法                    {                        Toast.makeText(getApplicationContext(), "該連結不是合法的URL", Toast.LENGTH_SHORT).show();                        if(handler!=null)     //實現連續掃描                            handler.restartPreviewAndDecode();                        return;                    }                    Intent intent = new Intent();      //開啟連結                       intent.setAction("android.intent.action.VIEW");                        Uri content_url = Uri.parse(resultString);                       intent.setData(content_url);                      startActivity(intent);                }            });                        resutlDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener()             {                @Override                public void onClick(DialogInterface dialog, int which)                 {                    dialog.dismiss();                    if(handler!=null)     //實現連續掃描                        handler.restartPreviewAndDecode();                }            });                resutlDialog.show();        }        //CaptureActivity.this.finish();    }

  6)識別完成之後的震動。

  如果需要取消二維碼識別之後的震動,只需要在CaptureActivity類的onResume方法中將vibrate 設定為false即可。

  7)修改取景框距螢幕頂部位置

  如果需要修改取景框距螢幕頂部位置,只需要修改CameraManager.java的getFramingRect方法,在getFramingRect方法中,

int topOffset = (screenResolution.y - height) / 2

  這句是控製取景框到螢幕頂部的距離,若需要減小距螢幕頂部的距離,只需要將分母變大即可。

  8)取景框下方提示文字的繪製。

  在ViewfinderView.java的 Collection<ResultPoint> currentPossible = possibleResultPoints前面加入以下代碼:

  

TextPaint textPaint = new TextPaint();           
textPaint.setARGB(0xFF, 0xFF, 0xFF,0xFF); //字型顏色textPaint.setTextSize(TEXT_SIZE * density);textPaint.setAntiAlias(true); //設定消除鋸齒,否則字跡會很模糊
StaticLayout layout = new StaticLayout(getResources().getString(R.string.scan_text),textPaint, frame.right-frame.left,Alignment.ALIGN_NORMAL,1.0F,0.0F,true);canvas.translate(frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density)); //繪製起始位置layout.draw(canvas);

  這裡解釋一下,textPaint.setAntiAlias(true); 是設定為消除鋸齒,否則字型會很模糊。StaticLayout的第一個參數就是要繪製的字串,第二個是畫筆,第三個參數是設定每一行的寬度,即超過該寬度就換行,第四個是對齊。

  canvas.translate(frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density))的參數是繪製字串的起始位置。

  9)亂碼問題

  在掃描一部分二維碼時會出現亂碼的情況,是由於編碼的問題,可以參照這篇文章:

  http://www.2cto.com/kf/201209/152900.html

  如果不想編譯core包,我的工程源碼中的core包是已經改好了的,可以識別GBK二維碼,需要的朋友可以直接拿去用。

  關於zxing 二維碼識別的問題暫時就討論這麼多,有興趣的朋友可以讀源碼深入研究一下。

 

【轉】Android平台下利用zxing實現二維碼開發

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.