29、phonegap入门

 

0. PhoneGap介绍

0.1  什么是PhoneGap?

PhoneGap是一个基于HTML、CSS、JS创建跨平台移动应程序的快速开发平台。与传统Web应用不同的是,它使开发者能够利用iPhone、Android等职能手机的核心本地功能,比如GPS、传感器、震动。

 

 

 

0. PhoneGap介绍

0.1  什么是PhoneGap?

PhoneGap是一个基于HTML、CSS、JS创建跨平台移动应程序的快速开发平台。与传统Web应用不同的是,它使开发者能够利用iPhone、Android等职能手机的核心本地功能,比如GPS、传感器、震动。

 

0.2  PhoneGap 历史

0.2.1 产生:08年、hackathon

在苹果08年举行的iOSDevCamp(黑客马拉松)中,一名iOS程序员无法忍受OC的语法,觉着简单的HTML和JavaScript如果能部署在移动端并能实现同iphone实现简单的交互(比如调用摄像头、重力感应)该多好,于是phonegap便产生了!

0.2.2 流行:流行、获奖、支持7个平台

09年,PhoneGap 0.6版本发布,这是第一个稳定版,不仅支持iOS,还支持Android、BlackBerry平台,这使得PhoneGap在移动开发领域越来越重要。

到10年7月,又实现了对WindowsMoible、Palm、Symbian的支持,支持平台达到6个。

11年7月,发布1.0版本,其中加入了很多访问本地设备的API;

11年11月,发布1.2版本,正事支持Windows Phone7,支持平台数字达到7个!

0.2.3 收购

11年10月,Adobe宣布收购PhoneGap;

收购之后PhoneGap团队将核心代码贡献给Adobe,但保留PhoneGap商标所有权,其实是做为Adobe的商业项目存在的(收购是为了挣钱!);

 

 

Adobe将PhoneGap贡献的代码作为一个新的项目,名为Cordova

0.2.4 现在版本

截止到16年7月份,PhoneGap已经更新到了6.2.0版本,已经支持windows mobile10

0.3  Phonegap与Cordova的区别、联系

0.3.1 区别

Cordova是PhoneGap贡献给Apache的项目,是一个开源的、核心的跨平台模块。

PhoneGap是Adobe的商业产品;

0.3.2 关系

Cordova与PhoneGap的关系就像是WebKit与Chrome的关系。

0.4  为什么出现PhoneGap这种技术?

有了PhoneGap,Web开发人员便可以利用他们非常熟悉的JavaScript、HTML和CSS技术,或者结合移动Web UI框架jQuery Mobile、Sencha Touch来开发跨平台移动客户端,还能非常方便地发布程序到不同移动平台上。

0.5  核心特点

0.5.1 兼容性

完全做到了written once,run everywhere;代码编写完之后,通过phonegap的build工具构建

0.5.2 标准化

采用w3c标准,包括但不限于HTML5、CSS3、JavaScript,比如说W3C标准中的命名方式等

0.5.3 大众化

不需要手机编程基础,只要会HTML就能做应用,且能通过js调用设备底层硬件(比如摄像头、传感器。。。)

0.6  如何学习?

网站推荐:

http://phonegap.com/  https://cordova.apache.org/

http://www.phonegap100.com/

htt://cordova.apache.org

http://phonegap-plugins.com/

1、搭建环境

1.1 模式1:通过命令行安装、编译运行

1.1.1 安装

官网建议安装方式:npm install -g cordova

 

问题:速度很慢,基本下载不下来,怎么办?

 

http://npm.taobao.org/ 这是淘宝做的npm镜像,和npm官网资源保持一致(每隔10分钟更新一次)

 

解决方案: npm install -g cordova  --registry http://registry.npm.taobao.org info

 

如何检验是否安装成功?

windows+R->Cmd->cordova

 

cmd命令:

①删除非空文件夹 rmdir 某个目录 /s

 

 

②进入某个文件夹

    cd 文件夹

③显示文件夹下的内容

    dir 文件夹名称

④清除屏幕内容

    cls

 

