autojs 串口通信 替代无障碍 串口

欢迎使用CH34x系列usb串口-autojs支持库

  • 支持类型 串口为控制端,蓝牙为被控端的CH34x串口

目录

一、流程图

1.引入require
2.实例化
3.加载usb loadUsb
4.动态申请Usb串口权限
5.动态权限-用户点击同意
5.动态权限-用户点击拒绝
重新申请
6.进行设备连接
7.实例化
7.实例化
App
UsbModel模块
UsbDevice类
同意
拒绝
失败
TouchEvent
KeyBoardEvent
touch方法,按下坐标
unTouch方法,抬起坐标
KeyDown方法,按下键盘
KeyUp方法,抬起键盘

二、UsbModel.js帮助文档

1.引入串口模块

  • 使用Autojs常用的加载自定义模块的方式加载即可,尽量模块的加载使用单实例原则,否则多次连接串口可能会抛出未知的异常。

参数

  • {String} 路径,在项目的/model下的UsbModel.js文件

返回值

  • {*} usb串口模块的声明,但为被实例化
	//加载usb串口模块
	var UsbModel = require("./model/UsbModel");

2.实例化模块

参数

  • {android.content.Context} context 上下文

返回值

  • {UsbModel} UsbModel 实例化的串口模块对象
	//实例化串口模块
	this.UsbModel = new UsbModel(context);

3.判断设备是否支持外接串口操作

参数

返回值

  • {Boolean} : true 支持;false 不支持
	//判断设备是否支持外接串口操作
	this.UsbModel.isSupportOtg();

4.加载usb串口,并进行动态申请权限

参数

  • {Function} fun_NoDevice 未插入任何设备的回调方法
  • {Function} fun_NoneSupportDevice 在有连接usb设备而无可支持的设备时执行的回调方法
  • {Function} fun_onDeviceConnect 在设备连接时的回调方法,参数为android.hardware.usb.UsbDevice(用5中的connect进行设备连接)
  • {Function} fun_AfterAgree 在点击同意后执行的回调方法
  • {Function} fun_Aftercancle 在点击拒绝后执行的回调方法

返回值

    //加载usb设备并进行connect操作
    this.UsbModel.loadUsb(fun_NoDevice, fun_NoneSupportDevice, fun_onDeviceConnect, fun_AfterAgree, fun_Aftercancle);

5.对指定设备进行尝试连接、动态申请权限和串口设备初始化操作

注意:

  • 连接失败会抛出异常,需要进行捕获并处理
  • 在4中的fun_onDeviceConnect回调中使用该方法

参数

  • {android.hardware.usb.UsbDevice} device 需要连接的设备

返回值

	//连接设备,并进行数据对接
	this.UsbModel.connect(device);

6.动态申请权限

注意:

  • 此方法不需要进行使用,在5中的connect方法内部已经进行了调用。如需进行二次开发,请使用此方法来动态申请权限

参数

  • {android.hardware.usb.UsbDevice} device 需要连接的设备
  • {Function} fun_onDeviceConnect 在设备连接时的回调方法
  • {Function} fun_AfterAgree 在点击同意后执行的回调方法
  • {Function} fun_Aftercancle 在点击拒绝后执行的回调方法

返回值

	//动态申请权限,并在按下同意/拒绝、设备连接时进行回调的执行
	this.UsbModel.accessPermission(device, fun_onDeviceConnect, fun_AfterAgree, fun_Aftercancle);

7.初始化设备

注意:

  • 此方法不需要进行使用,在5中的connect中已经进行了调用。如需进行二次开发,请使用此方法来动态申请权限

参数

返回值

  • {Boolean}: true 初始化成功;false 初始化失败
	//初始化设备
	this.UsbModel.UartInit();

8.发送二进制数据给usb串口设备

注意:

  • 此方法不需要进行使用,在后续的各种指令操纵中已经进行了调用。如需进行二次开发,请使用此方法并结合DataCreater.jar包内方法来动态申请权限。详见DataCreater.jar包帮助文档

参数

  • {Array} data 字节数组的数据
  • {Number} dataLength 字节数组的长度
  • {Number} timeout 超时时间

