Zxing实现工作原理之QRCode

来源:互联网 发布:移动网络代理成电信的 编辑:程序博客网 时间:2024/06/08 19:44

QRCode是被广泛应用的一种二维码,解码速度快。二维码相对于条形码来说,二维码的存储数据量更大,空间利用率高,有一定的容错性。

二维码原理介绍:

二维码是用某种特定的几何图形按一定的规律在平面上分布的黑白相间的图形记录数据符号信息的;

在代码编制上巧妙的利用构成计算机内部逻辑基础的0/1比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图像输入设备或光电扫描设备自动识读以实现信息自动处理;

二维码能够在横向和纵向两个方位同时表达信息,因此能在很小的面积内表达大量的信息;

二维码相对于条形码的优势就是省空间;

 

\

上图是一个QRCode的基本结构:

位置探测图形、位置探测图形分隔符、定位图形:用于二维码的定位,对每个QR来说,位置都是固定存在的,只是大小规格有所差异;

校正图形:确定规格,校正图形的数量和位置也就确定了;

格式信息:表示二维码的纠错级别,分为L、M、Q、H;

版本信息:即二维码的规格,QR码符号共有40种规格的矩阵;

数据和纠错码字:实际保存的二维码信息,和纠错码字(用于修正二维码损坏带来的错误)。

 

 

条形码原理介绍:

\

 

条形码扫描器结构

由于不同颜色的物体,其反射的可见光的波长不同,所以当条形码扫描器光源发出的光经光阑及凸透镜1后,照射到黑白相间的条形码上时,反射光经凸透镜2聚焦后,照射到光电转换器上,于是光电转换器接受到与白条和黑条相对应的强弱不同的反射光信号,并转换成相应的电信号输出到放大整形电路,整形电路把模拟信号转换成数字电信号,再经译码接口电路译成数字字符信息。

整形电路的脉冲数字信号经译码器译成数字、字符信息.它通过识别起始、终止字符来判别出条形码符号的码制及扫描方向;通过测量脉冲数字电信号0、1的数目来判别出条和空的数目.通过测量0、1信号持续的时间来判别条和空的宽度.这样便得到了被辩读的条形码符号的条和空的数目及相应的宽度和所用码制,根据码制所对应的编码规则,便可将条形符号换成相应的数字、字符信息,通过接口电路送给计算机系统进行数据处理与管理,便完成了条形码辨读的全过程。

Zxing是一个开源的,用于Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的接口。

Zxing可以实现使用手机的内置摄像头完成条形码和二维码的扫描与解码。

Zxing可以实现条形码和二维码的编码与解码。

Zxing目前支持的格式如下:UPC-A、UPC-E、EAN-8、EAN-13、39码、93码、代码128、QR码。

二、Zxing使用原理介绍

Zxing是一个开源的,用于Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的接口。

Zxing可以实现使用手机的内置摄像头完成条形码和二维码的扫描与解码。

Zxing可以实现条形码和二维码的编码与解码。

Zxing目前支持的格式如下:UPC-A、UPC-E、EAN-8、EAN-13、39码、93码、代码128、QR码。

三、实现步骤

1.导入Zxing.jar包,直接将源码内的com.mining.app.zxing.camera,com.mining.app.zxing.decoding, com.mining.app.zxing.view, com.mining.app.zxing.encoding四个文件夹,zxing package直接复制到你的工程目录下。

 

源码下载地址:http://code.google.com/p/zxing/

GitHub下载地址:https://github.com/zxing/zxing

2.将raw文件(用于),values,权限以及资源文件,全部准备完全,详情可参见源码。

3. activity.xml主菜单布局文件

 