1.1.2 cordova 常用命令

   cordova项目平台部署操作

    增加或者删除ios平台

       cordova platform add ios

       cordova platform remove ios

    增加或删除android平台

       cordova platform add android

       cordova platform remove android

    查看当前项目所支持的平台

       cordova platfrom ls

 

   检查cordova项目build时是否具备条件(比如android,必须配置好了sdk)

    cordova requirements

 

   将cordova项目通过命令行的方式生产指定平台下的应用

       cordova run android(检查是否有真机,没有真机的话,去模拟器执行,没有模拟器就报错)

       cordova emulate android(在模拟器中执行)

   检查cordova项目可运行的设备列表

       cordova run --list

1.1.3 生成Android应用

必备条件:

①安装JDK(必须是JDK7或者更高的版本),并且配置环境变量JAVA_HOME(为JDK安装的路径)

②安装AndroidSDK,并且配置环境变量ANDROID_HOME(Android SDK安装的路径)

1.1.4 构建iOS应用

①必须在OS X操作系统的Mac上才能编译

②安装ios的SDK

③。。。

1.2 方式2 :借助图形化工具

cordova的商业项目phonegap,为了方便开发者使用,在命令行操作的基础上,推出了图形化工具,该工具分两部分:

 

1.2.1桌面端

可以安装在windows、Mac操作系统中

         下载地址:https://github.com/phonegap/phonegap-app-desktop/releases

 

         移动端:可以安装在iOS、Android、WindowsPhone手机或者平板中

         桌面版可以创建或者删除一个cordova项目,并且对项目做简单配置,然后提供一个服务;

1.2.2移动端

         主要是用来直接在指定的设备上预览app效果,而跳过编译代码、购买设备、注册账号(苹果开发者需要支付最低688元/年的费用)等繁琐的步骤,那么移动端是如何知道你在电脑中的代码并展示呢?这是在移动端应用打开时,需要指定服务器的地址和端口号,只要连上服务器就会自动从服务器读取内容并展示!

 

2、第一个cordova程序

2.1 选择开发模式

我们如何来创建自己的第一个Cordova程序呢?

命令行模式虽然在企业中使用较多,但对于我们新手过于极客,我们从图形化工具作为切入点开始学习,图形化开发的模式分两块,桌面端和移动端,在我给大家提供的资料中提供了一个windows安装版本(PhoneGapSetup-win32.exe),接下来,我们找到该文件并双击安装。

接下来安装移动端应用,在实际开发的时候,有两种选择:

一是通过模拟器,启动Android模拟器,通过adb将apk安装到模拟器中。

二是通过真机进行测试,将手机通过带有存储标识的数据线插入电脑,将apk直接拷贝到手机内存中,然后在手机中查找apk文件并点击安装,安装之后,如果想能够连接服务器端,需要保证在同一局域网内

那么我们应该选择哪种呢?显然,方式2的话,每个人需要一个手机且和自己的电脑在同一网络在公司中更合适,但是目前,方式1,也就是模拟器的方式更适合我们,所以接下来大家打开我们的eclipse,并启动模拟器。

2.2 开始

2.2.1 启动桌面版

默认界面是这样的:

 

 

   步骤1:创建用来存储项目的文件夹

首先在c盘根目录创建一个文件夹:phonegapDemo, 进入phonegaoDemo目录中

   步骤2:创建cordova项目

点击+号,在弹窗中可以创建或者打开一个cordova项目,我们在这里点击create来创建一个新的cordova项目!

 

 

 

 

   步骤3:判断是否创建成功

检查服务器是否跑起来

 

 

2.2.2 启动移动端

启动eclipse,启动虚拟设备管理器

 

 

 

接下来,需要将apk安装到虚拟机中,

有两个问题:从哪里下载apk?如何将apk安装到虚拟机中?

   如何下载

下载方式1:

通过官方提供的方式,直接去应用市场去下载:

iOS版本需要去AppleStore中下载

Android版本需要FQ去GooglePlay去下载

WindowsPhone版本需要去Windows Phone Stroe下载

 

下载方式2:

https://github.com/phonegap/phonegap-app-developer/tree/master/resources/release

 

 

比如说我要下载Android版本,怎么下载?

点击上图中的android文件夹,然后找到最新的版本:

 

 

点击下载即可。

   如何安装

通过adb来安装,那么什么是ADB?

ADB全称为Android Debug Bridge,是一个命令行工具,可以和模拟器或者Android真机进行通讯,可以通过adb命令来讲apk应用安装到模拟器或者真机上。

 