返回值

  • {Boolean} :执行结果 true 成功;false 失败
	//创建字节数组
	var data = util.java.array('byte', 1024);
	//发送数据给usb设备
	this.UsbModel.WriteData(data, data.length,10000);

9.断开连接

注意:

  • app退出前必须调用此方法,否则,再次打开app,串口会被占用,而导致触摸指令、键盘指令等执行失败
	//进行连接的断开操作
    this.UsbModel.disConnect();

10.判断设备是否是可支持的usb设备

  • 本模块已预设可支持的usb设备列表,如需进行扩展,可在模块内进行补充
  • 无需调用本方法,本方法已在4中方法内进行了调用,如需二次开发,请用本方法进行设备过滤

参数

  • {android.hardware.usb.UsbDevice} device 需要连接的设备

返回值

  • {Boolean} :是否支持 true 支持;false 不支持
	//判断该设备是否是可支持的设备
	this.UsbModel.isCheckUsb(device);

11.注册屏幕旋转

  • 串口模块的坐标无论屏幕如何旋转都是以手机正放的左上角为原点,而android的标准是当前屏幕位置的左上角。
  • DataCreater.jar包内进行了坐标的转换,但是需要在屏幕旋转时,设备屏幕朝向,所以需要对屏幕旋转进行监听,以动态化设备屏幕朝向,完美解决该冲突
  • 但是需要先进行TouchEvent对象的实例化,否则无法进行DataCreater.jar包内屏幕坐标转换器的屏幕朝向的注入
  • 此方法不需要进行调用,已在12中实例化TouchEvent时,进行了调用,如需二次开发,请使用本方法进行监听
	//注册屏幕旋转,并动态化设置屏幕朝向,完成坐标转化
	this.UsbModel.onScreenRotationListener();

12.实例化TouchEvent并获取该对象的引用

  • 在调用点击类操作时,务必使用本方法,对该对象实例化,否则会抛出异常
  • 本方法不仅可以实例化对象,还可以返回该对象的引用,在第二次调用该方法时,只返回引用而不再次实例化

参数

  • {Function} fun_log(point, eventType, command) 输出日志的回调方法,为null则不进行日志的处理(坐标,事件类型,命令报文)

返回值

  • {com.msyq.touch.TouchEvent} TouchEvent对象(详见DataCreater.jar包开发文档)
	// 实例化TouchEvent对象,并返回该对象引用
	 this.UsbModel.getTouchEvent((point, type, command) => {
      	log("触摸了:" + point +";事件类型:"+ type + ";命令:" + command);
     });

13.获取一个未被占用的触摸id

返回值

  • {Number} touchId 触摸id
	//获取一个未被占用的触摸id
	this.UsbModel.getFreeTouchId();

14.判断触摸id是否被占用

参数

  • {Number} touchId 触摸id

返回值

  • {Boolean} :是否被占用 true 被占用;false未被占用
	//判断触摸id是否被占用
	this.UsbModel.isFreeTouchId(touchId);

15.按下坐标,但不释放

  • 按下坐标,但不释放。需要调用unTouch(touchId)进行释放。
  • 当多次调用本方法而不释放的效果为滑动
  • 触摸id如果为null或大于255,或小于0 ,则会调用13中的方法getFreeTouchId(),申请一个未被占用的触摸id
  • 多次调用本方法,并传入不同的触摸id,效果为多点触控
  • 据android规定,最多只能同时按下是个坐标,如果超过10个坐标且未被释放,会抛出异常

参数

  • {Number} x 坐标x
  • {Number} y 坐标y
  • {Number} id 触摸id

返回值

  • {Boolean} 执行结果 true 执行成功;false 执行失败
	//方法一
	//滑动展示
	this.UsbModel.touch(200, 300, 0);
	this.UsbModel.touch(300, 300, 0);
	//仅释放触摸id 0
	this.UsbModel.unTouch(0);
	
	//方法二
	//点击坐标展示
	//按下(200,300),并自动分配触摸id
	this.UsbModel.touch(200, 300);
	//抬起所有的触摸id
	this.UsbModel.unTouch(0);

16.按下坐标并指定按下时长

参数

  • @param {Number} x 坐标的x
  • @param {Number} y 坐标的y
  • @param {Number} duration 等待时长