?
1
2
3
4
5
6
7
8
9
10
11
<linearlayout android:background="#ffe1e0de"android:layout_height="match_parent"android:layout_width="match_parent"android:orientation="vertical"xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><button android:id="@+id/button1"android:layout_alignparenttop="true"android:layout_height="wrap_content"android:layout_width="fill_parent"android:text="扫描二维码/条形码">
    <edittext android:id="@+id/et_qr_string"android:layout_height="wrap_content"android:layout_width="match_parent">
    <linearlayout android:layout_height="wrap_content"android:layout_width="match_parent">
        </linearlayout></edittext></button><button android:id="@+id/button2"android:layout_alignparenttop="true"android:layout_height="wrap_content"android:layout_width="wrap_content"android:text="生成二维码"></button><button android:id="@+id/button3"android:layout_alignparenttop="true"android:layout_height="wrap_content"android:layout_width="wrap_content"android:text="生成条形码">
     
 
 
    <textview android:gravity="center_horizontal"android:id="@+id/result"android:layout_below="@+id/button1"android:layout_height="wrap_content"android:layout_width="fill_parent"android:lines="2"android:textcolor="@android:color/black"android:textsize="16sp">
 
    <imageview android:id="@+id/qrcode_bitmap"android:layout_alignparentleft="true"android:layout_below="@+id/result"android:layout_height="fill_parent"android:layout_width="fill_parent">
</imageview></textview></button></linearlayout>

 

4.activity_capture.xml扫描布局文件

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--?xml version="1.0"encoding="utf-8"?-->
<framelayout android:layout_height="fill_parent"android:layout_width="fill_parent"xmlns:android="http://schemas.android.com/apk/res/android">
 
    <relativelayout android:layout_height="fill_parent"android:layout_width="fill_parent">
 
        <surfaceview android:id="@+id/preview_view"android:layout_gravity="center"android:layout_height="fill_parent"android:layout_width="fill_parent">
 
        <zxing.view.viewfinderview android:id="@+id/viewfinder_view"android:layout_height="wrap_content"android:layout_width="wrap_content">
 
        <include android:id="@+id/include1"android:layout_alignparenttop="true"android:layout_height="wrap_content"android:layout_width="fill_parent"layout="@layout/activity_title">
    </include></zxing.view.viewfinderview></surfaceview></relativelayout>
 
</framelayout>

 

5.activity_title.xml Title布局文件

 

?
1
2
3
4
5
6
<!--?xml version="1.0"encoding="utf-8"?-->
<relativelayout android:background="@drawable/mmtitle_bg_alpha"android:layout_height="wrap_content"android:layout_width="fill_parent"xmlns:android="http://schemas.android.com/apk/res/android"><button android:background="@drawable/mm_title_back_btn"android:id="@+id/button_back"android:layout_centervertical="true"android:layout_height="wrap_content"android:layout_marginleft="2dip"android:layout_width="75.0dip"android:text="返回"android:textcolor="@android:color/white">
 
    <textview android:gravity="center_vertical"android:id="@+id/textview_title"android:layout_alignbaseline="@+id/button_back"android:layout_alignbottom="@+id/button_back"android:layout_centerhorizontal="true"android:layout_height="wrap_content"android:layout_width="wrap_content"android:text="二维码扫描"android:textcolor="@android:color/white"android:textsize="18sp">
 
</textview></button></relativelayout>

 

上述文件用于标签,便于修改以及占少量内存

 

6.MainActivity.java.主界面用于事件的处理

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
packageexample.com.zxingdemo;
 
 
importandroid.app.Activity;
importandroid.content.Intent;
importandroid.graphics.Bitmap;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
importandroid.widget.EditText;
importandroid.widget.ImageView;
importandroid.widget.TextView;
importandroid.widget.Toast;
 
importcom.google.zxing.WriterException;
 
importzxing.encoding.EncodingHandler;
 