adb常用命令:

adb version 查看adb的版本

adb install **.apk 安装某个应用

adb devices 获取连接到本电脑的设备列表

adb-s  "emulator-5554"  install **.apk 为指定的某个设备安装某个应用

adb shell ** 后边可以接一些命令,比如查看目录,或者查看某个文件内容

adb shell ls       //查看当前目录有哪些内容

adb shell cat init.rc | more //查看某个文件,并将文件内容多次查看

adb shell pwd //打印当前的路径
adb start-server

adb kill-server

 

那么我们要安装肯定要用adb install就行了,如图操作:

 

 

安装成功之后,在模拟器找到PhoneGap点击启动,启动之后修改ip地址和端口号,连接成功之后会显示一个机器人图标!

  

 

 

 

2.3 显示helloWorld

打开webStorm,将之前创建的demo01文件夹导入到项目中,如下图所示:

 

 

2.3.1 修改文件

同时修改www目录下的index.html文件,将h1内容PhoneGap修改为HelloWorld,即完成第一个cordova的helloworld项目!

2.3.2 目录介绍

   index.html

工程根目录下的www目录的index.html文件,我们一起来分析一下:

<script type="text/javascript" src="cordova.js"></script>

cordova.js文件时phonegap的库文件,可以用来在JS中调用原生设备硬件(摄像头、联系人、GPS等),从而实现webapp无法实现的一些功能,更好的模拟原生应用。

但是我们会发现,在我们工程中是无法找到该文件的,是因为该文件已经集成apk文件,也就是Developer app或命令行的配置的工程中了

<meta name="format-detection" content="telephone=no" />

这个meta标签时在iOS系统中用来识别电话号码并自动产生连接可以直接拨号,但是很多时候像地址、ISBN或者其他数据,容易搞错,所以在这里设置为no

<meta name="msapplication-tap-highlight" content="no" />

这个是用在windowsPhone8或者以上版本用来禁止某种高亮效果等

 

   app.initialize()

  <script type="text/javascript">

        app.initialize();

    </script>

哪来的app对象?带着大家看index.js文件,在该文件中注意点如下:

事件:deviceReady,意味着Cordova设备api已经加载完毕可以调用了

index.js文件都做了那些事?

当接收到deviceReady事件之后,以冒泡的方式(第三个参数为false),执行onDeviceReady方法,在回调函数中又指定了receivedEvent方法,在这里,查找元素设置样式,就这么多!

 

   config.xml

该文件是一个全局配置文件,控制着cordova应用各个方面,根元素为一个widget,属性可以设置版本号、id编号等信息。

name为app正式名字,会在设备主屏幕上和应用市场里显示的

description为在应用市场中显示的app描述信息

author为作者信息

content会指定app启动时会执行的文件,默认值为项目目录下的www文件夹中的index.html文件

plugin会指定在项目中需要准备什么插件,会默认添加到工程中

preference可以指定一些键值对,比如config.xml中的:

 

 

3、cordova基础知识

在刚才的案例中,有一个注意点是deviceReady事件,这个事件是cordova本身的事件,那么cordova除此之外都有哪些事件?cordova又是如何和硬件进行沟通从而实现读写操作的?接下来,首先我们就从如何操作硬件来学习

3.1 如何读取硬件信息

步骤1:首先,打开网页phonegap-plugins.com

步骤2:我们以device这个插件为例,在上述网页中找到device这个插件 -->

https://github.com/apache/cordova-plugin-device

步骤3:找到www目录下的device.js文件

         可以看到这是个function,function有一些属性,比如platform、model,那么这些值是什么时候完成赋值的呢?在cordova进行初始化的时候,也就是在ondeviceready事件触发之前,在device.js中,会执行exec的方法,

         步骤4:资料中的cordova.js

         如果是在android平台运行的话,会自动触发android平台下的cordova.js文件中的androidExec方法,在这里会通过nativeApiProvider这个对象调用get方法,然后执行exec方法,得到返回的json对象,那么这个json对象哪来的?

 

步骤4:找到android目录下的device.java文件

在该文件中,会通过execute方法,获取到一个json对象,这个对象包含了本设备的一些基本信息,比如操作系统名称、型号信息

3.2 都有哪些事件

名称

说明

deviceready