返回值

	//按下坐标并指定按下时长
	//按下(200,300)的坐标3s(3s = 3000ms)
	this.UsbModel.press(200, 300, 3000);

17.点击坐标

参数

  • {Number} x 坐标的x
  • {Number} y 坐标的y

返回值

	//点击指定的坐标
	//点击(200,300)的坐标
	this.UsbModel.click(200, 300);

18.长按坐标

参数

  • {Number} x 坐标的x
  • {Number} y 坐标的y

返回值

	//长按特定的坐标
	//长按(200,300)的坐标
	this.UsbModel.longClick(200, 300);

19.两点间滑动

参数

  • {Number} x1 坐标1的x
  • {Number} y1 坐标1的y
  • {Number} x2 坐标2的x
  • {Number} y2 坐标2的y
  • {Number} duration 执行时长
  • {Boolean} isUnTouch 执行完后是否松手(可选参数,默认为true)
  • {Number} touchId 触摸id(可选参数,默认为0)

返回值

	//方法一
	//从(200,300)滑动到(400,500)
	this.UsbModel.slide(200, 300, 400, 500, 1000, true, 0);
	
	//方法二
	//从(200,300)滑动到(400,500)
	this.UsbModel.slide(200, 300, 400, 500, 1000);

20.模拟手势操作

  • 例如gesture(1000, [0, 0], [500, 500], [500, 1000])为模拟一个从(0, 0)到(500, 500)到(500, 100)的手势操作,时长为2秒。
  • 使用slide()函数也可以模拟手势,但是无法进行两点之间的点位补全。也就是说,slide()只能进行预设点位的滑动,gesture()滑动的更真实。

参数

  • {number} duration {number} 手势的时长
  • {[x , y],[x , y]…} [x , y]手势滑动路径的一系列坐标

返回值

	//从(500,500)经过(400,300)滑动到(800,800)
	this.UsbModel.gesture(800, [500, 500], [400, 300], [800, 800]);

21.同时模拟多个手势,多点触控

参数

  • gestures([delay1, duration1, [x1, y1], [x2, y2], …], [delay2, duration2, [x3, y3], [x4, y4], …], …)

返回值

	// 手指捏合
	obj.UsbModel.gestures([3000, [800, 300], [500, 1000]],[5000, [300, 1500], [500, 1000]]);

22.抬起手指

参数

  • {Number} id 需要抬起的触摸id(可选参数)为空则抬起所有的触摸点位

返回值

  • {Boolean} : 执行结果 true执行成功;false 执行失败
	//抬起触摸id为0的触摸
	this.UsbModel.unTouch(0);
	
	//抬起所有的触摸
	this.UsbModel.unTouch();

23.校验是否是ui线程,是则抛出异常

  • 本方法用于检验当前线程是否是UI线程,以进行坐标的阻塞操作
  • 本方法无返回值,但是如果是UI线程,则会抛出异常
  • 本方法无需调用,且已在需要进行阻塞的坐标操控方法中进行了调用。

参数

  • {String} 抛出的异常信息

返回值

	// An highlighted block
	this.UsbModel.checkNotUiThread("当前是UI线程,无法进行阻塞操作,请在多线程内尝试!");

24.将16进制命令转为字节数组

参数

  • {String} command 需要转换成二进制指令的16进制字符串

返回值

  • {byte[] } 字节数组形式的报文命令
	//得到按下键盘A的指令
	var data = this.UsbModel.getBtyeCommandFromHash("2c 08 45 41 00 00 04 00 00 00 00 00");
	
	//执行指令
	this.UsbModel.WriteData(data, data.length,10000);

25.获取KeyBoardEvent(键盘指令事件处理器)

  • 实例化KeyBoardEvent,以使UsbModel具有操作键盘的能力
  • 键码可通过com.msyq.keyboard.KeyBoard的静态字段来获取
  • 在使用键盘的操作命令前,请务必调用该方法来实例化对象,否则会抛出异常
  • 在第二次调用该方法时,如果对象未被销毁,则不会再次实例化对象,只进行对象引用的返回

参数

  • {Function} fun_log 输出日志的回调方法,为null则不进行日志的处理

