
Android平台设置UrlSchemes过程中遇到的几个坑
官方代码
document.addEventListener('plusready',function(){
checkArguments();
},false);
// 判断启动方式
function checkArguments(){
console.log("plus.runtime.launcher: "+plus.runtime.launcher);
var args= plus.runtime.arguments;
if(args){
// 处理args参数,如打开新页面等
}
}
// 处理从后台恢复
document.addEventListener('newintent',function(){
console.log("addEventListener: newintent");
checkArguments();
},false);
官方代码没解释太多东西。
第一个坑:
<a herf="test://index.html">点击打开app</a>
app打开之后提示 test://index.html 这个页面打不开
解决方法:
传递参数不要用html后缀之类的形式。建议还是按照官方标准,使用json字符串形式,接收到之后解析url并且跳转。
<a herf="test://{'url':'index.html'}">点击打开app</a>
第二个坑:
app后台运行发起没有任何问题,app如果后台关闭,通过链接唤醒之后,跳转到参数页面,然后无限刷新,根本停止不下来
根据html5+手册介绍 plus.runtime.arguments 这个参数是只读的,设置为空,并且判断也没用
解决方法:
这个放在入口html文件,app关闭情况下,加载会先加载这个页面,这里判断并且执行跳转。
这个plus.runtime.arguments参数清理不掉,但是其他文件不执行,解决了无限跳转的问题!
document.addEventListener('plusready',function(){
checkArguments();
},false);
下面这个放在全局,app不关闭,后台切换到前台的唤醒,自己就执行一次,我也不知道为什么!
// 判断启动方式
function checkArguments(){
console.log("plus.runtime.launcher: "+plus.runtime.launcher);
var args= plus.runtime.arguments;
if(args){
// 处理args参数,如打开新页面等
}
}
// 处理从后台恢复
document.addEventListener('newintent',function(){
console.log("addEventListener: newintent");
checkArguments();
},false);
官方代码
document.addEventListener('plusready',function(){
checkArguments();
},false);
// 判断启动方式
function checkArguments(){
console.log("plus.runtime.launcher: "+plus.runtime.launcher);
var args= plus.runtime.arguments;
if(args){
// 处理args参数,如打开新页面等
}
}
// 处理从后台恢复
document.addEventListener('newintent',function(){
console.log("addEventListener: newintent");
checkArguments();
},false);
官方代码没解释太多东西。
第一个坑:
<a herf="test://index.html">点击打开app</a>
app打开之后提示 test://index.html 这个页面打不开
解决方法:
传递参数不要用html后缀之类的形式。建议还是按照官方标准,使用json字符串形式,接收到之后解析url并且跳转。
<a herf="test://{'url':'index.html'}">点击打开app</a>
第二个坑:
app后台运行发起没有任何问题,app如果后台关闭,通过链接唤醒之后,跳转到参数页面,然后无限刷新,根本停止不下来
根据html5+手册介绍 plus.runtime.arguments 这个参数是只读的,设置为空,并且判断也没用
解决方法:
这个放在入口html文件,app关闭情况下,加载会先加载这个页面,这里判断并且执行跳转。
这个plus.runtime.arguments参数清理不掉,但是其他文件不执行,解决了无限跳转的问题!
document.addEventListener('plusready',function(){
checkArguments();
},false);
下面这个放在全局,app不关闭,后台切换到前台的唤醒,自己就执行一次,我也不知道为什么!
// 判断启动方式
function checkArguments(){
console.log("plus.runtime.launcher: "+plus.runtime.launcher);
var args= plus.runtime.arguments;
if(args){
// 处理args参数,如打开新页面等
}
}
// 处理从后台恢复
document.addEventListener('newintent',function(){
console.log("addEventListener: newintent");
checkArguments();
},false);
收起阅读 »