当设备加载完毕后会触发该事件

pause

当程序被暂停到后台运行时

resume

当程序从后台激活到前台

online

网络断开切换到网络连接

offline

网络连接切换到网络断开

batterycritical

电量低于10%时会触发

batterylow

电量低于某个指定值触发

batterystatus

剩余电量发生变化会触发

backbutton

按下返回按钮

menubutton

按下菜单按钮

startcallbutton

通话按钮

endcallbutton

挂断通话按钮

volumedownbutton

按下音量减按钮

volumeupbutton

按下音量加按钮

searchbutton

按下搜索按钮

3.3 监听音量的增减操作

练习1:修改index.js文件,对音量的增减进行监听。

步骤1:在index.html文件中,在刚才修改为helloworld的h1标签下边,加一个p标签,id设置为p_id

 

 

步骤2:在index.js文件中,找到bindEvents方法,在方法中添加音量加减的监听

 

 

bindEvents: function() {
 
document.addEventListener('deviceready', this.onDeviceReady, false);
 
var p = document.getElementById('p_id');
 
document.addEventListener('volumedownbutton', function () {
 
p.innerHTML="volume down";
 
});
 
document.addEventListener('volumeupbutton', function () {
 
p.innerHTML="volume up";
 
})
 
},

 

 
 

 

步骤3:在保证phonegap desktop(桌面版)和移动端phonegap developer(移动端)已经连接的情况下,去查看结果

4、和硬件沟通

除了能够将HTML页面打包成可以直接安装运行的app外,phoneGap的最大优势在于通过js调用设备来访问设备上的硬件信息,从而实现一些原本只有依靠原生SDK才能达到的目的,接下来,我们利用cordova给我们提供的api来获取设备信息

4.1 获取设备信息

要想获取设备信息,必须在deviceready之后,否则是获取不到数据的。

练习2:获取设备信息(操作系统、版本)                                                                                                                           

 

步骤1: 在index.html文件中,添加一个p标签,设置id为device-info

 

 

步骤2:在index.js文件中,找到deviceready之后的回调函数,调用device,将设备信息展示在标签中

 

var deviceInfo = document.getElementById('device_info');
 
deviceInfo.innerHTML="设备名称:"+device.name
 
+"<br/>操作系统为:"+device.platform
 
+"<br/> 操作系统版本为:"+device.version;

 

 
 

 

步骤3:检查结果

                                               

 

 

 

通过上述案例,我们可以看到device这个api可以获取设备信息,那么cordova都有哪些核心api?

cordova给我们提供了十几个核心api如下所示:

核心api

描述

accelerometer

加速度传感器

camera

摄像头

capture

采集

compass

罗盘

contacts

联系人

device

设备

events

事件

file

文件

geolocation

地理位置

media

媒体

notification

通知(提醒)

storage

存储

 

4.2 消息提示

phonegap的notification类中封装了一系列设备视觉、听觉、触觉的通知,可以为应用定制专门的消息推送、警告或者提示。

notification.alert() 弹出一个警告或者对话框

notification.confirm() 显示一个可定制的确认对话框

notification.prompt() 弹出一个对话框

notification.beep() 控制蜂鸣器发出蜂鸣声

notification.vibrate() 产生震动

 

练习3:调用phonegap的notification api让手机发出声音、震动

步骤1:修改index.html,添加两个h1标签

  <h1 οnclick="playBeep()">蜂鸣器发声</h1>

   <h1 οnclick="vibrate()">使设备震动</h1>

步骤2:添加点击时的回调函数

 

<script type="text/javascript">
 
app.initialize();
 
function playBeep() {
 
navigator.notification.beep(3);
 
};
 
function vibrate() {
 
navigator.notification.vibrate(300);
 
}
 
</script>

 

 

 

步骤3:给学生解释下,模拟器是无法观察到震动和发生操作的,怎么办?

演示下,如何通过云打包的方式来生成apk。

登录build.phonegap.com,需要登录(用户名kunyashaw@gmail.com),然后打包上传,等待打包完成即可。(注意按照这种方式,目前还有问题,会提示cordova class not founf) 。

 

练习4:各种对话框

演示一个alert的用法,其余的自己去尝试

步骤1:修改index.html

 

 

步骤2:在js中添加个function

 