返回值

  • {com.msyq.keyboard.KeyBoardEvent} KeyBoardEvent 对象的引用
	//为串口模板实例化KeyBoardEvent类
    this.UsbModel.getKeyBoardEvent((point, type, command) => {
        log("键盘命令:" + command);
    });

26.按下键盘的键码- 但不释放(只按下不抬起)

参数

  • {Number} keyNum 键码 - 所有的键码可参照com.msyq.keyboard.KeyBoard的声明的键码来获取

返回值

  • {Boolean} :执行结果 true 执行成功;false 执行失败
	//喊话功能
	
	//设置粘贴板
	setClip("这里是Ch34x串口测试");
	
    //使用ctrl+V进行粘贴
    //按下左Ctrl
	this.UsbModel.keyDown(com.msyq.keyboard.KeyBoard.Keyboard_LeftControl);
	//按下V
    this.UsbModel.keyDown(com.msyq.keyboard.KeyBoard.Keyboard_V);
    
    //松开ctrl+v
    this.UsbModel.keyUp(com.msyq.keyboard.KeyBoard.Keyboard_LeftControl, com.msyq.keyboard.KeyBoard.Keyboard_V);

27.抬起指定的键位

参数

  • {Number} keyNum 可变参数-键码(为空则抬起所有的键盘)

返回值

  • {Boolean} :执行结果 true 执行成功;false 执行失败
	//抬起所有的按键
	this.UsbModel.keyUp();

	//抬起指定的按键
	this.UsbModel.keyUp(com.msyq.keyboard.KeyBoard.Keyboard_LeftControl);
	
	//同时抬起多个组合键
	this.UsbModel.keyUp(com.msyq.keyboard.KeyBoard.Keyboard_LeftControl, com.msyq.keyboard.KeyBoard.Keyboard_V);

三、DataCreater.jar帮助文档

jar内为各种指令生成器转换器,分别为:

  • TouchEvent (屏幕触摸坐标事件生成器)
  • TouchPointConverter (android坐标与硬件串口坐标转换器)
  • Multimedia (多媒体事件生成器)
  • KeyBoardEvent(键盘事件生成器)
  • MouseEvent (鼠标事件生成器)

1.TouchEvent 屏幕触摸坐标事件生成器

(1).使用屏幕分辨率来实例化对象

参数

  • {int} screenWidth 屏幕的宽
  • {int} screenHeight 屏幕的高

返回值

  • {TouchEvent} 屏幕触摸坐标事件生成器对象
// 初始化一个屏幕宽960,高为1024分辨率的屏幕
TouchEvent touchEvent = new TouchEvent(960,1024);
(2).使用分辨率+屏幕朝向来实例化对象

参数

  • {int} screenWidth 屏幕的宽
  • {int} screenHeight 屏幕的高
  • {int} screenOrientation 屏幕朝向
    共有四个取值:
    TouchEvent.SCREEN_HOME_BUTTOM   home在屏幕下方
    TouchEvent.SCREEN_HOME_LEFT   home在屏幕左侧
    TouchEvent.SCREEN_HOME_RIGHT   home在屏幕右侧
    TouchEvent.SCREEN_HOME_TOP   home在屏幕顶部

返回值

  • {TouchEvent} 屏幕触摸坐标事件生成器对象
// 初始化一个屏幕宽960,高为1024分辨率的屏幕,且朝向为home在下
TouchEvent touchEvent = new TouchEvent(960,1024,TouchEvent.SCREEN_HOME_BUTTOM);
(3).setScreenOrientation 设置屏幕朝向
  • 说明:串口的屏幕朝向是始终以正放屏幕的左上角为原点,无论无何旋转屏幕,原点不变,所以在android和autojs中时,需要监听屏幕旋转,并且动态修改屏幕朝向,以保证生成的坐标正确。

参数

  • {int} screenOrientation 屏幕朝向
    共有四个取值:
    TouchEvent.SCREEN_HOME_BUTTOM   home在屏幕下方
    TouchEvent.SCREEN_HOME_LEFT   home在屏幕左侧
    TouchEvent.SCREEN_HOME_RIGHT   home在屏幕右侧
    TouchEvent.SCREEN_HOME_TOP   home在屏幕顶部

