Zxing实现工作原理之QRCode

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
package example.com.zxingdemo;
 
 
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
 
import com.google.zxing.WriterException;
 
import zxing.encoding.EncodingHandler;
 
public class MainActivity extends Activity {
     private final static int SCANNIN_GREQUEST_CODE = 1 ;
     /**
      * 显示扫描结果
      */
     private TextView mTextView ;
     /**
      * 显示扫描拍的图片
      */
     private ImageView mImageView;
 
     /**
      * 输入框产生二维码
      * @param savedInstanceState
      */
     private EditText qrStrEditText;
 
 
     @Override
     protected void 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( new OnClickListener() {
 
             @Override
             public void onClick(View v) {
                 Intent intent = new Intent();
                 intent.setClass(MainActivity. this , MipcaActivityCapture. class );
                 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                 startActivityForResult(intent, SCANNIN_GREQUEST_CODE);
             }
         });
 
 
         //产生二维码
         mBtnTwoCode.setOnClickListener( new OnClickListener() {
             @Override
             public void 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( new OnClickListener() {
             @Override
             public void onClick(View v) {
                 String contentString = qrStrEditText.getText().toString();
                 int size = contentString.length();
                 for ( int i = 0 ; i < size; i++) {
                     int c = 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
     protected void onActivityResult( int requestCode, int resultCode, Intent data) {
         super .onActivityResult(requestCode, resultCode, data);
         switch (requestCode) {
             case SCANNIN_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
package example.com.zxingdemo;
 
import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Vibrator;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
 
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
 
import java.io.IOException;
import java.util.Vector;
 
import zxing.camera.CameraManager;
import zxing.decoding.CaptureActivityHandler;
import zxing.decoding.InactivityTimer;
import zxing.view.ViewfinderView;
 
/**
  * Initial the camera
  * @author Ryan.Tang
  */
public class MipcaActivityCapture extends Activity implements Callback {
 
     private CaptureActivityHandler handler;
     private ViewfinderView viewfinderView;
     private boolean hasSurface;
     private Vector<barcodeformat> decodeFormats;
     private String characterSet;
     private InactivityTimer inactivityTimer;
     private MediaPlayer mediaPlayer;
     private boolean playBeep;
     private static final float BEEP_VOLUME = 0 .10f;
     private boolean vibrate;
 
     /**
      * Called when the activity is first created.
      */
     @Override
     public void 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( new OnClickListener() {
 
             @Override
             public void onClick(View v) {
                 MipcaActivityCapture. this .finish();
 
             }
         });
         hasSurface = false ;
         inactivityTimer = new InactivityTimer( this );
     }
 
     @Override
     protected void 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
     protected void onPause() {
         super .onPause();
         if (handler != null ) {
             handler.quitSynchronously();
             handler = null ;
         }
         CameraManager.get().closeDriver();
     }
 
     @Override
     protected void onDestroy() {
         inactivityTimer.shutdown();
         super .onDestroy();
     }
 
     /**
      * 处理扫描结果
      *
      * @param result
      * @param barcode
      */
     public void 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 = new Intent();
             Bundle bundle = new Bundle();
             bundle.putString( "result" , resultString);
             bundle.putParcelable( "bitmap" , barcode);
             resultIntent.putExtras(bundle);
             this .setResult(RESULT_OK, resultIntent);
         }
         MipcaActivityCapture. this .finish();
     }
 
     private void initCamera(SurfaceHolder surfaceHolder) {
         try {
             CameraManager.get().openDriver(surfaceHolder);
         } catch (IOException ioe) {
             return ;
         } catch (RuntimeException e) {
             return ;
         }
         if (handler == null ) {
             handler = new CaptureActivityHandler( this , decodeFormats,
                     characterSet);
         }
     }
 
     @Override
     public void surfaceChanged(SurfaceHolder holder, int format, int width,
                                int height) {
 
     }
 
     @Override
     public void surfaceCreated(SurfaceHolder holder) {
         if (!hasSurface) {
             hasSurface = true ;
             initCamera(holder);
         }
 
     }
 
     @Override
     public void surfaceDestroyed(SurfaceHolder holder) {
         hasSurface = false ;
 
     }
 
     public ViewfinderView getViewfinderView() {
         return viewfinderView;
     }
 
     public Handler getHandler() {
         return handler;
     }
 
     public void drawViewfinder() {
         viewfinderView.drawViewfinder();
 
     }
 
     private void 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 = new MediaPlayer();
             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 ;
             }
         }
     }
 
     private static final long VIBRATE_DURATION = 200L;
 
     private void 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.
      */
     private final OnCompletionListener beepListener = new OnCompletionListener() {
         public void 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( new OnClickListener() {
             @Override
             public void 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
private static final int BLACK = 0xff000000 ;
     //生成二维码
     public static Bitmap createQRCode(String str, int widthAndHeight) throws WriterException {
         Hashtable<encodehinttype, string= "" > hints = new Hashtable<encodehinttype, string= "" >(); 
         hints.put(EncodeHintType.CHARACTER_SET, "utf-8" );
         BitMatrix matrix = new MultiFormatWriter().encode(str,
                 BarcodeFormat.QR_CODE, widthAndHeight, widthAndHeight);
         int width = matrix.getWidth();
         int height = matrix.getHeight();
         int [] pixels = new int [width * height];
         
         for ( int y = 0 ; y < height; y++) {
             for ( int x = 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);
         return bitmap;
     }</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( new OnClickListener() {
             @Override
             public void onClick(View v) {
                 String contentString = qrStrEditText.getText().toString();
                 int size = contentString.length();
                 for ( int i = 0 ; i < size; i++) {
                     int c = 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
//生成条形码
     public static Bitmap CreateOneDCode(String content) throws WriterException {
         // 生成一维条码,编码时指定大小,不要生成了图片以后再进行缩放,这样会模糊导致识别失败
         BitMatrix matrix = new MultiFormatWriter().encode(content,
                 BarcodeFormat.CODE_128, 500 , 200 );
         int width = matrix.getWidth();
         int height = matrix.getHeight();
         int [] pixels = new int [width * height];
         for ( int y = 0 ; y < height; y++) {
             for ( int x = 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( new OnClickListener() {
 
             @Override
             public void onClick(View v) {
                 Intent intent = new Intent();
                 intent.setClass(MainActivity. this , MipcaActivityCapture. class );
                 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                 startActivityForResult(intent, SCANNIN_GREQUEST_CODE);
             }
         });

 

 

五、实现效果图

\ \

\ \

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值