function showAlert(){
navigator.notification.alert(
"content",//内容
function () {//按钮点击的回调函数
playBeep();
alert("hello world");
}
);
}

 

 
 

 

 

4.3 加速度传感器

加速度传感器是一种用来检测物体相对运动方向以及沿着xyz三个方向运动的部件,phonegap提供了accelerometer类用来接收来自加速度传感器的数据,从而检测到设备在控件上的位置变化,比如说现在很流行的手环,或者说实现类似微信摇一摇功能的,传感器加上一些算法,那么我们如何使用phonegap是来得到传感器数据呢?

首先我们先开看第一个,如果获取加速度信息:

 

function getDeviceAcceletate(){
 
navigator.accelerometer.getCurrentAcceleration(function (acceleration) {
 
alert("x is "+acceleration.x+"\n y is "+acceleration.y+"\n z is "+acceleration.z);
 
}, function () {
 
alert('获取加速度信息失败')
 
})
 
}
 

 

 

那么通过上述方式,如果要检测运动数据的话,是不是得一直调用该方法啊,其实phonegap还给我们提供了一个方法:watchAcceleration(),我们一起来看下用法

 

 

function startWatch(){
var info = document.getElementById("accel_info");
id = navigator.accelerometer.watchAcceleration(
function (accel) {
info.innerHTML= "x is "+accel.x+"\nyis "+accel.y+"\nz is "+accel.z+"\n";
},
function () {
alert('error');
},
{frequency:300});
}
 
function stopWatch() {
if (id)
{
navigator.accelerometer.clearWatch(id);
}
id=null;
}

 

 
 

 

4.4 音视频的录制

 可以实现音视频的播放、录制

接下来,我们来看下phonegap是如何实现音频的录制的.

navigator.device.capture.captureAudio(captureSuccess, captureError, options);

// 限制采集上限为3个媒体文件,每个文件不超过10秒

var options = { limit: 3, duration: 10 };

 

 
 
 

function record(){
// 采集操作成功完成后的回调函数
var captureSuccess = function(mediaFiles) {
var i, path, len;
for (i = 0, len = mediaFiles.length; i < len; i += 1) {
path = mediaFiles[i].fullPath;
// 对文件进行感兴趣的操作
alert(path);
}
};
 
// 采集操作出错后的回调函数
var captureError = function(error) {
navigator.notification.alert('Error code: ' + error.code, null, 'Capture Error');
};
 
// 开始采集音频
navigator.device.capture.captureAudio(captureSuccess, captureError, {limit:2});
}

 

 

 

在phonegap中,如果想进行拍照、录制视频的话,只需要将captureAudio,修改为captrueImage、captureVideo就行了。

 

4.5 文件操作

phoneGap通常是将一类操作完全封装在同一个对象中,比如设备信息都在device类中,但是phoneGap对文件的操作方法却封装在了许多不同的对象中,所以这是phoneGap最为复杂的一类对象。接下来,我们一起来学习下phoneGap的文件操作的基础知识。

如何操作一个文件?phoneGap提供了两个类分别是fileReader类和fileWriter类,在这两个类中,调用不同的方法去操作文件,插件名称是file,该插件是基于h5的file api,所以如果大家接触过h5的文件api的话,其实phonegap这里基本是一致的。

插件地址:https://github.com/apache/cordova-plugin-file/

 

4.5.1 fileReader

 

首先先来看第一个类fileReader,是一个允许用户读取文件的对象,文件以文本或者Base64编码的字符串样式读出来,用户注册自己的事件监听器来接受loadstart、loadend等事件

属性:

         readyState:当前读取器所处的状态,取值为以下三者之一:EMPTY、LOADING和DONE。

         result已读取文件的内容。(DOMString类型)

         error包含错误信息的对象。(FileError类型)

         onloadstart读取启动时调用的回调函数。(函数类型)

         onprogress读取过程中调用的回调函数,用于汇报读取进度(progress.loaded和progress.total)。(函数类型) 不支持

         onload读取安全完成后调用的回调函数。(函数类型)

         onabort读取被中止后调用的回调函数,例如通过调用abort()方法。(函数类型)

         onerror读取失败后调用的回调函数。(函数类型)

         onloadend请求完成后调用的回调函数(无论请求是成功还是失败)。(函数类型)

 