返回值

//设定屏幕的朝向正常
touchEvent.setScreenOrientation(TouchEvent.SCREEN_HOME_LEFT);
(4).setMsgLog 设定日志处理器
  • 说明:日志处理器用于实时输出产生的日志信息,如有需要,请调用本方法,否则日志处理器不生效

参数

  • {MsgLog<Point>} MsgLog 实现MsgLog日志处理器接口的类

返回值

//设定日志处理器,需要实现MsgLog接口
touchEvent.setMsgLog(new MsgLog<Point>() {
    @Override
    public void commandLog(Point point, String eventType, String command) {
        System.out.println("坐标:"+point+";类型:"+eventType+";指令"+command);
    }
});
(5).setScreenSize 重设屏幕尺寸

参数

  • {int} screenWidth 屏幕的宽
  • {int} screenHeight 屏幕的高

返回值

//重设屏幕尺寸为960,1024
touchEvent.setScreenSize(960,1024);
(6).touchPoint 使用指定id,按下指定坐标

说明:

  • 触摸id为使用者任意指定的0-255之间的整数,任意且在释放前唯一
  • 返回值为字节数组,如需字符串数组,可以使用日志处理器查看
  • 生成的指令为按下但不松手
  • 手机产生滑动的效果方法:使用该方法后不释放,再次按下别的位置,即可产生滑动效果
  • 最大的触摸个数为10,若未释放的个数为10,再次调用本方法则会抛出CH34xUARTException异常

参数

  • {Point} 触摸的坐标
  • {Integer} touchId 按下的手指序列(0-255)

返回值

  • {byte[ ]} 指令的二进制数组
//触摸坐标(100,200),并绑定到触摸id 1上
byte[] bytes = touchEvent.touchPoint(new Point(100, 200), 1);
(7).unTouch 释放单个触摸id

参数

  • {int} touchId 释放的触摸id

返回值

  • {byte[ ]} 指令的二进制数组
//释放触摸id 1的按下状态
byte[] bytes = touchEvent.unTouch(1);
(8).unTouch 释放所有触摸id

参数

返回值

  • {byte[ ][ ]} 指令的二进制的二维数组
//释放所有的触摸id
byte[][] bytes = touchEvent.unTouch();
(9).isFreeId 判断触摸id是否被占用

参数

  • {int} id 触摸id

返回值

  • {boolean} 是否暂用
//判断触摸id 1是否占用
boolean freeId = touchEvent.isFreeId(1);
(10).getFreeTouchId获取一个未被占用的触摸id

参数

返回值

  • {int} 未被占用的触摸id
//获取一个未被占用的触摸id
int freeTouchId = touchEvent.getFreeTouchId();

2.KeyBoardEvent键盘事件生成器

(1).实例化对象

参数

返回值

  • {KeyBoardEvent} 键盘事件生成器对象
//实例化KeyBoardEvent对象
KeyBoardEvent keyBoardEvent = new KeyBoardEvent();
(2).setMsgLog 设定日志处理器
  • 说明:日志处理器用于实时输出产生的日志信息,如有需要,请调用本方法,否则日志处理器不生效
    参数
  • {MsgLog} MsgLog 实现MsgLog日志处理器接口的类

返回值

//设定键盘事件生成器的日志处理器
keyBoardEvent.setMsgLog(new MsgLog() {
   @Override
    public void commandLog(Object o, String eventType, String command) {
        System.out.println("命令:"+command);
    }
});
(3).keyDown 按下键盘键位

说明:

  • 生成的指令为按下但不松手

参数

  • {int} keyBoard 键盘键码
    所有的可用键码在类com.msyq.keyboard.KeyBoard中 不建议直接传入数字

返回值

  • {byte[ ]} 二进制命令
//按下键盘A
byte[] bytes = keyBoardEvent.keyDown(KeyBoard.Keyboard_A);
(4).keyUp 释放单个/多个键盘键位

参数

  • {int…} keyBoard 键盘键码,可变参数,可以传入单个/多个键位键码
    所有的可用键码在类com.msyq.keyboard.KeyBoard中 不建议直接传入数字

返回值

  • {byte[ ]} 二进制命令