mui开发H5+app——原生选择系统相册图片上传七牛云
```要完成用MUI 拍照和从系统相册选择图片上传的功能,可以理解成有三个功能
1 调用手机相机的功能(可以查看官方API http://www.html5plus.org/doc/zh_cn/camera.html)
2 调用系统相册的功能(可以查看官方API http://www.html5plus.org/doc/zh_cn/gallery.html)
3 照片资源上传到服务(可以查看官方API http://www.html5plus.org/doc/zh_cn/uploader.html)
完整实例
流程:获取七牛云token => 选择图片 => 上传图片 =>返回key。
代码如图:
```javascript
upImg: function() {
this.getToken(); //获取token
var _this = this;
console.log(_this.token);
// 从相册获取图片
plus.gallery.pick(function(ret) {
// 获取图片名称
var path = ret;
var file = ret.substr(ret.lastIndexOf("/") + 1);
var token = _this.token; // 填写你的七牛上传令牌
// 上传图片
var url = "http://upload-z2.qiniup.com";
document.getElementById('img').src = path;
this.ajaxUp(token, file, url, path);
});
}
一、获取七牛云token
此项目因使用七牛云,所以后端选择PHP的TP5框架。
1.将七牛云的第三方SDK放至tp5框架中,参考https://developer.qiniu.com/kodo/sdk/1241/php
2.配置AK和SK,写好后端接口
代码如图:
<?php
namespace app\index\controller;
下载sdk包解压并改名为qiniuyun,放在vendor目录下,引入qiniuyun目录下的autoload.php文件
require_once VENDOR_PATH . 'qiniuyun/autoload.php';
use think\Controller;
use \Qiniu\Auth;
/**
- 直接通过前端上传文件到七牛云储存空间(文件小于4MB)
- 上传token应先生成,并填写到上传表单的隐藏域,表单提交地址目前固定为:http://up-z2.qiniup.com
- <form method="post" action="http://up-z2.qiniup.com" enctype="multipart/form-data">
- <input name="token" type="hidden" value="上传token">
- <input name="file" type="file" />
- <input type="submit" value="上传"/>
- </form>
-
上传成功后会返回包含key的json数据,可根据该key生成文件的获取地址(地址存数据库)
*/
class Qiniuyun extends Controller
{
private $accessKey = '你的AK'; # accessKey
private $secretKey = '你的SK'; # secretKey
private $bucket = '你的空间'; # 存储空间名称
private $domain = '空间对应域名'; # 域名(与上面存储空间对应)
public $auth; # auth对象public function _initialize()
{
if (!$this->auth) {
$this->auth = new Auth($this->accessKey, $this->secretKey);
}
return $this->auth;
}/**
- 获取上传token
- @return string
*/
public function getToken()
{
return $this->auth->uploadToken($this->bucket);
}
/**
- 获取文件访问地址
- @param string $key 文件上传成功后返回的key
- @return string
*/
public function getUrl($key)
{
$baseUrl = 'http://' . $this->domain . '/' . $key;
return $this->auth->privateDownloadUrl($baseUrl);
}
}
前端请求后端接口获取token(请求开发者服务器)
代码如图:
/获取token凭证,上传前无凭证无法上传/
getToken: function() {
var _this = this;
mui.ajax('https://XXXXX/index/upload/getToken', {
data: {},
dataType: 'jsonp',
type: 'post',
timeout: '10000',
success: function(res) {
//此步骤打印出返回的token 观察是否存在双引号
_this.token = res.replace(/\"/g,"");//去双引号 (无双引号可省略)
console.log(res);
},
error: function(xhr, type, errorThrown) {
plus.nativeUI.toast('网络请求错误');
console.log(errorThrown);
}
});
}
3.请求七牛云服务器,上传图片
代码如图:
/**
- 上传图片
- @param token(str) 上传token 由七牛返回
- @param file(str) 文件名
- @param url(str) 请求路径
-
@param path(file) 文件对象
**/
ajaxUp: function(token, file, url, path) {
var _this = this;
var uploader = plus.uploader.createUpload(url, {
method: "POST"
}, function(up, state) {var res = JSON.parse(up.responseText); if(state == 200){ document.getElementById('imgUrl').value = _this.imgUrl+res.key; console.log("上传成功"+ res.key); } else{ console.log("上传失败 - " + state); } }); /** * * 上传参数addData * * 上传文件addFile * * 请勿将二者弄混 addFile上传的是文字对象 addData上传为字符串 * */ uploader.addData("key", file); uploader.addData("token", token); //添加上传参数 token必填 uploader.addFile(path, { "key": "file" }); // 固定值,千万不要修改 uploader.start(); }
html部分
<!--这个是表单提交的方法-->
<!--<form method="post" action="http://up-z2.qiniup.com" enctype="multipart/form-data">
<input name="token" type="text" :value="token">
<input name="file" type="file" accept="image/*" @click="getToken()"/>
<input type="submit" value="上传"/>
</form>-->
<!--这个是调用原生接口的方法-->
<img class="my_img_class" width="100px" height="100px" @click="upImg" id="img" src=""/>
<p>
此input只做演示,实际应用时 input应为隐藏
<input type="text" name="imgUrl" id="imgUrl" value="" />
</p>
最后
通过七牛云上传图片后,将返回的key保存,然后获取对应的图片路径。
注意事项:
1.七牛云空间所在地区。七牛云空间所处地区不同,请求的URL也不一样,本人是华南的空间,因此路径为http://upload-z2.qiniup.com(是客户端路径不是服务器端路径)
2.H5+手机原生上传组件 addData与addFile的使用。上传的图片是对象,因此图片应使用addFile,但七牛云需要我们上传字段,所以上传的字段我们使用addData
3.返回的token。调试时请打印返回的token,观察token是否带有“”,有则去掉。否则接口会报401错误
结尾
本文为手机H5+APP开发中图片上传解决思路,并非最好的方法,仅列出常见问题,因系统差异问题,ios与安卓所遇问题不一定相同。
原文链接https://my.oschina.net/incess/blog/1818787
```要完成用MUI 拍照和从系统相册选择图片上传的功能,可以理解成有三个功能
1 调用手机相机的功能(可以查看官方API http://www.html5plus.org/doc/zh_cn/camera.html)
2 调用系统相册的功能(可以查看官方API http://www.html5plus.org/doc/zh_cn/gallery.html)
3 照片资源上传到服务(可以查看官方API http://www.html5plus.org/doc/zh_cn/uploader.html)
完整实例
流程:获取七牛云token => 选择图片 => 上传图片 =>返回key。
代码如图:
```javascript
upImg: function() {
this.getToken(); //获取token
var _this = this;
console.log(_this.token);
// 从相册获取图片
plus.gallery.pick(function(ret) {
// 获取图片名称
var path = ret;
var file = ret.substr(ret.lastIndexOf("/") + 1);
var token = _this.token; // 填写你的七牛上传令牌
// 上传图片
var url = "http://upload-z2.qiniup.com";
document.getElementById('img').src = path;
this.ajaxUp(token, file, url, path);
});
}
一、获取七牛云token
此项目因使用七牛云,所以后端选择PHP的TP5框架。
1.将七牛云的第三方SDK放至tp5框架中,参考https://developer.qiniu.com/kodo/sdk/1241/php
2.配置AK和SK,写好后端接口
代码如图:
<?php
namespace app\index\controller;
下载sdk包解压并改名为qiniuyun,放在vendor目录下,引入qiniuyun目录下的autoload.php文件
require_once VENDOR_PATH . 'qiniuyun/autoload.php';
use think\Controller;
use \Qiniu\Auth;
/**
- 直接通过前端上传文件到七牛云储存空间(文件小于4MB)
- 上传token应先生成,并填写到上传表单的隐藏域,表单提交地址目前固定为:http://up-z2.qiniup.com
- <form method="post" action="http://up-z2.qiniup.com" enctype="multipart/form-data">
- <input name="token" type="hidden" value="上传token">
- <input name="file" type="file" />
- <input type="submit" value="上传"/>
- </form>
-
上传成功后会返回包含key的json数据,可根据该key生成文件的获取地址(地址存数据库)
*/
class Qiniuyun extends Controller
{
private $accessKey = '你的AK'; # accessKey
private $secretKey = '你的SK'; # secretKey
private $bucket = '你的空间'; # 存储空间名称
private $domain = '空间对应域名'; # 域名(与上面存储空间对应)
public $auth; # auth对象public function _initialize()
{
if (!$this->auth) {
$this->auth = new Auth($this->accessKey, $this->secretKey);
}
return $this->auth;
}/**
- 获取上传token
- @return string
*/
public function getToken()
{
return $this->auth->uploadToken($this->bucket);
}
/**
- 获取文件访问地址
- @param string $key 文件上传成功后返回的key
- @return string
*/
public function getUrl($key)
{
$baseUrl = 'http://' . $this->domain . '/' . $key;
return $this->auth->privateDownloadUrl($baseUrl);
}
}
前端请求后端接口获取token(请求开发者服务器)
代码如图:
/获取token凭证,上传前无凭证无法上传/
getToken: function() {
var _this = this;
mui.ajax('https://XXXXX/index/upload/getToken', {
data: {},
dataType: 'jsonp',
type: 'post',
timeout: '10000',
success: function(res) {
//此步骤打印出返回的token 观察是否存在双引号
_this.token = res.replace(/\"/g,"");//去双引号 (无双引号可省略)
console.log(res);
},
error: function(xhr, type, errorThrown) {
plus.nativeUI.toast('网络请求错误');
console.log(errorThrown);
}
});
}
3.请求七牛云服务器,上传图片
代码如图:
/**
- 上传图片
- @param token(str) 上传token 由七牛返回
- @param file(str) 文件名
- @param url(str) 请求路径
-
@param path(file) 文件对象
**/
ajaxUp: function(token, file, url, path) {
var _this = this;
var uploader = plus.uploader.createUpload(url, {
method: "POST"
}, function(up, state) {var res = JSON.parse(up.responseText); if(state == 200){ document.getElementById('imgUrl').value = _this.imgUrl+res.key; console.log("上传成功"+ res.key); } else{ console.log("上传失败 - " + state); } }); /** * * 上传参数addData * * 上传文件addFile * * 请勿将二者弄混 addFile上传的是文字对象 addData上传为字符串 * */ uploader.addData("key", file); uploader.addData("token", token); //添加上传参数 token必填 uploader.addFile(path, { "key": "file" }); // 固定值,千万不要修改 uploader.start(); }
html部分
<!--这个是表单提交的方法-->
<!--<form method="post" action="http://up-z2.qiniup.com" enctype="multipart/form-data">
<input name="token" type="text" :value="token">
<input name="file" type="file" accept="image/*" @click="getToken()"/>
<input type="submit" value="上传"/>
</form>-->
<!--这个是调用原生接口的方法-->
<img class="my_img_class" width="100px" height="100px" @click="upImg" id="img" src=""/>
<p>
此input只做演示,实际应用时 input应为隐藏
<input type="text" name="imgUrl" id="imgUrl" value="" />
</p>
最后
通过七牛云上传图片后,将返回的key保存,然后获取对应的图片路径。
注意事项:
1.七牛云空间所在地区。七牛云空间所处地区不同,请求的URL也不一样,本人是华南的空间,因此路径为http://upload-z2.qiniup.com(是客户端路径不是服务器端路径)
2.H5+手机原生上传组件 addData与addFile的使用。上传的图片是对象,因此图片应使用addFile,但七牛云需要我们上传字段,所以上传的字段我们使用addData
3.返回的token。调试时请打印返回的token,观察token是否带有“”,有则去掉。否则接口会报401错误
结尾
本文为手机H5+APP开发中图片上传解决思路,并非最好的方法,仅列出常见问题,因系统差异问题,ios与安卓所遇问题不一定相同。
原文链接https://my.oschina.net/incess/blog/1818787

请教大佬们一个问题,你们在布局的时候是怎么适配手机的刘海屏的啊
就是布局里面,有些是需要把一个元素贴在顶部显示的,或者需要距离顶部一些距离,
在小程序端我想到了一个方法就是获取小程序胶囊按钮的位置,然后通过胶囊按钮的top属性来适配,( wx.getMenuButtonBoundingClientRect() )这个属性
哪在app端或者h5端哪?这个有什么好的方法来适配的,大佬们,有没有好的方法分享一下
就是布局里面,有些是需要把一个元素贴在顶部显示的,或者需要距离顶部一些距离,
在小程序端我想到了一个方法就是获取小程序胶囊按钮的位置,然后通过胶囊按钮的top属性来适配,( wx.getMenuButtonBoundingClientRect() )这个属性
哪在app端或者h5端哪?这个有什么好的方法来适配的,大佬们,有没有好的方法分享一下
收起阅读 »
如果在正式版1.9.4里面无法使用自定义组价编译模式或者在安卓机上面卡顿的 可以使用Alpha版 v1.9.7 亲测有效
如果在正式版1.9.4里面无法使用自定义组价编译模式或者在安卓机上面卡顿的 可以使用Alpha版 v1.9.7 亲测有效
如果在正式版1.9.4里面无法使用自定义组价编译模式或者在安卓机上面卡顿的 可以使用Alpha版 v1.9.7 亲测有效

真机运行总是卡在43%,其中一个可能
大概只有我遇到过吧,总是卡在43%,安装失败,找不到原因,后来发现它莫名其妙安装在了手机分身里,导致安装失败
大概只有我遇到过吧,总是卡在43%,安装失败,找不到原因,后来发现它莫名其妙安装在了手机分身里,导致安装失败

hubilder的代码块,官方文档实在看不懂。
hubilder的代码块,官方文档实在看不懂。百度出来的也是以前旧的代码块设置方法或者就是拷贝的现在的文档。
hubilder的代码块,官方文档实在看不懂。百度出来的也是以前旧的代码块设置方法或者就是拷贝的现在的文档。

HBuilderX:代码块说明及自定义代码块教程
本帖文档已集成到: hx产品文档
代码块是快速开发的利器。简单的敲几个字母,回车,就能生成大段代码。
比如我们经常会敲if...else结构,在HBuilderX中,只需敲ife
回车,就能直接生成相应的代码结构。
- 敲ife
- 回车后生成if结构体
HBuilderX已经内置了大量常用的代码块,熟悉这些代码块,对于提高编程效率有重要帮助。
查看内建的代码块,点菜单-工具-代码块设置,选择你要查看的语言的代码块。
打开的界面中,左侧即是预置的代码块,右侧是开发者可以自己扩展代码块的地方。
常用代码块列表
通用js代码块
- iff :简单if
- forr :for循环结构体
- fori :for循环结构体并包含i
- funn:函数
- funa:匿名函数
- clog:打印日志
- clogvar:打印变量命名和值
dom代码块
- dg :document.getElementById
- dl :$("")
vue代码块
敲v,即可拉出各种vue代码块
uni-app代码块
敲u,即可拉出各种uni-app代码块
还有ifios、ifandroid,这2个平台判断代码块(HBuilderX 1.9.10+)
模板示例
自定义代码块都是配置json文件中的,直接来一个js例子吧,上述ife
代码块的配置如下:
{
"if ... else": {
"body": [
"if ($1) {",
"\t$0",
"} else{",
"\t",
"}"
],
"prefix": "ife",
"scope": "source.js"
}
}
代码块配置格式说明
HBuilderX使用json定义代码块的格式,兼容vscode的代码块格式,也就是你可以把vscode里已经配置的自定义代码块方便的挪到HBuilderX中使用。
每个配置项的说明如下:
key
"key" :代码块显示名称,显示在代码助手列表中的名字。key是不能重复的。
上面例子中"if ... else"
就是一个key
。
prefix
"prefix" :代码块的触发字符,就是敲什么字母可以激活这个代码块。
body
"body" :代码块的内容。内容中有如下特殊格式
$1
表示代码块输入后光标的所在位置。如需要多光标,就在多个地方配置$1
;如该位置有预置数据且需要选中,则写法是${1:selectedtext}
;这里还支持下拉候选菜单,多选项即下拉候选列表使用${1:foo1/foo2/foo3}
$2
表示代码块输入后再次按tab后光标的切换位置tabstops
(代码块展开后按tab可以跳到下一个tabstop
,在HBuilderX中看到类似绿色光标的不闪的竖线,就可以按tab或回车跳转光标过去)
$0
代表代码块输入后最终光标的所在位置(也可以按回车直接跳过去)。
双引号使用\"
转义
换行使用多个数组表示,每个行一个数组,用双引号
包围,并用逗号
分隔
缩进需要用\t
表示,不能直接输入缩进或空格!
triggerAssist
"triggerAssist" :为true
表示该代码块输入到文档后立即在第一个tabstop
上触发代码提示,拉出代码助手,默认为false
。
project
project: 将代码块控制在指定项目类型下生效。可取值有:uni-app
、Web
、App
、Wap2App
比如:"project": "uni-app"
,代表这个代码块仅在uni-app项目下生效
如果不设置,则该代码块在所有项目类型下均生效。
Web
指普通项目,App
指5+App项目。
如需设置多种项目类型,用逗号分隔。比如:"project": "uni-app,App"
注意事项
- 每个代码块以
key
为主键,多个代码块需要逗号
分隔。 - 如果json语法不合法,编辑器状态栏会弹出错误信息,json中会画出红波浪线,请注意修正。
本帖文档已集成到: hx产品文档
代码块是快速开发的利器。简单的敲几个字母,回车,就能生成大段代码。
比如我们经常会敲if...else结构,在HBuilderX中,只需敲ife
回车,就能直接生成相应的代码结构。
- 敲ife
- 回车后生成if结构体
HBuilderX已经内置了大量常用的代码块,熟悉这些代码块,对于提高编程效率有重要帮助。
查看内建的代码块,点菜单-工具-代码块设置,选择你要查看的语言的代码块。
打开的界面中,左侧即是预置的代码块,右侧是开发者可以自己扩展代码块的地方。
常用代码块列表
通用js代码块
- iff :简单if
- forr :for循环结构体
- fori :for循环结构体并包含i
- funn:函数
- funa:匿名函数
- clog:打印日志
- clogvar:打印变量命名和值
dom代码块
- dg :document.getElementById
- dl :$("")
vue代码块
敲v,即可拉出各种vue代码块
uni-app代码块
敲u,即可拉出各种uni-app代码块
还有ifios、ifandroid,这2个平台判断代码块(HBuilderX 1.9.10+)
模板示例
自定义代码块都是配置json文件中的,直接来一个js例子吧,上述ife
代码块的配置如下:
{
"if ... else": {
"body": [
"if ($1) {",
"\t$0",
"} else{",
"\t",
"}"
],
"prefix": "ife",
"scope": "source.js"
}
}
代码块配置格式说明
HBuilderX使用json定义代码块的格式,兼容vscode的代码块格式,也就是你可以把vscode里已经配置的自定义代码块方便的挪到HBuilderX中使用。
每个配置项的说明如下:
key
"key" :代码块显示名称,显示在代码助手列表中的名字。key是不能重复的。
上面例子中"if ... else"
就是一个key
。
prefix
"prefix" :代码块的触发字符,就是敲什么字母可以激活这个代码块。
body
"body" :代码块的内容。内容中有如下特殊格式
$1
表示代码块输入后光标的所在位置。如需要多光标,就在多个地方配置$1
;如该位置有预置数据且需要选中,则写法是${1:selectedtext}
;这里还支持下拉候选菜单,多选项即下拉候选列表使用${1:foo1/foo2/foo3}
$2
表示代码块输入后再次按tab后光标的切换位置tabstops
(代码块展开后按tab可以跳到下一个tabstop
,在HBuilderX中看到类似绿色光标的不闪的竖线,就可以按tab或回车跳转光标过去)
$0
代表代码块输入后最终光标的所在位置(也可以按回车直接跳过去)。
双引号使用\"
转义
换行使用多个数组表示,每个行一个数组,用双引号
包围,并用逗号
分隔
缩进需要用\t
表示,不能直接输入缩进或空格!
triggerAssist
"triggerAssist" :为true
表示该代码块输入到文档后立即在第一个tabstop
上触发代码提示,拉出代码助手,默认为false
。
project
project: 将代码块控制在指定项目类型下生效。可取值有:uni-app
、Web
、App
、Wap2App
比如:"project": "uni-app"
,代表这个代码块仅在uni-app项目下生效
如果不设置,则该代码块在所有项目类型下均生效。
Web
指普通项目,App
指5+App项目。
如需设置多种项目类型,用逗号分隔。比如:"project": "uni-app,App"
注意事项
- 每个代码块以
key
为主键,多个代码块需要逗号
分隔。 - 如果json语法不合法,编辑器状态栏会弹出错误信息,json中会画出红波浪线,请注意修正。

分享一个Andorid手机插入一个日程事件
实现功能:
- 建立自己的账户,每次插入日历都使用自己的账户,区分第三方日历账户与系统日历账户
- 实现建立的日历删除
- 实现2次闹钟提醒,(addcalendar中的参数 fr, tr的值必须为分钟数字,比如:你的闹钟设置是1个小时提醒,输入的参数必须是60)
mui.plusReady(function() {
if(mui.os.ios) {
} else {
var calanderURL = 'content://com.android.calendar/calendars',
eventsURL = 'content://com.android.calendar/events',
ContentValues = plus.android.importClass("android.content.ContentValues"),
Uri = plus.android.importClass('android.net.Uri'),
ContentUris = plus.android.importClass('android.content.ContentUris'),
Calendar = plus.android.importClass('java.util.Calendar'),
main = plus.android.runtimeMainActivity(),
userCursor = plus.android.invoke(main.getContentResolver(), 'query', Uri.parse(calanderURL), null, null, null, null),
userCursor_count = plus.android.invoke(userCursor, 'getCount'),
TimeZone = plus.android.importClass('java.util.TimeZone'),
TimeZone_str = plus.android.invoke(TimeZone.getDefault(), 'getID');
}
//Nid参数为数据库中的一个id,可以不用,我用这个Nid实现与手机系统的日历事件id进行关联
addcalendar = function(Nid, title, description, date_str, etime, fr, tr) {
if(userCursor_count <= 0) { //如果没有日历账户
addcalendaraccount();
userCursor = plus.android.invoke(main.getContentResolver(), 'query', Uri.parse(calanderURL), null, null, null, null);
userCursor_count++;
}
plus.android.invoke(userCursor, 'moveToFirst');//第一个
//plus.android.invoke(userCursor, 'moveToLast');//最后一个
//plus.android.invoke(userCursor, 'moveToNext');//下一个
for(var i = 0; i < userCursor_count; i++) {
var calName = plus.android.invoke(userCursor, 'getString', plus.android.invoke(userCursor, 'getColumnIndex', 'calendar_displayName'));
//获得日历的描述名称
if(calName == "OA日历") {
break;
}
if(i == (userCursor_count - 1)) {
addcalendaraccount();
userCursor = plus.android.invoke(main.getContentResolver(), 'query', Uri.parse(calanderURL), null, null, null, null);
userCursor_count++;
}
plus.android.invoke(userCursor, 'moveToNext');
}
try {
var calId = plus.android.invoke(userCursor, 'getString', plus.android.invoke(userCursor, 'getColumnIndex', '_id')),
events = new ContentValues(),
mCalendar = Calendar.getInstance(),
date = date_str.split(/\s{1}|:|-/g);
plus.android.invoke(mCalendar, 'set', Calendar.YEAR, ~~date[0]);
plus.android.invoke(mCalendar, 'set', Calendar.MONTH, ((~~date[1]) - 1));
plus.android.invoke(mCalendar, 'set', Calendar.DATE, ~~date[2]);
plus.android.invoke(mCalendar, 'set', Calendar.HOUR_OF_DAY, ~~date[3]);
plus.android.invoke(mCalendar, 'set', Calendar.MINUTE, ~~date[4]);
var start = plus.android.invoke(plus.android.invoke(mCalendar, 'getTime'), 'getTime');
//设置日历事件
var etimestamp = new Date(etime.replace(/-/g, "\/")).getTime();//时间可以用传入参数的模式
events.put('title', title);
events.put('description', description);
events.put('calendar_id', calId);
events.put('dtstart', start);
events.put('dtend', etimestamp);
//events.put('ALL_DAY', 0); //值为 1 表示此事件占用一整天(按照本地时区的定义)。 值为 0 表示它是常规事件,可在一天内的任何时间开始和结束。
//events.put('RRULE')
events.put('hasAlarm', 1);
events.put('eventTimezone', TimeZone_str);
var newEvent = plus.android.invoke(main.getContentResolver(), 'insert', Uri.parse('content://com.android.calendar/events'), events);
var id = plus.android.invoke(newEvent, 'getLastPathSegment');
if(fr.length > 0) {
var values = new ContentValues();
values.put('event_id', id);
values.put('minutes', fr);
values.put('method', '1');
plus.android.invoke(main.getContentResolver(), 'insert', Uri.parse('content://com.android.calendar/reminders'), values);
}
if(tr.length > 0) {
var values2 = new ContentValues();
values2.put('event_id', id);
values2.put('minutes', tr);
values2.put('method', '1');
plus.android.invoke(main.getContentResolver(), 'insert', Uri.parse('content://com.android.calendar/reminders'), values2);
}
//SaveCalendID(id, Nid, callSetProRmData); 把插入日历的id保存起来,便于删除调用
//也可以用于更新(先删后插入);
} catch(e) {
mui.alert("您没有允许APP访问日历的权限");
}
}
deletecalendar = function(eventID) {
if(userCursor_count <= 0) { //如果没有日历账户
addcalendaraccount();
}
var id = Number(eventID);
//alert(id);
var deleteUri = ContentUris.withAppendedId(Uri.parse(eventsURL), id);
var rows = plus.android.invoke(main.getContentResolver(), "delete", deleteUri, null, null);
if(rows == -1) {
return;
}
}
addcalendaraccount = function() {
var account = new ContentValues(),
buildUpon = plus.android.invoke(Uri.parse(calanderURL), 'buildUpon'),
CalendarContract = plus.android.importClass('android.provider.CalendarContract');
plus.android.invoke(buildUpon, 'appendQueryParameter', CalendarContract.CALLER_IS_SYNCADAPTER, 'true');
plus.android.invoke(buildUpon, 'appendQueryParameter', 'account_name', 'OA@xxx.com.com');
plus.android.invoke(buildUpon, 'appendQueryParameter', 'account_type', 'com.android.exchange');
//设置账户信息
account.put('name', 'OAAppAdmin');
account.put('account_name', 'OA@xxx.com.com');
account.put('account_type', 'com.android.exchange');
account.put('calendar_displayName', 'OA日历');
account.put('visible', 1);
account.put('calendar_color', '-9206951');
account.put('calendar_access_level', '700');
account.put('sync_events', 1);
account.put('calendar_timezone', TimeZone_str);
account.put('ownerAccount', 'OA@xxx.com.com');
account.put('canOrganizerRespond', 0);
//保存账户信息
plus.android.invoke(main.getContentResolver(), 'insert', plus.android.invoke(buildUpon, 'build'), account);
}
});
实现功能:
- 建立自己的账户,每次插入日历都使用自己的账户,区分第三方日历账户与系统日历账户
- 实现建立的日历删除
- 实现2次闹钟提醒,(addcalendar中的参数 fr, tr的值必须为分钟数字,比如:你的闹钟设置是1个小时提醒,输入的参数必须是60)
mui.plusReady(function() {
if(mui.os.ios) {
} else {
var calanderURL = 'content://com.android.calendar/calendars',
eventsURL = 'content://com.android.calendar/events',
ContentValues = plus.android.importClass("android.content.ContentValues"),
Uri = plus.android.importClass('android.net.Uri'),
ContentUris = plus.android.importClass('android.content.ContentUris'),
Calendar = plus.android.importClass('java.util.Calendar'),
main = plus.android.runtimeMainActivity(),
userCursor = plus.android.invoke(main.getContentResolver(), 'query', Uri.parse(calanderURL), null, null, null, null),
userCursor_count = plus.android.invoke(userCursor, 'getCount'),
TimeZone = plus.android.importClass('java.util.TimeZone'),
TimeZone_str = plus.android.invoke(TimeZone.getDefault(), 'getID');
}
//Nid参数为数据库中的一个id,可以不用,我用这个Nid实现与手机系统的日历事件id进行关联
addcalendar = function(Nid, title, description, date_str, etime, fr, tr) {
if(userCursor_count <= 0) { //如果没有日历账户
addcalendaraccount();
userCursor = plus.android.invoke(main.getContentResolver(), 'query', Uri.parse(calanderURL), null, null, null, null);
userCursor_count++;
}
plus.android.invoke(userCursor, 'moveToFirst');//第一个
//plus.android.invoke(userCursor, 'moveToLast');//最后一个
//plus.android.invoke(userCursor, 'moveToNext');//下一个
for(var i = 0; i < userCursor_count; i++) {
var calName = plus.android.invoke(userCursor, 'getString', plus.android.invoke(userCursor, 'getColumnIndex', 'calendar_displayName'));
//获得日历的描述名称
if(calName == "OA日历") {
break;
}
if(i == (userCursor_count - 1)) {
addcalendaraccount();
userCursor = plus.android.invoke(main.getContentResolver(), 'query', Uri.parse(calanderURL), null, null, null, null);
userCursor_count++;
}
plus.android.invoke(userCursor, 'moveToNext');
}
try {
var calId = plus.android.invoke(userCursor, 'getString', plus.android.invoke(userCursor, 'getColumnIndex', '_id')),
events = new ContentValues(),
mCalendar = Calendar.getInstance(),
date = date_str.split(/\s{1}|:|-/g);
plus.android.invoke(mCalendar, 'set', Calendar.YEAR, ~~date[0]);
plus.android.invoke(mCalendar, 'set', Calendar.MONTH, ((~~date[1]) - 1));
plus.android.invoke(mCalendar, 'set', Calendar.DATE, ~~date[2]);
plus.android.invoke(mCalendar, 'set', Calendar.HOUR_OF_DAY, ~~date[3]);
plus.android.invoke(mCalendar, 'set', Calendar.MINUTE, ~~date[4]);
var start = plus.android.invoke(plus.android.invoke(mCalendar, 'getTime'), 'getTime');
//设置日历事件
var etimestamp = new Date(etime.replace(/-/g, "\/")).getTime();//时间可以用传入参数的模式
events.put('title', title);
events.put('description', description);
events.put('calendar_id', calId);
events.put('dtstart', start);
events.put('dtend', etimestamp);
//events.put('ALL_DAY', 0); //值为 1 表示此事件占用一整天(按照本地时区的定义)。 值为 0 表示它是常规事件,可在一天内的任何时间开始和结束。
//events.put('RRULE')
events.put('hasAlarm', 1);
events.put('eventTimezone', TimeZone_str);
var newEvent = plus.android.invoke(main.getContentResolver(), 'insert', Uri.parse('content://com.android.calendar/events'), events);
var id = plus.android.invoke(newEvent, 'getLastPathSegment');
if(fr.length > 0) {
var values = new ContentValues();
values.put('event_id', id);
values.put('minutes', fr);
values.put('method', '1');
plus.android.invoke(main.getContentResolver(), 'insert', Uri.parse('content://com.android.calendar/reminders'), values);
}
if(tr.length > 0) {
var values2 = new ContentValues();
values2.put('event_id', id);
values2.put('minutes', tr);
values2.put('method', '1');
plus.android.invoke(main.getContentResolver(), 'insert', Uri.parse('content://com.android.calendar/reminders'), values2);
}
//SaveCalendID(id, Nid, callSetProRmData); 把插入日历的id保存起来,便于删除调用
//也可以用于更新(先删后插入);
} catch(e) {
mui.alert("您没有允许APP访问日历的权限");
}
}
deletecalendar = function(eventID) {
if(userCursor_count <= 0) { //如果没有日历账户
addcalendaraccount();
}
var id = Number(eventID);
//alert(id);
var deleteUri = ContentUris.withAppendedId(Uri.parse(eventsURL), id);
var rows = plus.android.invoke(main.getContentResolver(), "delete", deleteUri, null, null);
if(rows == -1) {
return;
}
}
addcalendaraccount = function() {
var account = new ContentValues(),
buildUpon = plus.android.invoke(Uri.parse(calanderURL), 'buildUpon'),
CalendarContract = plus.android.importClass('android.provider.CalendarContract');
plus.android.invoke(buildUpon, 'appendQueryParameter', CalendarContract.CALLER_IS_SYNCADAPTER, 'true');
plus.android.invoke(buildUpon, 'appendQueryParameter', 'account_name', 'OA@xxx.com.com');
plus.android.invoke(buildUpon, 'appendQueryParameter', 'account_type', 'com.android.exchange');
//设置账户信息
account.put('name', 'OAAppAdmin');
account.put('account_name', 'OA@xxx.com.com');
account.put('account_type', 'com.android.exchange');
account.put('calendar_displayName', 'OA日历');
account.put('visible', 1);
account.put('calendar_color', '-9206951');
account.put('calendar_access_level', '700');
account.put('sync_events', 1);
account.put('calendar_timezone', TimeZone_str);
account.put('ownerAccount', 'OA@xxx.com.com');
account.put('canOrganizerRespond', 0);
//保存账户信息
plus.android.invoke(main.getContentResolver(), 'insert', plus.android.invoke(buildUpon, 'build'), account);
}
});
收起阅读 »

关于uniapp中getphonenumber的问题
昨天遇到了uniapp在开发的时候,使用wx.getPhoneNumber报错的问题。在社区里找了很多帖子也没有具体解决办法,下面是今天我解决后的一些经验:
- 首先确保你在调用wx.getPhoneNumber这个之前,是已经做了wx.login()这个方法,在这里笔者是在onLoad的时候调用的。
- 其次一定是以button组件去调用<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
- 第三method中已写好getPhoneNumber函数。
- 最后确定你的小程序是否有权限调用getPhoneNumber函数,微信只对认证的小程序开放此类接口。
昨天遇到了uniapp在开发的时候,使用wx.getPhoneNumber报错的问题。在社区里找了很多帖子也没有具体解决办法,下面是今天我解决后的一些经验:
- 首先确保你在调用wx.getPhoneNumber这个之前,是已经做了wx.login()这个方法,在这里笔者是在onLoad的时候调用的。
- 其次一定是以button组件去调用<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
- 第三method中已写好getPhoneNumber函数。
- 最后确定你的小程序是否有权限调用getPhoneNumber函数,微信只对认证的小程序开放此类接口。

Mob短信验证码插件使用说明
欢迎使用Mob短信验证码插件
插件地址:
https://ext.dcloud.net.cn/plugin?id=399
MobSms实现了注册时短信验证,取回密码手机验证功能,使用此模块之前需要先去http://mob.com/ 注册获取MOBAppKey和MOBAppSecret,mob每天可以有10000条免费的短信,基本上可以算是免费的了
使用注意事项: 1、请在云端打包!请在云端打包!请在云端打包!(重要的事说三遍) 2、短信中的掌淘科技可以去除,具体请咨询mob官方 3、老给一个手机号发短信会导致失败率很高,这个是运营商为了防骚扰做的
使用Mob短信验证码插件
1. 配置插件[^code]
将相应的MOBAppKey和MOBAppSecret填写到APP原生插件配置里
1. 注册插件[^code]
const mobsms = uni.requireNativePlugin('HY-MobSms');
2. 发送验证码[^code]
// 请求验证码,其中country表示国家代码,如“86”;phone表示手机号码,如“13800138000”
//成功
mobsms.sendSms({country:'86',phone:this.phone},result=>{
console.log(JSON.stringify(result))
if(result.code==1){
uni.showToast({
title: "发送成功",
icon: "success"
})
}else{
uni.showToast({
title: "发送失败",
icon: "none"
})
//失败会返回错误码 //错误码对应查看地址:[http://wiki.mob.com/android-api-%E9%94%99%E8%AF%AF%E7%A0%81%E5%8F%82%E8%80%83/][3]
//const status= result.status
})
1. 验证验证码[^code]
mobsms.verify({country:'86',phone:this.mobile.phone,code:this.mobile.code},result=>{
console.log(JSON.stringify(result))
if(result.code==1){
uni.showToast({
title: "验证成功",
icon: "success"
})
}else{
uni.showToast({
title: "验证失败",
icon: "none"
})
//失败会返回错误码
//错误码对应查看地址:[http://wiki.mob.com/android-api-%E9%94%99%E8%AF%AF%E7%A0%81%E5%8F%82%E8%80%83/][4]
//const status= result.status
}
})
欢迎使用Mob短信验证码插件
插件地址:
https://ext.dcloud.net.cn/plugin?id=399
MobSms实现了注册时短信验证,取回密码手机验证功能,使用此模块之前需要先去http://mob.com/ 注册获取MOBAppKey和MOBAppSecret,mob每天可以有10000条免费的短信,基本上可以算是免费的了
使用注意事项: 1、请在云端打包!请在云端打包!请在云端打包!(重要的事说三遍) 2、短信中的掌淘科技可以去除,具体请咨询mob官方 3、老给一个手机号发短信会导致失败率很高,这个是运营商为了防骚扰做的
使用Mob短信验证码插件
1. 配置插件[^code]
将相应的MOBAppKey和MOBAppSecret填写到APP原生插件配置里
1. 注册插件[^code]
const mobsms = uni.requireNativePlugin('HY-MobSms');
2. 发送验证码[^code]
// 请求验证码,其中country表示国家代码,如“86”;phone表示手机号码,如“13800138000”
//成功
mobsms.sendSms({country:'86',phone:this.phone},result=>{
console.log(JSON.stringify(result))
if(result.code==1){
uni.showToast({
title: "发送成功",
icon: "success"
})
}else{
uni.showToast({
title: "发送失败",
icon: "none"
})
//失败会返回错误码 //错误码对应查看地址:[http://wiki.mob.com/android-api-%E9%94%99%E8%AF%AF%E7%A0%81%E5%8F%82%E8%80%83/][3]
//const status= result.status
})
1. 验证验证码[^code]
mobsms.verify({country:'86',phone:this.mobile.phone,code:this.mobile.code},result=>{
console.log(JSON.stringify(result))
if(result.code==1){
uni.showToast({
title: "验证成功",
icon: "success"
})
}else{
uni.showToast({
title: "验证失败",
icon: "none"
})
//失败会返回错误码
//错误码对应查看地址:[http://wiki.mob.com/android-api-%E9%94%99%E8%AF%AF%E7%A0%81%E5%8F%82%E8%80%83/][4]
//const status= result.status
}
})
收起阅读 »

HBuilderX代码提示系统说明
> 本帖文档已集成到: hx产品文档
HBuilderX的代码提示系统很庞大,支持多种语法提示模型。
内置语法库
- web项目有内置的html、js、css语法库
- App项目有内置的plus扩展语法库
- uni-app项目有内置的uni-app语法库
- 微信小程序、快应用等项目也有对应的内置语法库
js框架语法库(sdocml格式)
HBuilderX中,在可以输入js的文件,比如js、html等文件里,(不含vue、ts),底部状态栏有“语法提示库”,可以加载内置的框架语法库。
其中node.js也是作为一种框架语法而存在的。
勾选相应js框架语法后,js区域即可提示相应语法(初次勾选需要延时几秒后才能使用)
该选择是项目级的,一旦勾选后,整个项目下可以写js的地方都会加载。
如果文件是单独打开,没有在HBuilderX左侧的项目管理器中,则无法使用本功能。
如果HBuilderX能检测到项目下有jquery或mui等常用框架,也会自动给这个项目挂载语法提示库。但有时可能检测不准,需要开发者手动引入。
d.ts
很多框架都内置了d.ts语法提示库。HBuilderX完整支持d.ts的语法提示。
如果项目下有某个框架的d.ts文件,HBuilderX则可以提示这个框架的语法提示。
jsdoc+
jsdoc是以注释方式声明方法、参数、属性,HBuilderX提供了经过扩展的jsdoc+,可实现强大的语法提示,详见:https://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/129
vue doc
vue组件开发者,如果想给组件使用者提供更好的使用方式,应该给组件写vue doc。
vue doc是一种类似jsdoc的方式,通过在注释里描述组件的方法、参数、属性。
详见:https://ask.dcloud.net.cn/article/35814
兼容vscode vetur插件中的vue规范
一些vue的组件库,已经按照vetur规范制作语法提示库,比如Element UI、Onsen UI、Bootstrap Vue等框架。
这些框架npm安装是在node_module下会自带一个json语法库,或在HBuilderX新建模板中选择element ui模板安装也会包含该库。有了这个语法库,就可以直接代码提示。如下图
代码块
HBuilderX支持自定义代码块,在菜单工具-代码块设置中可自行扩展。
代码块数据格式兼容vscode,并扩展了更多丰富设置。对于提高开发效率帮助很大。
> 本帖文档已集成到: hx产品文档
HBuilderX的代码提示系统很庞大,支持多种语法提示模型。
内置语法库
- web项目有内置的html、js、css语法库
- App项目有内置的plus扩展语法库
- uni-app项目有内置的uni-app语法库
- 微信小程序、快应用等项目也有对应的内置语法库
js框架语法库(sdocml格式)
HBuilderX中,在可以输入js的文件,比如js、html等文件里,(不含vue、ts),底部状态栏有“语法提示库”,可以加载内置的框架语法库。
其中node.js也是作为一种框架语法而存在的。
勾选相应js框架语法后,js区域即可提示相应语法(初次勾选需要延时几秒后才能使用)
该选择是项目级的,一旦勾选后,整个项目下可以写js的地方都会加载。
如果文件是单独打开,没有在HBuilderX左侧的项目管理器中,则无法使用本功能。
如果HBuilderX能检测到项目下有jquery或mui等常用框架,也会自动给这个项目挂载语法提示库。但有时可能检测不准,需要开发者手动引入。
d.ts
很多框架都内置了d.ts语法提示库。HBuilderX完整支持d.ts的语法提示。
如果项目下有某个框架的d.ts文件,HBuilderX则可以提示这个框架的语法提示。
jsdoc+
jsdoc是以注释方式声明方法、参数、属性,HBuilderX提供了经过扩展的jsdoc+,可实现强大的语法提示,详见:https://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/129
vue doc
vue组件开发者,如果想给组件使用者提供更好的使用方式,应该给组件写vue doc。
vue doc是一种类似jsdoc的方式,通过在注释里描述组件的方法、参数、属性。
详见:https://ask.dcloud.net.cn/article/35814
兼容vscode vetur插件中的vue规范
一些vue的组件库,已经按照vetur规范制作语法提示库,比如Element UI、Onsen UI、Bootstrap Vue等框架。
这些框架npm安装是在node_module下会自带一个json语法库,或在HBuilderX新建模板中选择element ui模板安装也会包含该库。有了这个语法库,就可以直接代码提示。如下图
代码块
HBuilderX支持自定义代码块,在菜单工具-代码块设置中可自行扩展。
代码块数据格式兼容vscode,并扩展了更多丰富设置。对于提高开发效率帮助很大。