方法:

         abort中止读取文件。

         readAsDataURL读取文件,结果以Base64编码的data URL形式返回。(data URL的格式由IETF在RFC2397中定义)

         readAsText读取文件,结果以文本字符串返回。

        

         使用案例

修改index.html

 

     

<!-- 读取文件操作-->
 
<br/>
 
<button οnclick="readFile()">读取文件</button>
 
<h1 id="file_read_id">example</h1>
 

 

 

在index.html中添加js代码

 

 

 

function readFile(){
 
window.requestFileSystem(LocalFileSystem.PERSISTENT,0,gotFSRead,fail);//只有执行了该函数,才能操作系统中的文件
 
}
 
 
 
function gotFSRead(fileSystem) {
 
 
 
fileSystem.root.getFile("readme.txt",null,getFileEntryRead,fail);//得到一个fileEntry对象,代表文件系统中的一个文件
 
}
 
 
 
function getFileEntryRead(fileEntry) {
 
 
 
fileEntry.file(function (file) {
 
var reader = new FileReader();//获取对文件进行读操作的对象
 
reader.onloadend = function (evt) {
 
alert(evt.target.result);
 
document.getElementById('file_read_id').innerHTML = evt.target.result;
 
};
 
reader.readAsText(file);
 
},fail)
 
}
 
function fail(evt) {
 
alert(evt.target.error.code);
 
}
 
 
 

 

 

 

4.5.2 fileWriter

 

<!-- 写入文件操作-->
 
<br/>
 
<button οnclick="writeFile()">写入文件</button>
 
<input id="file_write_id"/>
 
 
 
 
function writeFile(){
 
window.requestFileSystem(LocalFileSystem.PERSISTENT,0,gotFSRead,fail);//只有执行了该函数,才能操作系统中的文件
 
}
 
 
 
 
 
function gotFSWrite(fileSystem) {
 
alert('call getFile Write');
 
fileSystem.root.getFile("readme2.txt",{create:true},getFileEntryWrite,fail);//得到一个fileEntry对象,代表文件系统中的一个文件
 
}
 
 
 
function getFileEntryWrite(fileEntry){
 
fileEntry.createWriter(function (writer) {
 
//writer 获取对文件进行写操作的对象
 
writer.onwrite = function (evt) {
 
alert("success");
 
};
 
 
 
writer.write(document.getElementById('file_write_id').value);
 
},fail);
 
}

 

 
 
 
 
 

 

5、每日一练

根据今天学习的加速度传感器的知识,实现摇一摇,改变页面背景色为一个随机颜色。

如何实现:

1、要实现对加速度传感器的监听

2、判断传感器上下左右任意一个值的变化,如果前后两次变化的值超过了25,就改变背景色

3、多久判断一次呢,可以创建个定时器相隔1s判断一次

 

 
 
function startWatch(){
 
var info = document.getElementById("accel_info");
 
id = navigator.accelerometer.watchAcceleration(function (accel) {
 
info.innerHTML= "x is "+accel.x+"\nyis "+accel.y+"\nz is "+accel.z+"\n";
 
x1=accel.x;
 
y1=accel.y;
 
z1=accel.z;
 
}, function () {
 
alert('error');
 
},{frequency:300});
 
}
 
 
 
function stopWatch() {
 
if (id)
 
{
 
navigator.accelerometer.clearWatch(id);
 
}
 
id=null;
 
}
 
var x1= 0,x2= 0,y1= 0,y2= 0,z1= 0,z2=0;
 
setInterval(function () {
 
var change = Math.abs(x1-x2+y1-y2+z1-z2);
 
if(change>25)
 
{
 
document.getElementById("btn_id").style.backgroundColor = getRandomColor();
 
}
 
x2=x1;
 
y2=y1;
 
z2=z1;
 
},100);
 
function getRandomColor(){
 
var r = Math.floor(Math.random()*256);
 
var g = Math.floor(Math.random()*256);
 
var b = Math.floor(Math.random()*256);
 
 
 
var hexR = r.toString(16);
 
var hexG = g.toString(16);
 
var hexB = b.toString(16);
 
 
 
return "#"+hexR+hexG+hexB;
 
 
 
}

 

 

 

转载于:https://www.cnblogs.com/kunyashaw/p/5661228.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值