Android二维码识别技术

本文介绍了一种基于第三方库实现的自定义二维码扫描界面的方法,包括界面效果展示、引入库文件、Java代码实现及自定义组件ViewfinderView的详细解析。

最近开发的项目中又涉及到二维码识别的技术,主要是做二维码扫码支付功能,我就从网上找了一个高逼格的可以自定义的三方库进行使用(使用的是博客一片枫叶_刘超,博客http://my.csdn.net/qq_23547831),首先看一下界面效果

首先你要引入三方库文件

compile 'cn.yipianfengye.android:zxing-library:2.2'

Java代码如下

             case R.id.button3://定制化二维码扫描模式
                intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivityForResult(intent, REQUEST_CODE);
                break;

这里的SecondActivity就是自定义的Activity

只是比之前的CaptureActivity多了一行代码

        CodeUtils.setFragmentArgs(captureFragment, R.layout.my_camera);
这里的布局文件中引用了一个自定义的控件ViewFinerView,他是继承自View的

<declare-styleable name="ViewfinderView">
        <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="boolean" />
    </declare-styleable>


从上面的图片中你看到的所有矩形(包括上下左右的黑框和扫描的四个角的八个矩形)都是通过Canvas.drawRect(left,top,right,bottom)方法绘制的

/**
 * 自定义组件实现,扫描功能
 */
public final class ViewfinderView extends View {

    private static final long ANIMATION_DELAY = 100L;
    private static final int OPAQUE = 0xFF;

    private final Paint paint;
    private Bitmap resultBitmap;
    private final int maskColor;
    private final int resultColor;
    private final int resultPointColor;
    private Collection<ResultPoint> possibleResultPoints;
    private Collection<ResultPoint> lastPossibleResultPoints;
    // 扫描线移动的y
    private int scanLineTop;
    // 扫描线移动速度
    private int SCAN_VELOCITY;
    // 扫描线
    private Bitmap scanLight;
    // 是否展示小圆点
    private boolean isCircle;

    public ViewfinderView(Context context) {
        this(context, null);
    }

    public ViewfinderView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);

    }

    public ViewfinderView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
        Resources resources = getResources();
        maskColor = resources.getColor(R.color.viewfinder_mask);
        resultColor = resources.getColor(R.color.result_view);
        resultPointColor = resources.getColor(R.color.possible_result_points);
        possibleResultPoints = new HashSet<>(5);

        scanLight = BitmapFactory.decodeResource(resources,
                R.drawable.scan_light);

        initInnerRect(context, attrs);
    }

    /**
     * 初始化内部框的大小
     *
     * @param context
     * @param attrs
     */
    private void initInnerRect(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView);
        // 扫描框距离顶部
        float innerMarginTop = ta.getDimension(R.styleable.ViewfinderView_inner_margintop, -1);
        if (innerMarginTop != -1) {
            CameraManager.FRAME_MARGINTOP = (int) innerMarginTop;
        }

        // 扫描框的宽度
        CameraManager.FRAME_WIDTH = (int) ta.getDimension(R.styleable.ViewfinderView_inner_width, DisplayUtil.screenWidthPx / 2);

        // 扫描框的高度
        CameraManager.FRAME_HEIGHT = (int) ta.getDimension(R.styleable.ViewfinderView_inner_height, DisplayUtil.screenWidthPx / 2);

        // 扫描框边角颜色
        innercornercolor = ta.getColor(R.styleable.ViewfinderView_inner_corner_color, Color.parseColor("#45DDDD"));
        // 扫描框边角长度
        innercornerlength = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_length, 65);
        // 扫描框边角宽度
        innercornerwidth = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_width, 15);

        // 扫描bitmap
        Drawable drawable = ta.getDrawable(R.styleable.ViewfinderView_inner_scan_bitmap);
        if (drawable != null) {

        }
        // 扫描控件
        scanLight = BitmapFactory.decodeResource(getResources(), ta.getResourceId(R.styleable.ViewfinderView_inner_scan_bitmap, R.drawable.scan_light));
        // 扫描速度
        SCAN_VELOCITY = ta.getInt(R.styleable.ViewfinderView_inner_scan_speed, 5);

        isCircle = ta.getBoolean(R.styleable.ViewfinderView_inner_scan_iscircle, true);

        ta.recycle();
    }

    @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);

        if (resultBitmap != null) {
            // Draw the opaque result bitmap over the scanning rectangle
            paint.setAlpha(OPAQUE);
            canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
        } else {

            drawFrameBounds(canvas, frame);

            drawScanLight(canvas, frame);

            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);

                if (isCircle) {
                    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);

                if (isCircle) {
                    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);
        }
    }
    // 扫描框边角颜色
    private int innercornercolor;
    // 扫描框边角长度
    private int innercornerlength;
    // 扫描框边角宽度
    private int innercornerwidth;

    /**
     * 绘制取景框边框
     *
     * @param canvas
     * @param frame
     */
    private void drawFrameBounds(Canvas canvas, Rect frame) {

        /*paint.setColor(Color.WHITE);
        paint.setStrokeWidth(2);
        paint.setStyle(Paint.Style.STROKE);

        canvas.drawRect(frame, paint);*/

        paint.setColor(innercornercolor);
        paint.setStyle(Paint.Style.FILL);

        int corWidth = innercornerwidth;
        int corLength = innercornerlength;

        // 左上角
        canvas.drawRect(frame.left, frame.top, frame.left + corWidth, frame.top
                + corLength, paint);
        canvas.drawRect(frame.left, frame.top, frame.left
                + corLength, frame.top + corWidth, paint);
        // 右上角
        canvas.drawRect(frame.right - corWidth, frame.top, frame.right,
                frame.top + corLength, paint);
        canvas.drawRect(frame.right - corLength, frame.top,
                frame.right, frame.top + corWidth, paint);
        // 左下角
        canvas.drawRect(frame.left, frame.bottom - corLength,
                frame.left + corWidth, frame.bottom, paint);
        canvas.drawRect(frame.left, frame.bottom - corWidth, frame.left
                + corLength, frame.bottom, paint);
        // 右下角
        canvas.drawRect(frame.right - corWidth, frame.bottom - corLength,
                frame.right, frame.bottom, paint);
        canvas.drawRect(frame.right - corLength, frame.bottom - corWidth,
                frame.right, frame.bottom, paint);
    }


    /**
     * 绘制移动扫描线
     *
     * @param canvas
     * @param frame
     */
    private void drawScanLight(Canvas canvas, Rect frame) {

        if (scanLineTop == 0) {
            scanLineTop = frame.top;
        }

        if (scanLineTop >= frame.bottom - 30) {
            scanLineTop = frame.top;
        } else {
            scanLineTop += SCAN_VELOCITY;
        }
        Rect scanRect = new Rect(frame.left, scanLineTop, frame.right,
                scanLineTop + 30);
        canvas.drawBitmap(scanLight, null, scanRect, paint);
    }
    public void drawViewfinder() {
        resultBitmap = null;
        invalidate();
    }

    public void addPossibleResultPoint(ResultPoint point) {
        possibleResultPoints.add(point);
    }


    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}