//释放键位 A + 左ctrl
byte[] bytes = keyBoardEvent.keyUp(KeyBoard.Keyboard_A, KeyBoard.Keyboard_LeftControl);
(5).keyUp 释放所有的键盘键码

参数

返回值

  • {byte[ ]} 二进制命令
//释放所有的键盘键码
byte[] bytes = keyBoardEvent.keyUp();

3.Multimedia多媒体事件生成器

(1).实例化对象

参数

返回值

  • {Multimedia} 多媒体事件生成器对象
//实例化多媒体事件生成器对象
Multimedia multimedia = new Multimedia();
(2).setMsgLog 设定日志处理器
  • 说明:日志处理器用于实时输出产生的日志信息,如有需要,请调用本方法,否则日志处理器不生效

参数

  • {MsgLog} MsgLog 实现MsgLog日志处理器接口的类

返回值

//设置Multimedia的日志处理器
multimedia.setMsgLog(new MsgLog<String>() {
    @Override
    public void commandLog(String name, String eventType, String command) {
        System.out.println("操作:"+name+";类型:"+eventType+";指令:"+command);
    }
});
(3).androidBack 松开/按下返回键

参数

  • {boolean} isDown 按键状态是否是按下

返回值

  • {byte[ ]} 二进制命令
//按下返回键
byte[] bytes = multimedia.androidBack(true);
(4).androidHome 松开/按下主页键

参数

  • {boolean} isDown 按键状态是否是按下

返回值

  • {byte[ ]} 二进制命令
//按下主页键
byte[] bytes = multimedia.androidHome(true);
(5).androidMenu 松开/按下菜单键

参数

  • {boolean} isDown 按键状态是否是按下

返回值

  • {byte[ ]} 二进制命令
//按下菜单键
byte[] bytes = multimedia.androidMenu(true);
(6).androidPower 按下并松开电源键

说明:

  • 该方法是短按电源键,因串口未提供相关api,故无法实现关机

参数

返回值

  • {byte[ ]} 二进制命令
//按下并松开电源键
byte[] bytes = multimedia.androidPower();
(7).androidVolumeDown 松开/按下音量-

参数

  • {boolean} isDown 按键状态是否是按下

返回值

  • {byte[ ]} 二进制命令
//按下音量-
byte[] bytes = multimedia.androidVolumeDown(true);
(8).androidVolumeUp 松开/按下音量+

参数

  • {boolean} isDown 按键状态是否是按下

返回值

  • {byte[ ]} 二进制命令
//按下音量+
byte[] bytes = multimedia.androidVolumeUp(true);
(9).androidVolumeNone 按下并松开静音键

说明:

  • 该方法是短按静音键,因串口未提供相关api,故无法实现长按静音键

参数

返回值

  • {byte[ ]} 二进制命令
//按下并松开静音键
byte[] bytes = multimedia.androidVolumeNone();
(10).lastSong 松开/按下上一首

参数

  • {boolean} isDown 按键状态是否是按下

返回值

  • {byte[ ]} 二进制命令
//按下上一首
byte[] bytes = multimedia.lastSong(true);
(11).nextSong 松开/按下下一首

参数

  • {boolean} isDown 按键状态是否是按下

返回值

  • {byte[ ]} 二进制命令
//按下下一首
byte[] bytes = multimedia.nextSong(true);
(12).changeSongStatus 松开/按下改变音乐播放状态

说明:

  • 当前为播放,按下后变为暂停
  • 当前为暂停,按下后变为播放
    参数
  • {boolean} isDown 按键状态是否是按下

返回值

  • {byte[ ]} 二进制命令
//按下改变音乐播放状态
byte[] bytes = multimedia.changeSongStatus(true);

4.MouseEvent鼠标事件生成器

  • 因大部分手机不支持此方式的点击,暂未完成该部分的代码

5.CH34xUARTException异常类

说明 :可以在调用本jar时,捕获此异常。抛出异常的时机包括但不仅限于:

  • TouchEvent.touchPoint时,已有10个触摸id
  • TouchEvent.getFreeTouchId时,已有10个触摸id
  • TouchEvent.isFreeId时,已有10个触摸id
  • TouchPointConverter设置屏幕方向错误
  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值