publicclass MainActivity extendsActivity {
    privatefinal static int SCANNIN_GREQUEST_CODE = 1;
    /**
     * 显示扫描结果
     */
    privateTextView mTextView ;
    /**
     * 显示扫描拍的图片
     */
    privateImageView mImageView;
 
    /**
     * 输入框产生二维码
     * @param savedInstanceState
     */
    privateEditText qrStrEditText;
 
 
    @Override
    protectedvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        mTextView = (TextView) findViewById(R.id.result);
        mImageView = (ImageView) findViewById(R.id.qrcode_bitmap);
        qrStrEditText = (EditText) findViewById(R.id.et_qr_string);
 
        //点击按钮跳转到二维码扫描界面,这里用的是startActivityForResult跳转
        //扫描完了之后调到该界面
        Button mButtonScan = (Button) findViewById(R.id.button1);
        Button mBtnTwoCode = (Button) findViewById(R.id.button2);
        Button mBtnOneCode = (Button) findViewById(R.id.button3);
 
        mButtonScan.setOnClickListener(newOnClickListener() {
 
            @Override
            publicvoid onClick(View v) {
                Intent intent = newIntent();
                intent.setClass(MainActivity.this, MipcaActivityCapture.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivityForResult(intent, SCANNIN_GREQUEST_CODE);
            }
        });
 
 
        //产生二维码
        mBtnTwoCode.setOnClickListener(newOnClickListener() {
            @Override
            publicvoid onClick(View v) {
                String contentString = qrStrEditText.getText().toString();
                try{
                    if(!contentString.equals("")) {
                        Bitmap qrCodeBitmap = EncodingHandler.createQRCode(contentString, 350);
                        mImageView.setImageBitmap(qrCodeBitmap);
                        qrStrEditText.setText("");
                        mTextView.setText(contentString);
                    }else{
                        Toast.makeText(getApplicationContext(),"Text can be not empty", Toast.LENGTH_SHORT).show();
                    }
                }catch(WriterException e) {
                    e.printStackTrace();
                }
            }
        });
 
        //产生条形码
        mBtnOneCode.setOnClickListener(newOnClickListener() {
            @Override
            publicvoid onClick(View v) {
                String contentString = qrStrEditText.getText().toString();
                intsize = contentString.length();
                for(inti = 0; i < size; i++) {
                    intc = contentString.charAt(i);
                    if((19968<= c && c < 40623)) {
                        Toast.makeText(getApplicationContext(),"text not be chinese", Toast.LENGTH_SHORT).show();
                        return;
                    }
                }
                Bitmap mBmpOneCode = null;
                try{
                    if(contentString != null&& !"".equals(contentString)) {
                        mBmpOneCode = EncodingHandler.CreateOneDCode(contentString);
                        qrStrEditText.setText("");
                        mTextView.setText(contentString);
                    }
                }catch(WriterException e) {
                    e.printStackTrace();
                }
                if(mBmpOneCode != null) {
                    mImageView.setImageBitmap(mBmpOneCode);
                }
            }
        });
    }
 
 
    @Override
    protectedvoid onActivityResult(intrequestCode, intresultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch(requestCode) {
            caseSCANNIN_GREQUEST_CODE:
                if(resultCode == RESULT_OK){
                    Bundle bundle = data.getExtras();
                    //显示扫描到的内容
                    mTextView.setText(bundle.getString("result"));
                    //显示
                    mImageView.setImageBitmap((Bitmap) data.getParcelableExtra("bitmap"));
                }
                break;
        }
    }
}

 

7.MipcaActivityCapture.java.主要是调用起Camera功能进行扫描

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
packageexample.com.zxingdemo;
 
importandroid.app.Activity;
importandroid.content.Intent;
importandroid.content.res.AssetFileDescriptor;
importandroid.graphics.Bitmap;
importandroid.media.AudioManager;
importandroid.media.MediaPlayer;
importandroid.media.MediaPlayer.OnCompletionListener;
importandroid.os.Bundle;
importandroid.os.Handler;
importandroid.os.Vibrator;
importandroid.view.SurfaceHolder;
importandroid.view.SurfaceHolder.Callback;
importandroid.view.SurfaceView;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
importandroid.widget.Toast;
 
importcom.google.zxing.BarcodeFormat;
importcom.google.zxing.Result;
 
importjava.io.IOException;
importjava.util.Vector;
 
importzxing.camera.CameraManager;
importzxing.decoding.CaptureActivityHandler;
importzxing.decoding.InactivityTimer;
importzxing.view.ViewfinderView;
 
/**
 * Initial the camera
 * @author Ryan.Tang
 */