用户可以根据自己的需求进行适当的修改颜色和文字等内容
最后附上github地址 https://github.com/yipianfengye/android-zxingLibrary

二维码现在越来越火爆,使用的越发广泛,对于二维码二维码的生成与解析,有多种途径,这里我选择用google老大的ZXing。    其中刘超大神使用ZXing写了一个快速集成二维码扫描的工具类,实现其核心的实现扫描的功能。使用时通过调用二维码工具类,几行代码就可以实现二维码扫描的功能! 一、实现对二维码的扫描 使用步骤: 1.在module的build.gradle中dependencies添加 compile 'cn.yipianfengye.android:zxing-library:2.1' 2.在Application中执行初始化操作 提示:在清单文件中注册Application public class MyApplication extends Application{ @Override public void onCreate() { super.onCreate(); //初始化二维码工具类 ZXingLibrary.initDisplayOpinion(this); } } 3.在清单文件配置权限(安卓6.0以上的手机还要额外的权限配置)。 <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" /> 4.在代码中执行打开扫描二维码界面操作  Intent intent = new Intent(MainActivity.this, CaptureActivity.class); startActivityForResult(intent, REQUEST_CODE); 这里的REQUEST_CODE是我们定义的int型常量,这里设置为5,为了方便接受onActivityResult分别进行处理。 5.在Activity的onActivityResult方法中接收扫描结果(下面有完整代码) protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 5) { 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(); } } } } 实现效果:(由于模拟器没有摄像头不能扫描二维码,手机录屏需要root,所以就截取了效果图) 第一张是进行二维码扫描,第二张是当扫描结束,显示信息。 只用区区几行代码,就可以实现二维码的快速扫描。 二、定制化显示扫描UI 1.在values的colors文件下添加颜色,决定了二维码四个边框的颜色 <color name="scan_corner_color">#0effc2</color>2. 2.在drawable文件下添加一个scan_image.png的图片,就是二维码扫描的那条横线 3.创建一个新的Activity(demo里叫 SecondActivity )集成FragmentActivity,在清单文件里进行配置。 4. 修改新的Activity布局文件,是二维码的背景布局 <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:layout_gravity="bottom|center_horizontal" android:layout_marginBottom="10dp" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_marginTop="20dp" android:text="取消二维码扫描" /> <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效果等。 5. 创建my_camera.xml布局文件,这个就是扫描二维码的界面 要自定义二维码扫描页面,就在这个布局里进行修改,这里我加了一个ToolBar,一个Button按钮 用来退出扫描。 <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" /> <android.support.v7.widget.Toolbar android:background="#FF0000" android:layout_width="match_parent" android:layout_height="100dp"> </android.support.v7.widget.Toolbar> <com.uuzuche.lib_zxing.view.ViewfinderView android:id="@+id/viewfinder_view" android:layout_width="wrap_content" android:layout_height="wrap_content" app:inner_corner_color="@color/scan_corner_color" app:inner_corner_length="30dp" app:inner_corner_width="5dp" app:inner_height="200dp" app:inner_margintop="150dp" app:inner_scan_bitmap="@drawable/scan_image" app:inner_scan_iscircle="false" app:inner_scan_speed="10" app:inner_width="200dp" /> </FrameLayout> 6. 二维码解析回调函数,代码放到SecondActivity 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(); } }; 7.在Activity中执行Fragment的初始化操作 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); initView(); //在Activity中执行Fragment的初始化操作 //执行扫面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(); } 8.使用Activity里定义的Button按钮,完成用户通过点击,退出二维码扫描界面的操作 //点击退出按钮就退出扫描二维码的界面 @Override public void onClick(View v) { switch (v.getId()) { case R.id.second_button1: finish(); break; } } 效果图: 完整代码(布局只有两个Button): MainActivity: /*使用二维码工具类步骤: * 1. 在module的build.gradle中dependencies添加:compile 'cn.yipianfengye.android:zxing-library:2.1' * 2. 在Application中执行二维码工具类的初始化操作 ZXingLibrary.initDisplayOpinion(this); * 3. 在清单文件配置权限和自定义的APPlication,安卓6.0的手机还要做额外的权限配置,方有效. * 3. 更加点击事件调用开启扫描二维码界面操作 * 4. 在自动接收跳转页面传过来的数据onActivityResult里接收扫描结果,并对结果进行处理 * */ public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button ZXing; private Button ZXingOK; private Button ZXingOK2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { ZXing = (Button) findViewById(R.id.ZXing); ZXingOK = (Button) findViewById(R.id.ZXingOK); ZXing.setOnClickListener(this); ZXingOK.setOnClickListener(this); ZXingOK2 = (Button) findViewById(R.id.ZXingOK2); ZXingOK2.setOnClickListener(this); } //根据点击事件开启二维码扫描,这里的5是为了方便接受onActivityResult分别进行处理 @Override public void onClick(View v) { switch (v.getId()) { case R.id.ZXing: Intent intent = new Intent(MainActivity.this, CaptureActivity.class); startActivityForResult(intent, 5); break; case R.id.ZXingOK: Intent intent1 = new Intent(MainActivity.this, SecondActivity.class); startActivityForResult(intent1, 7); break; case R.id.ZXingOK2: Intent intent2 = new Intent(MainActivity.this, Main2Activity.class); startActivity(intent2); break; } } //在自动接收跳转页面传过来的数据OnActivityResult里接收扫描结果,并对结果进行处理 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { //处理二维码扫描的结果,5就是对在startActivityForResult里设置的标识进行判断,执行对应的逻辑 if (requestCode == 5) { //处理扫描结果(在界面上显示) 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(); } } } if (requestCode == 7) { 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(); } } } } } SecondActivity: /* 定制化显示扫描界面 * 1.在values的colors文件下添加颜色,决定了二维码四个边框的颜色 * 2.在drawable文件下添加一个scan_image.png的图片,决定了二维码扫描的那条横线的颜色 * 3.创建一个新的Activity(demo里叫 SecondActivity )集成FragmentActivity,在清单文件里进行配置. * 4.修改新的Activity布局文件,是二维码的背景布局 * 5.创建my_camera.xml布局文件,这个就是扫描二维码的界面 * 6.二维码解析回调函数,代码放到这个Activity即可(SecondActivity就在这个Activity名字) * 7.在Activity中执行Fragment的初始化操作 * 8.使用Activity里定义的Button按钮,完成用户通过点击,退出二维码扫描界面的操作. * 9.使用这个我们自定义的二维码扫描界面和使用默认的界面类似,只需替换CaptureActivity即可 */ public class SecondActivity extends AppCompatActivity implements View.OnClickListener { private Button second_button1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); initView(); //在Activity中执行Fragment的初始化操作 //执行扫面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(); } private void initView() { second_button1 = (Button) findViewById(R.id.second_button1); second_button1.setOnClickListener(this); } //点击退出按钮就退出扫描二维码的界面 @Override public void onClick(View v) { switch (v.getId()) { case R.id.second_button1: finish(); break; } } //二维码解析回调函数,代码放到这个Activity即可(SecondActivity就在这个Activity名字) 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(); } }; } 三、生成二维码 只需要在点击事件中加入相应执行代码,即可完成效果: @Override public void onClick(View v) { switch (v.getId()) { //生成不带logo的二维码图片 case R.id.btnStart: String textContent = edShow.getText().toString(); if (TextUtils.isEmpty(textContent)) { Toast.makeText(Main2Activity.this, "您的输入为空!", Toast.LENGTH_SHORT).show(); return; } edShow.setText(""); Bitmap image = CodeUtils.createImage(textContent, 400, 400, null); imgShow.setImageBitmap(image); break; //生成带logo的二维码图片 case R.id.btnStart2: String textContent1 = edShow.getText().toString(); if (TextUtils.isEmpty(textContent1)) { Toast.makeText(Main2Activity.this, "您的输入为空!", Toast.LENGTH_SHORT).show(); return; } edShow.setText(""); Bitmap image1 = CodeUtils.createImage(textContent1, 400, 400, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)); imgShow.setImageBitmap(image1); break; } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值