publicclass MipcaActivityCapture extendsActivity implementsCallback {
 
    privateCaptureActivityHandler handler;
    privateViewfinderView viewfinderView;
    privateboolean hasSurface;
    privateVector<barcodeformat> decodeFormats;
    privateString characterSet;
    privateInactivityTimer inactivityTimer;
    privateMediaPlayer mediaPlayer;
    privateboolean playBeep;
    privatestatic final float BEEP_VOLUME = 0.10f;
    privateboolean vibrate;
 
    /**
     * Called when the activity is first created.
     */
    @Override
    publicvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_capture);
        //ViewUtil.addTopView(getApplicationContext(), this, R.string.scan_card);
        CameraManager.init(getApplication());
        viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
 
        Button mButtonBack = (Button) findViewById(R.id.button_back);
        mButtonBack.setOnClickListener(newOnClickListener() {
 
            @Override
            publicvoid onClick(View v) {
                MipcaActivityCapture.this.finish();
 
            }
        });
        hasSurface = false;
        inactivityTimer = newInactivityTimer(this);
    }
 
    @Override
    protectedvoid onResume() {
        super.onResume();
        SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
        SurfaceHolder surfaceHolder = surfaceView.getHolder();
        if(hasSurface) {
            initCamera(surfaceHolder);
        }else{
            surfaceHolder.addCallback(this);
            surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }
        decodeFormats = null;
        characterSet = null;
 
        playBeep = true;
        AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
        if(audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
            playBeep = false;
        }
        initBeepSound();
        vibrate = true;
 
    }
 
    @Override
    protectedvoid onPause() {
        super.onPause();
        if(handler != null) {
            handler.quitSynchronously();
            handler = null;
        }
        CameraManager.get().closeDriver();
    }
 
    @Override
    protectedvoid onDestroy() {
        inactivityTimer.shutdown();
        super.onDestroy();
    }
 
    /**
     * 处理扫描结果
     *
     * @param result
     * @param barcode
     */
    publicvoid handleDecode(Result result, Bitmap barcode) {
        inactivityTimer.onActivity();
        playBeepSoundAndVibrate();
        String resultString = result.getText();
        if(resultString.equals("")) {
            Toast.makeText(MipcaActivityCapture.this,"Scan failed!", Toast.LENGTH_SHORT).show();
        }else{
            Intent resultIntent = newIntent();
            Bundle bundle = newBundle();
            bundle.putString("result", resultString);
            bundle.putParcelable("bitmap", barcode);
            resultIntent.putExtras(bundle);
            this.setResult(RESULT_OK, resultIntent);
        }
        MipcaActivityCapture.this.finish();
    }
 
    privatevoid initCamera(SurfaceHolder surfaceHolder) {
        try{
            CameraManager.get().openDriver(surfaceHolder);
        }catch(IOException ioe) {
            return;
        }catch(RuntimeException e) {
            return;
        }
        if(handler == null) {
            handler = newCaptureActivityHandler(this, decodeFormats,
                    characterSet);
        }
    }
 
    @Override
    publicvoid surfaceChanged(SurfaceHolder holder, intformat, intwidth,
                               intheight) {
 
    }
 
    @Override
    publicvoid surfaceCreated(SurfaceHolder holder) {
        if(!hasSurface) {
            hasSurface = true;
            initCamera(holder);
        }
 
    }
 
    @Override
    publicvoid surfaceDestroyed(SurfaceHolder holder) {
        hasSurface = false;
 
    }
 
    publicViewfinderView getViewfinderView() {
        returnviewfinderView;
    }
 
    publicHandler getHandler() {
        returnhandler;
    }
 
    publicvoid drawViewfinder() {
        viewfinderView.drawViewfinder();
 
    }
 
    privatevoid initBeepSound() {
        if(playBeep && mediaPlayer == null) {
            // The volume on STREAM_SYSTEM is not adjustable, and users found it
            // too loud,
            // so we now play on the music stream.
            setVolumeControlStream(AudioManager.STREAM_MUSIC);
            mediaPlayer = newMediaPlayer();
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mediaPlayer.setOnCompletionListener(beepListener);
 
            AssetFileDescriptor file = getResources().openRawResourceFd(
                    R.raw.beep);
            try{
                mediaPlayer.setDataSource(file.getFileDescriptor(),
                        file.getStartOffset(), file.getLength());
                file.close();
                mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
                mediaPlayer.prepare();
            }catch(IOException e) {
                mediaPlayer = null;
            }
        }
    }
 
    privatestatic final long VIBRATE_DURATION = 200L;
 
    privatevoid playBeepSoundAndVibrate() {
        if(playBeep && mediaPlayer != null) {
            mediaPlayer.start();
        }
        if(vibrate) {
            Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
            vibrator.vibrate(VIBRATE_DURATION);
        }
    }
 
    /**
     * When the beep has finished playing, rewind to queue up another one.
     */
    privatefinal OnCompletionListener beepListener = newOnCompletionListener() {
        publicvoid onCompletion(MediaPlayer mediaPlayer) {
            mediaPlayer.seekTo(0);
        }
    };
 
}</barcodeformat>

 

 

四、代码分析

1.生成二维码

调用方法:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//产生二维码
        mBtnTwoCode.setOnClickListener(newOnClickListener() {
            @Override
            publicvoid onClick(View v) {
                String contentString = qrStrEditText.getText().toString();
                try{
                    if(!contentString.equals("")) {
                        Bitmap qrCodeBitmap = EncodingHandler.createQRCode(contentString, 350);
                        mImageView.setImageBitmap(qrCodeBitmap);
                        qrStrEditText.setText("");
                        mTextView.setText(contentString);
                    }else{
                        Toast.makeText(getApplicationContext(),"Text can be not empty", Toast.LENGTH_SHORT).show();
                    }
                }catch(WriterException e) {
                    e.printStackTrace();
                }
            }
        });

 

实现方法:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
privatestatic final int BLACK = 0xff000000;
    //生成二维码
    publicstatic Bitmap createQRCode(String str,intwidthAndHeight) throwsWriterException {
        Hashtable<encodehinttype, string=""> hints = newHashtable<encodehinttype, string="">(); 
        hints.put(EncodeHintType.CHARACTER_SET,"utf-8");
        BitMatrix matrix = newMultiFormatWriter().encode(str,
                BarcodeFormat.QR_CODE, widthAndHeight, widthAndHeight);
        intwidth = matrix.getWidth();
        intheight = matrix.getHeight();
        int[] pixels = newint[width * height];
         
        for(inty = 0; y < height; y++) {
            for(intx = 0; x < width; x++) {
                if(matrix.get(x, y)) {
                    pixels[y * width + x] = BLACK;
                }
            }
        }
        Bitmap bitmap = Bitmap.createBitmap(width, height,
                Bitmap.Config.ARGB_8888);
        bitmap.setPixels(pixels,0, width, 0,0, width, height);
        returnbitmap;
    }</encodehinttype,></encodehinttype,>

 

2.生成条形码

调用方法:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//产生条形码
        mBtnOneCode.setOnClickListener(newOnClickListener() {
            @Override
            publicvoid onClick(View v) {
                String contentString = qrStrEditText.getText().toString();
                intsize = contentString.length();
                for(inti = 0; i < size; i++) {
                    intc = contentString.charAt(i);
                    if((19968<= c && c < 40623)) {
                        Toast.makeText(getApplicationContext(),"text not be chinese", Toast.LENGTH_SHORT).show();
                        return;
                    }
                }
                Bitmap mBmpOneCode = null;
                try{
                    if(contentString != null&& !"".equals(contentString)) {
                        mBmpOneCode = EncodingHandler.CreateOneDCode(contentString);
                        qrStrEditText.setText("");
                        mTextView.setText(contentString);
                    }
                }catch(WriterException e) {
                    e.printStackTrace();
                }
                if(mBmpOneCode != null) {
                    mImageView.setImageBitmap(mBmpOneCode);
                }
            }
        });

 

实现方法:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//生成条形码
    publicstatic Bitmap CreateOneDCode(String content) throwsWriterException {
        // 生成一维条码,编码时指定大小,不要生成了图片以后再进行缩放,这样会模糊导致识别失败
        BitMatrix matrix = newMultiFormatWriter().encode(content,
                BarcodeFormat.CODE_128,500,200);
        intwidth = matrix.getWidth();
        intheight = matrix.getHeight();
        int[] pixels = newint[width * height];
        for(inty = 0; y < height; y++) {
            for(intx = 0; x < width; x++) {
                if(matrix.get(x, y)) {
                    pixels[y * width + x] = BLACK;
                }
            }
        }

 

3.扫描二维码、条形码

 

?
1
2
3
4
5
6
7
8
9
10
mButtonScan.setOnClickListener(newOnClickListener() {
 
            @Override
            publicvoid onClick(View v) {
                Intent intent = newIntent();
                intent.setClass(MainActivity.this, MipcaActivityCapture.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivityForResult(intent, SCANNIN_GREQUEST_CODE);
            }
        });

 

 

五、实现效果图

\ \

\ \