Android插件,TTS语音合成(文字转化语音)。
相关介绍
概念介绍
语音合成是实现人机语音交互,建立一个有听和讲能力的交互系统所必需的关键技术。随着语音技术的发展,百度自主研发了语音合成系统(TTS),功能是接受用户发送的文本,生成语音发送给用户。
对本文中将提到的概念约定如下:
语音合成:将文本合成为语音,即声音文件。
合成引擎:将文本合成为语音的核心模块。
TTS:Text To Speech,即“从文本到语音”。
BDTTSClient:语音合成 SDK 简称,详见下条。
语音合成 SDK:即本开发包,文中简称为 BDTTSClient。BDTTSClient 是一个封装了网络收发、音频播放功能的语音合成解决方案。借助 BDTTSClient 可以快速地在应用程序中集成语音合成功能。
百度语音
合成方式
1.离在线语音合成
2.在线语音合成(本文使用方式)
应用场景
我们团队的应用场景是在推送消息后,需要播放推送消息。
准备事项
1.下载Android 平台 HTML5+ SDK 。
2.注册百度开发者,创建应用,开通服务,请参考集成指南。
3.下载在线语音合成SDK_Android版。
实现步骤
1.先导入5+SDK就不多说了,参考Android平台第三方插件开发指导。
2.添加BDTTSClient到工程(将开发包中的 libs 目录整体拷贝到工程目录,libs 目录包括了各平台的 SO 库,开发者视应用需要可以进行删减。galaxy_lite.jar 是百度 Android 公共基础库。)
3.权限声明(需要在 AndroidManifest.xml 文件,增加以上三个权限)
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
4.集成java代码
import org.json.JSONArray;
import com.baidu.speechsynthesizer.SpeechSynthesizer;
import com.baidu.speechsynthesizer.SpeechSynthesizerListener;
import com.baidu.speechsynthesizer.publicutility.SpeechError;
import android.media.AudioManager;
import android.util.Log;
import io.dcloud.common.DHInterface.IWebview;
import io.dcloud.common.DHInterface.StandardFeature;
import io.dcloud.common.util.JSUtil;
public class baidutts extends StandardFeature implements SpeechSynthesizerListener{
private IWebview iwv ;
private String text;
private SpeechSynthesizer speechSynthesizer;
private static final String TAG = "zlz";
public void speak(IWebview pWebview, JSONArray array)
{
iwv = pWebview;
String CallBackID = array.optString(0);
JSONArray newArray = new JSONArray();
newArray.put(array.optString(1));
//前台传过来的文本信息
text = array.optString(1);
//Log.i(TAG, text);
initialTts();
JSUtil.execCallback(pWebview, CallBackID, newArray, JSUtil.OK, false);
}
//百度TTS初始化
private void initialTts() {
speechSynthesizer = new SpeechSynthesizer(iwv.getContext(),
"holder", this);
// 此处需要将setApiKey方法的两个参数替换为你在百度开发者中心注册应用所得到的apiKey和secretKey
speechSynthesizer.setApiKey("your-apiKey", "your-secretKey");
speechSynthesizer.setAudioStreamType(AudioManager.STREAM_MUSIC);
//setVolumeControlStream(AudioManager.STREAM_MUSIC);
new Thread(new Runnable() {
@Override
public void run() {
setParams();
int ret = speechSynthesizer.speak(text);
if (ret != 0) {
Log.i(TAG,"开始合成器失败" );
}
}
}).start();
}
private void setParams() {
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5");
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_ENCODE, SpeechSynthesizer.AUDIO_ENCODE_AMR);
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_RATE, SpeechSynthesizer.AUDIO_BITRATE_AMR_15K85);
}
@Override
public void onBufferProgressChanged(SpeechSynthesizer arg0, int arg1) {}
@Override
public void onCancel(SpeechSynthesizer arg0) {}
@Override
public void onError(SpeechSynthesizer arg0, SpeechError arg1) {}
@Override
public void onNewDataArrive(SpeechSynthesizer arg0, byte[] arg1, boolean arg2) {}
@Override
public void onSpeechFinish(SpeechSynthesizer arg0) {}
@Override
public void onSpeechPause(SpeechSynthesizer arg0) {}
@Override
public void onSpeechProgressChanged(SpeechSynthesizer arg0, int arg1) {}
@Override
public void onSpeechResume(SpeechSynthesizer arg0) {}
@Override
public void onSpeechStart(SpeechSynthesizer arg0) {}
@Override
public void onStartWorking(SpeechSynthesizer arg0) {}
@Override
public void onSynthesizeFinish(SpeechSynthesizer arg0) {}
}
5.前台javascript代码
document.addEventListener( "plusready", function()
{
var _BARCODE = 'kxdPlugins',
B = window.plus.bridge;
var kxdPlugins =
{
// 声明异步返回方法
speak : function (Argus, successCallback, errorCallback )
{
var success = typeof successCallback !== 'function' ? null : function(args)
{
successCallback(args);
},
fail = typeof errorCallback !== 'function' ? null : function(code)
{
errorCallback(code);
};
callbackID = B.callbackId(success, fail);
// 通知Native层plugintest扩展插件运行”speak”方法
return B.exec(_BARCODE, "speak", [callbackID, Argus]);
}
};
window.plus.kxdPlugins = kxdPlugins;
}, true );
使用方式
和普通的API调用方式一样:plus.kxdPlugins.speak("这是语音播放的文字内容");
注意事项
1.在/assets/data/properties.xml中添加插件权限。
2.在/assets/apps/appid/www/manifest.json 中添加对应插件权限。
3.在/AndroidManifest.xml 文件中添加对应的百度语音合成权限。
相关介绍
概念介绍
语音合成是实现人机语音交互,建立一个有听和讲能力的交互系统所必需的关键技术。随着语音技术的发展,百度自主研发了语音合成系统(TTS),功能是接受用户发送的文本,生成语音发送给用户。
对本文中将提到的概念约定如下:
语音合成:将文本合成为语音,即声音文件。
合成引擎:将文本合成为语音的核心模块。
TTS:Text To Speech,即“从文本到语音”。
BDTTSClient:语音合成 SDK 简称,详见下条。
语音合成 SDK:即本开发包,文中简称为 BDTTSClient。BDTTSClient 是一个封装了网络收发、音频播放功能的语音合成解决方案。借助 BDTTSClient 可以快速地在应用程序中集成语音合成功能。
百度语音
合成方式
1.离在线语音合成
2.在线语音合成(本文使用方式)
应用场景
我们团队的应用场景是在推送消息后,需要播放推送消息。
准备事项
1.下载Android 平台 HTML5+ SDK 。
2.注册百度开发者,创建应用,开通服务,请参考集成指南。
3.下载在线语音合成SDK_Android版。
实现步骤
1.先导入5+SDK就不多说了,参考Android平台第三方插件开发指导。
2.添加BDTTSClient到工程(将开发包中的 libs 目录整体拷贝到工程目录,libs 目录包括了各平台的 SO 库,开发者视应用需要可以进行删减。galaxy_lite.jar 是百度 Android 公共基础库。)
3.权限声明(需要在 AndroidManifest.xml 文件,增加以上三个权限)
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
4.集成java代码
import org.json.JSONArray;
import com.baidu.speechsynthesizer.SpeechSynthesizer;
import com.baidu.speechsynthesizer.SpeechSynthesizerListener;
import com.baidu.speechsynthesizer.publicutility.SpeechError;
import android.media.AudioManager;
import android.util.Log;
import io.dcloud.common.DHInterface.IWebview;
import io.dcloud.common.DHInterface.StandardFeature;
import io.dcloud.common.util.JSUtil;
public class baidutts extends StandardFeature implements SpeechSynthesizerListener{
private IWebview iwv ;
private String text;
private SpeechSynthesizer speechSynthesizer;
private static final String TAG = "zlz";
public void speak(IWebview pWebview, JSONArray array)
{
iwv = pWebview;
String CallBackID = array.optString(0);
JSONArray newArray = new JSONArray();
newArray.put(array.optString(1));
//前台传过来的文本信息
text = array.optString(1);
//Log.i(TAG, text);
initialTts();
JSUtil.execCallback(pWebview, CallBackID, newArray, JSUtil.OK, false);
}
//百度TTS初始化
private void initialTts() {
speechSynthesizer = new SpeechSynthesizer(iwv.getContext(),
"holder", this);
// 此处需要将setApiKey方法的两个参数替换为你在百度开发者中心注册应用所得到的apiKey和secretKey
speechSynthesizer.setApiKey("your-apiKey", "your-secretKey");
speechSynthesizer.setAudioStreamType(AudioManager.STREAM_MUSIC);
//setVolumeControlStream(AudioManager.STREAM_MUSIC);
new Thread(new Runnable() {
@Override
public void run() {
setParams();
int ret = speechSynthesizer.speak(text);
if (ret != 0) {
Log.i(TAG,"开始合成器失败" );
}
}
}).start();
}
private void setParams() {
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5");
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_ENCODE, SpeechSynthesizer.AUDIO_ENCODE_AMR);
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_RATE, SpeechSynthesizer.AUDIO_BITRATE_AMR_15K85);
}
@Override
public void onBufferProgressChanged(SpeechSynthesizer arg0, int arg1) {}
@Override
public void onCancel(SpeechSynthesizer arg0) {}
@Override
public void onError(SpeechSynthesizer arg0, SpeechError arg1) {}
@Override
public void onNewDataArrive(SpeechSynthesizer arg0, byte[] arg1, boolean arg2) {}
@Override
public void onSpeechFinish(SpeechSynthesizer arg0) {}
@Override
public void onSpeechPause(SpeechSynthesizer arg0) {}
@Override
public void onSpeechProgressChanged(SpeechSynthesizer arg0, int arg1) {}
@Override
public void onSpeechResume(SpeechSynthesizer arg0) {}
@Override
public void onSpeechStart(SpeechSynthesizer arg0) {}
@Override
public void onStartWorking(SpeechSynthesizer arg0) {}
@Override
public void onSynthesizeFinish(SpeechSynthesizer arg0) {}
}
5.前台javascript代码
document.addEventListener( "plusready", function()
{
var _BARCODE = 'kxdPlugins',
B = window.plus.bridge;
var kxdPlugins =
{
// 声明异步返回方法
speak : function (Argus, successCallback, errorCallback )
{
var success = typeof successCallback !== 'function' ? null : function(args)
{
successCallback(args);
},
fail = typeof errorCallback !== 'function' ? null : function(code)
{
errorCallback(code);
};
callbackID = B.callbackId(success, fail);
// 通知Native层plugintest扩展插件运行”speak”方法
return B.exec(_BARCODE, "speak", [callbackID, Argus]);
}
};
window.plus.kxdPlugins = kxdPlugins;
}, true );
使用方式
和普通的API调用方式一样:plus.kxdPlugins.speak("这是语音播放的文字内容");
注意事项
1.在/assets/data/properties.xml中添加插件权限。
2.在/assets/apps/appid/www/manifest.json 中添加对应插件权限。
3.在/AndroidManifest.xml 文件中添加对应的百度语音合成权限。
获取图片当前旋转状态
今日开发过程中遇见从相册选择或者自拍照片,显示时为横向。故封装如下代码,希望对大家有所帮助。
function getImgOrientation (filePath) {
var ExifInterface = plus.android.importClass("android.media.ExifInterface");
var exifInterface = new ExifInterface(filePath.replace("file:///", ""));
var orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
var degree = 0;
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
return degree;
} 今日开发过程中遇见从相册选择或者自拍照片,显示时为横向。故封装如下代码,希望对大家有所帮助。
function getImgOrientation (filePath) {
var ExifInterface = plus.android.importClass("android.media.ExifInterface");
var exifInterface = new ExifInterface(filePath.replace("file:///", ""));
var orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
var degree = 0;
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
return degree;
} 收起阅读 »
整包(apk/ipa)升级
官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见
5+应用可使用以下方式进行升级
- 整包升级:适用于大版本更新,runtime发生变化时(模块、配置、版本等变化)必须使用此更新方法
- 应用资源升级:适用于小版本更新 。runtime不变,前端页面整体压缩包更新
- 应用资源差量升级:适用于小版本更新 。runtime不变,前端页面仅需要更新的部分更新。
本文重点描述5+应用整包升级,逻辑其实是非常简单,主要有三个步骤:
- 查询是否有新版本更新
- 下载新版本
- 安装新版本
而在实际应用的升级过程中则需要处理很多细节问题,下面就按照这个流程来简单说明在5+App中实现应用升级的逻辑。
查询是否有新版本更新
这个操作其实非常简单,就是客户端与升级服务器的一次交互操作,比较升级服务器上发布的最新客户端版本是否高于当前客户端版本号(5+ API中可以通过plus.runtime.version获取当前apk/ipa的版本号,注意打包方生效)?是的话则需要升级,否则无需升级。
从逻辑上来考虑有两种判断模式:
- 客户端判断是否有升级
客户端从服务器获取最新的版本号,本地js判断是否需要升级。 - 服务器判断是否有升级
客户端提交版本到服务器,有服务器判断返回是否需要升级。
前者的优点是否服务器压力小,静态返回最新客户端版本即可,后者的优点则升级控制会更灵活,可以根据其它条件动态控制部分用户先升级(灰度发布)等。有条件的情况推荐采用第二种方式进行判断。
可以使用Javascript中的标准XHR请求,如果存在跨域问题则使用5+ API的XMLHttpRequest。
下载新版本
如果判断到需要更新版本,则需要从服务器下载新版本,通常升级服务器应该返回下载新版本的地址(或者从固定的地址获取)。
有两种下载方法,一种调用Downloader API下载,示例如下:
var url=""; // 下载文件地址
var dtask = plus.downloader.createDownload( url, {}, function ( d, status ) {
if ( status == 200 ) { // 下载成功
var path = d.filename;
console.log(d.filename);
} else {//下载失败
alert( "Download failed: " + status );
}
});
dtask.start();
安装新版本
下载原生安装包apk后,可调用[plus.runtime.install]()方法安装,示例如下:
plus.runtime.install(path); // 安装下载的apk文件
注意
iOS平台的ipa无法安装,此时需要跳转到appstore,提示用户自动点击升级更新,跳转到appstore的方法为打开应用的appstore地址,示例如下:
var url='itms-apps://itunes.apple.com/cn/app/hello-h5+/id682211190?l=zh&mt=8';// HelloH5应用在appstore的地址
plus.runtime.openURL(url);
此处url是以"itms-apps://"开头,后面跟appstore上应用地址。
示例
在Hello H5+和Hello mui示例里,有2种不同的检查更新处理方式。
Hello H5+里的update.js比较复杂,在js里执行下载apk并安装的逻辑。
Hello mui里的update.js比较简单,弹出新包下载地址到浏览器,由浏览器执行下载逻辑。
如何选择看自己的要求。
官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见
5+应用可使用以下方式进行升级
- 整包升级:适用于大版本更新,runtime发生变化时(模块、配置、版本等变化)必须使用此更新方法
- 应用资源升级:适用于小版本更新 。runtime不变,前端页面整体压缩包更新
- 应用资源差量升级:适用于小版本更新 。runtime不变,前端页面仅需要更新的部分更新。
本文重点描述5+应用整包升级,逻辑其实是非常简单,主要有三个步骤:
- 查询是否有新版本更新
- 下载新版本
- 安装新版本
而在实际应用的升级过程中则需要处理很多细节问题,下面就按照这个流程来简单说明在5+App中实现应用升级的逻辑。
查询是否有新版本更新
这个操作其实非常简单,就是客户端与升级服务器的一次交互操作,比较升级服务器上发布的最新客户端版本是否高于当前客户端版本号(5+ API中可以通过plus.runtime.version获取当前apk/ipa的版本号,注意打包方生效)?是的话则需要升级,否则无需升级。
从逻辑上来考虑有两种判断模式:
- 客户端判断是否有升级
客户端从服务器获取最新的版本号,本地js判断是否需要升级。 - 服务器判断是否有升级
客户端提交版本到服务器,有服务器判断返回是否需要升级。
前者的优点是否服务器压力小,静态返回最新客户端版本即可,后者的优点则升级控制会更灵活,可以根据其它条件动态控制部分用户先升级(灰度发布)等。有条件的情况推荐采用第二种方式进行判断。
可以使用Javascript中的标准XHR请求,如果存在跨域问题则使用5+ API的XMLHttpRequest。
下载新版本
如果判断到需要更新版本,则需要从服务器下载新版本,通常升级服务器应该返回下载新版本的地址(或者从固定的地址获取)。
有两种下载方法,一种调用Downloader API下载,示例如下:
var url=""; // 下载文件地址
var dtask = plus.downloader.createDownload( url, {}, function ( d, status ) {
if ( status == 200 ) { // 下载成功
var path = d.filename;
console.log(d.filename);
} else {//下载失败
alert( "Download failed: " + status );
}
});
dtask.start();
安装新版本
下载原生安装包apk后,可调用[plus.runtime.install]()方法安装,示例如下:
plus.runtime.install(path); // 安装下载的apk文件
注意
iOS平台的ipa无法安装,此时需要跳转到appstore,提示用户自动点击升级更新,跳转到appstore的方法为打开应用的appstore地址,示例如下:
var url='itms-apps://itunes.apple.com/cn/app/hello-h5+/id682211190?l=zh&mt=8';// HelloH5应用在appstore的地址
plus.runtime.openURL(url);
此处url是以"itms-apps://"开头,后面跟appstore上应用地址。
示例
在Hello H5+和Hello mui示例里,有2种不同的检查更新处理方式。
Hello H5+里的update.js比较复杂,在js里执行下载apk并安装的逻辑。
Hello mui里的update.js比较简单,弹出新包下载地址到浏览器,由浏览器执行下载逻辑。
如何选择看自己的要求。
如何在本地进行微信公众号的开发调试
在进行微信公众号开发时,以前必须要有外网域名才能收到微信服务器的推送,这给开发和调试带来很大的不便。
现在方便了,QQ浏览器提供了微信公众号调试工具,开发者可以在自己的开发机上进行开发和调试,不再需要外网IP和域名。不仅可以直观看到已接收和已发送的消息内容和事件,方便开发和问题定位,还可以重复发送已接收的微信报文,避免调试时频繁操作手机
原理
调试工具内置了一个server,监听你本地运行的程序,并为你生成一个外网的url,你只需要在公众号的开发信息中配置上这个url,公众号收到消息后,就会通过这个url转发到调试工具上,调试工具再转给你的本地运行程序。
使用方法
(1)在本机启动运行自己程序的server
(2)打开qq浏览器,在应用中心中安装“微信调试工具”,安装完成后,选择“服务器端调试”
(3)填写本地server的ip和端口号,调试工具会返回一个外部URL
(4)到公众号后台开发者配置信息中填写这个URL
(5)用微信向公众号发送消息测试
在进行微信公众号开发时,以前必须要有外网域名才能收到微信服务器的推送,这给开发和调试带来很大的不便。
现在方便了,QQ浏览器提供了微信公众号调试工具,开发者可以在自己的开发机上进行开发和调试,不再需要外网IP和域名。不仅可以直观看到已接收和已发送的消息内容和事件,方便开发和问题定位,还可以重复发送已接收的微信报文,避免调试时频繁操作手机
原理
调试工具内置了一个server,监听你本地运行的程序,并为你生成一个外网的url,你只需要在公众号的开发信息中配置上这个url,公众号收到消息后,就会通过这个url转发到调试工具上,调试工具再转给你的本地运行程序。
使用方法
(1)在本机启动运行自己程序的server
(2)打开qq浏览器,在应用中心中安装“微信调试工具”,安装完成后,选择“服务器端调试”
(3)填写本地server的ip和端口号,调试工具会返回一个外部URL
(4)到公众号后台开发者配置信息中填写这个URL
(5)用微信向公众号发送消息测试
IOS平台:如何在离线打包时配置 3D Touch图标快捷菜单
Apple最新发布的iPhone 6s (plus) 设备支持3D touch功能,用户可通过重按屏幕上的图标,打开设置的菜单。H5+ SDK已经支持添加应用的快捷菜单,显示效果如下:
离线工程配置的方法
打开离线打包工程的info.plist文件,在文件中添加UIApplicationShortcutItems节点,并在节点下添加item。
| 各字段值规范如下表 各值的关系请参考上图 |
节点名 | 节点类型 | 可选性 | 节点说明 | 备注 |
|---|---|---|---|---|---|
| UIApplicationShortcutItems | Array | 必选 | 图标快捷菜单节点声明 | ||
| UIApplicationShortcutItemType | String | 必选 | 菜单类型 | ||
| UIApplicationShortcutItemTitle | String | 必选 | 菜单项的标题 | ||
| UIApplicationShortcutItemSubtitle | String | 可选 | 菜单项的副标题 | ||
| UIApplicationShortcutItemIconFile | String | 可选 | 指定菜单项显示安装包内自带的图标,相对安装包的根目录位置 | 与UIApplicationShortcutItemIconType节点同时配置时优先级高于UIApplicationShortcutItemIconType | |
| UIApplicationShortcutItemIconType | String | 可选 | 指定菜单项显示系统自带图标 | 取值可参考苹果官方文档 | |
| UIApplicationShortcutItemUserInfo | String | 可选 | 用户自定义的菜单项附加信息 | 附带信息可在应用内通过plus.runtime.arguments获取 |
App中处理快捷菜单项
请参考文档iOS平台支持3D Touch快捷菜单项
Apple最新发布的iPhone 6s (plus) 设备支持3D touch功能,用户可通过重按屏幕上的图标,打开设置的菜单。H5+ SDK已经支持添加应用的快捷菜单,显示效果如下:
离线工程配置的方法
打开离线打包工程的info.plist文件,在文件中添加UIApplicationShortcutItems节点,并在节点下添加item。
| 各字段值规范如下表 各值的关系请参考上图 |
节点名 | 节点类型 | 可选性 | 节点说明 | 备注 |
|---|---|---|---|---|---|
| UIApplicationShortcutItems | Array | 必选 | 图标快捷菜单节点声明 | ||
| UIApplicationShortcutItemType | String | 必选 | 菜单类型 | ||
| UIApplicationShortcutItemTitle | String | 必选 | 菜单项的标题 | ||
| UIApplicationShortcutItemSubtitle | String | 可选 | 菜单项的副标题 | ||
| UIApplicationShortcutItemIconFile | String | 可选 | 指定菜单项显示安装包内自带的图标,相对安装包的根目录位置 | 与UIApplicationShortcutItemIconType节点同时配置时优先级高于UIApplicationShortcutItemIconType | |
| UIApplicationShortcutItemIconType | String | 可选 | 指定菜单项显示系统自带图标 | 取值可参考苹果官方文档 | |
| UIApplicationShortcutItemUserInfo | String | 可选 | 用户自定义的菜单项附加信息 | 附带信息可在应用内通过plus.runtime.arguments获取 |
App中处理快捷菜单项
请参考文档iOS平台支持3D Touch快捷菜单项
收起阅读 »iOS平台支持3D Touch快捷菜单项
iPhone6s(plus)设备已经支持3D Touch屏幕,HBuilder已支持添加应用的快捷菜单,效果如下:
配置快捷菜单项
打开应用的manifest.json文件,切换到代码视图,在plus -> distribute -> apple 下添加shortcuts节点,并配置各菜单项,数组中每项对应一个快捷菜单项:
"shortcuts": [
{
"type": "share",
"title": "分 享",
"subtitle": "分享到微信、微博、QQ",
"icontype": "UIApplicationShortcutIconTypeShare"
},
{
"type": "about",
"title": "关 于",
"subtitle": "www.dcloud.io",
"iconfile": "sa.png",
"userinfo": {
"key3":"value3"
}
}
],
uni-app项目将以上数据添加到"app-plus"->"distribute"->"ios"节点下
其中各字段值规范如下:
- type: (必选)菜单项类型,字符串类型,用于标识菜单项
- title: (必选)菜单项上显示的标题,字符串类型
- subtitle: (可选)菜单项上显示的子标题,字符串类型
- icontype: (可选)菜单项上显示的图标类型,字符串类型,取值参考iOS官方文档UIApplicationShortcutIconType
- iconfile: (可选)菜单项上显示的图标文件,字符串类型,相对5+应用根目录路径,图标要求35x35分辨率,单色,参考Apple官方PS模板图
- userinfo: (可选)菜单项上的自定义数据,JSON格式
App中处理快捷菜单项
判断应用是否通过快捷菜单项启动
plus.runtime.launcher的值为"shortcut"表示应用从快捷菜单项启动,示例如下:
if(plus.runtime.launcher=='shortcut'){
// ...
}
判断快捷菜单项启动的参数
plus.runtime.arguments中保存所有快捷菜单项配置的值(JSON格式字符串),示例如下:
var cmd = JSON.parse(plus.runtime.arguments);
console.log("Shortcut-plus.runtime.arguments: "+plus.runtime.arguments)
var type=cmd&&cmd.type;
switch(type){
case 'share':
// 用户点击了‘share'菜单项
break;
case 'about':
// 用户点击了’about'菜单项
break;
default:
break;
}
plus.runtime.arguments的JSON格式字符串示例如下:
“{"type":"about","title":"关 于","subtitle": "www.dcloud.io","userinfo":{"key3":"value3"}}”
其中包括以下键值:
- type: 菜单项类型,字符串类型
- title: 菜单项上显示的标题,字符串类型
- subtitle: 菜单项上显示的子标题,字符串类型
- userinfo: 菜单项上的自定义数据
注:真机运行不生效,需提交App云端打包后才生效
实际用法参考HelloH5应用的“js/shortcut.js”
iOS平台5+SDK原生环境配置方法
uni-app网友经验分享:https://ask.dcloud.net.cn/article/36103
iPhone6s(plus)设备已经支持3D Touch屏幕,HBuilder已支持添加应用的快捷菜单,效果如下:
配置快捷菜单项
打开应用的manifest.json文件,切换到代码视图,在plus -> distribute -> apple 下添加shortcuts节点,并配置各菜单项,数组中每项对应一个快捷菜单项:
"shortcuts": [
{
"type": "share",
"title": "分 享",
"subtitle": "分享到微信、微博、QQ",
"icontype": "UIApplicationShortcutIconTypeShare"
},
{
"type": "about",
"title": "关 于",
"subtitle": "www.dcloud.io",
"iconfile": "sa.png",
"userinfo": {
"key3":"value3"
}
}
],
uni-app项目将以上数据添加到"app-plus"->"distribute"->"ios"节点下
其中各字段值规范如下:
- type: (必选)菜单项类型,字符串类型,用于标识菜单项
- title: (必选)菜单项上显示的标题,字符串类型
- subtitle: (可选)菜单项上显示的子标题,字符串类型
- icontype: (可选)菜单项上显示的图标类型,字符串类型,取值参考iOS官方文档UIApplicationShortcutIconType
- iconfile: (可选)菜单项上显示的图标文件,字符串类型,相对5+应用根目录路径,图标要求35x35分辨率,单色,参考Apple官方PS模板图
- userinfo: (可选)菜单项上的自定义数据,JSON格式
App中处理快捷菜单项
判断应用是否通过快捷菜单项启动
plus.runtime.launcher的值为"shortcut"表示应用从快捷菜单项启动,示例如下:
if(plus.runtime.launcher=='shortcut'){
// ...
}
判断快捷菜单项启动的参数
plus.runtime.arguments中保存所有快捷菜单项配置的值(JSON格式字符串),示例如下:
var cmd = JSON.parse(plus.runtime.arguments);
console.log("Shortcut-plus.runtime.arguments: "+plus.runtime.arguments)
var type=cmd&&cmd.type;
switch(type){
case 'share':
// 用户点击了‘share'菜单项
break;
case 'about':
// 用户点击了’about'菜单项
break;
default:
break;
}
plus.runtime.arguments的JSON格式字符串示例如下:
“{"type":"about","title":"关 于","subtitle": "www.dcloud.io","userinfo":{"key3":"value3"}}”
其中包括以下键值:
- type: 菜单项类型,字符串类型
- title: 菜单项上显示的标题,字符串类型
- subtitle: 菜单项上显示的子标题,字符串类型
- userinfo: 菜单项上的自定义数据
注:真机运行不生效,需提交App云端打包后才生效
实际用法参考HelloH5应用的“js/shortcut.js”
iOS平台5+SDK原生环境配置方法
uni-app网友经验分享:https://ask.dcloud.net.cn/article/36103
收起阅读 »如何动态兼容沉浸式状态栏模式
HBuilder6.6.1版本已经完全支持沉浸式状态栏,可以下载最新版本HelloH5应用体验。
各平台配置参考:
由于各系统版本的限制,沉浸式状态栏对系统有要求(Android4.4及以上、iOS7.0及以上),如果要兼容各系统版本,需要动态判断当前环境是否支持沉浸式状态栏以及系统状态栏的高度:
使用5+API
- 判断当前环境是否支持沉浸式状态栏
plus.navigator.isImmersedStatusbar()
如果当前支持沉浸式状态栏则返回true,否则返回false。 - 获取当前系统状态栏高度
plus.navigator.getStatusbarHeight()
获取系统状态栏高度,Number类型。
其单位是逻辑像素值,即css中可直接使用的像素值,可能存在小数点。
实际用法参考HelloH5应用的“plus/doc.html”:
// 创建加载内容窗口
var topoffset='45px';
if(plus.navigator.isImmersedStatusbar()){// 兼容immersed状态栏模式
// 获取状态栏高度并根据业务需求处理,这里重新计算了子窗口的偏移位置
topoffset=(Math.round(plus.navigator.getStatusbarHeight())+45)+'px';
}
// 使用偏移位置创建子窗口
wc=plus.webview.create(null,'doccontent',{top:topoffset,bottom:'0px',bounce:'vertical',bounceBackground:'#FFFFFF'});
通过userAgent判断
5+API需要在plusready事件后才能调用,通常此事件在DOM加载渲染后才会触发,无法再渲染前根据不同的状态来设置css。
为了解决此问题,在支持5+API运行环境的userAgent中特定字段Html5Plus/1.0后添加Immersed标识,如下:
"Html5Plus/1.0 (Immersed/30)"
其中Immersed/后的30表示状态栏的高度,单位为逻辑像素值。
可以使用正则表达式进行获取:
var immersed = 0;
var ms=(/Html5Plus\/.+\s\(.*(Immersed\/(\d+\.?\d*).*)\)/gi).exec(navigator.userAgent);
if(ms&&ms.length>=3){ // 当前环境为沉浸式状态栏模式
immersed=parseFloat(ms[2]);// 获取状态栏的高度
}
immersed值如果大于0则表示当前环境支持沉浸式状态栏。
获取状态栏高度后,可以使用js动态修改DOM元素的css属性来设置样式,如设置界面头区域的顶部内边距为状态栏的高度(避免系统状态栏与界面头重叠),示例如下:
var t=document.getElementById('header');
t&&t.style.paddingTop=immersed+'px';
具体项目中可根据界面设计,灵活使用immersed值来动态适配各种效果。
完整用法可参考HelloH5应用中的“js/immersed.js”
HBuilder6.6.1版本已经完全支持沉浸式状态栏,可以下载最新版本HelloH5应用体验。
各平台配置参考:
由于各系统版本的限制,沉浸式状态栏对系统有要求(Android4.4及以上、iOS7.0及以上),如果要兼容各系统版本,需要动态判断当前环境是否支持沉浸式状态栏以及系统状态栏的高度:
使用5+API
- 判断当前环境是否支持沉浸式状态栏
plus.navigator.isImmersedStatusbar()
如果当前支持沉浸式状态栏则返回true,否则返回false。 - 获取当前系统状态栏高度
plus.navigator.getStatusbarHeight()
获取系统状态栏高度,Number类型。
其单位是逻辑像素值,即css中可直接使用的像素值,可能存在小数点。
实际用法参考HelloH5应用的“plus/doc.html”:
// 创建加载内容窗口
var topoffset='45px';
if(plus.navigator.isImmersedStatusbar()){// 兼容immersed状态栏模式
// 获取状态栏高度并根据业务需求处理,这里重新计算了子窗口的偏移位置
topoffset=(Math.round(plus.navigator.getStatusbarHeight())+45)+'px';
}
// 使用偏移位置创建子窗口
wc=plus.webview.create(null,'doccontent',{top:topoffset,bottom:'0px',bounce:'vertical',bounceBackground:'#FFFFFF'});
通过userAgent判断
5+API需要在plusready事件后才能调用,通常此事件在DOM加载渲染后才会触发,无法再渲染前根据不同的状态来设置css。
为了解决此问题,在支持5+API运行环境的userAgent中特定字段Html5Plus/1.0后添加Immersed标识,如下:
"Html5Plus/1.0 (Immersed/30)"
其中Immersed/后的30表示状态栏的高度,单位为逻辑像素值。
可以使用正则表达式进行获取:
var immersed = 0;
var ms=(/Html5Plus\/.+\s\(.*(Immersed\/(\d+\.?\d*).*)\)/gi).exec(navigator.userAgent);
if(ms&&ms.length>=3){ // 当前环境为沉浸式状态栏模式
immersed=parseFloat(ms[2]);// 获取状态栏的高度
}
immersed值如果大于0则表示当前环境支持沉浸式状态栏。
获取状态栏高度后,可以使用js动态修改DOM元素的css属性来设置样式,如设置界面头区域的顶部内边距为状态栏的高度(避免系统状态栏与界面头重叠),示例如下:
var t=document.getElementById('header');
t&&t.style.paddingTop=immersed+'px';
具体项目中可根据界面设计,灵活使用immersed值来动态适配各种效果。
完整用法可参考HelloH5应用中的“js/immersed.js”
Android平台设置沉浸式状态栏显示效果
系列文章导航:
状态栏大全-状态栏透明(沉浸式)、变色及全屏的区别
Android平台设置沉浸式状态栏显示效果
iOS平台设置沉浸式状态栏显示效果
正文:
应用可视区域到系统状态栏下透明显示效果,如下图所示:

此模式下应用占用全屏区域,而系统状态栏会拦截用户操作事件,此时需要预留出系统状态栏高度。
获取系统状态栏高度及沉浸式状态判断参考:如何动态判断沉浸式状态栏模式。
HBuilder创建的应用默认不使用沉浸式状态栏样式,需要进行如下配置开启:
打开应用的manifest.json文件,切换到代码视图,在plus -> statusbar 下添加immersed节点并设置值为true。
"plus": {
"statusbar": {
"immersed": true
}
}
如下图所示:
保存后提交App云端打包
注意:
- 真机运行不生效,需提交App云端打包后才生效;
- 此功能仅在Android4.4及以上系统有效。
状态栏大全-状态栏透明(沉浸式)、变色及全屏的区别
Android平台5+SDK原生环境配置方法
iOS平台设置沉浸式状态栏显示效果
系列文章导航:
状态栏大全-状态栏透明(沉浸式)、变色及全屏的区别
Android平台设置沉浸式状态栏显示效果
iOS平台设置沉浸式状态栏显示效果
正文:
应用可视区域到系统状态栏下透明显示效果,如下图所示:

此模式下应用占用全屏区域,而系统状态栏会拦截用户操作事件,此时需要预留出系统状态栏高度。
获取系统状态栏高度及沉浸式状态判断参考:如何动态判断沉浸式状态栏模式。
HBuilder创建的应用默认不使用沉浸式状态栏样式,需要进行如下配置开启:
打开应用的manifest.json文件,切换到代码视图,在plus -> statusbar 下添加immersed节点并设置值为true。
"plus": {
"statusbar": {
"immersed": true
}
}
如下图所示:
保存后提交App云端打包
注意:
- 真机运行不生效,需提交App云端打包后才生效;
- 此功能仅在Android4.4及以上系统有效。
状态栏大全-状态栏透明(沉浸式)、变色及全屏的区别
Android平台5+SDK原生环境配置方法
iOS平台设置沉浸式状态栏显示效果
怎样选择微信外包机构
在互联网的浪潮下,不少传统公司都纷纷开通了自己的微信公众平台,因此微信公众平台的二次开发也如火如荼,那么请务必记住以下这几条,以后有做微信外包的机构找你时,就拿这几条去观察,虽然无法保证你会赚钱,但至少能保证你不会被坑蒙拐骗了还帮流氓数钱。
首先查看他们做过哪些案例,这是最容易想到的,但要从几个步骤入手。
1、案例究竟是不是他们做的,这个可以要求把牛逼吹上天的微信外包机构给个甲方的联系方式,要座机哦,去调查,问问,那个外包机构是不是他们的年度服务商,所讲的案例的数字是不是真的,例如一周涨了50万微信粉丝,微信上一个月流水过百万,是不是这个外包机构干出来的。要是说对方负责人休假电话不方便给啊,对方联系人已经离职啊啥的,这些借口别听,可以送客了。
顺道给大家普及个常识,大品牌公司在选择年度服务商的时候会通过比稿的方式来选择外包机构,比稿通过的,成为年度服务商,比稿不通过的,会拿到比稿费,他们比稿里所陈述的方案会被选上的外包机构拿来用。
因此,不少外包服务机构会到处吹嘘某公司是他们服务的,并在网站上挂着一大堆牛逼品牌的LOGO,其中90%以上都只是跟他们的工作人员见过一面,或者是外包产业链里最低端的分包商而已,例如很多没见过世面的公司,经常会因此而激动地向别人吹嘘那谁谁谁是他们服务过的客户,事实上嘛,前面告诉过你啦。
2、他们所说的案例做得怎样,既然是案例,肯定有微信公众号,上去看看他们公众号的内容做得如何,微博上给微信拉粉的素材创作水平,天天都有促销信息、配图难看、行文如嚼蜡、样式死板、最近还顶风作案诱导用户把内容分享到朋友圈的,你想被封号就找他们吧。
3、看名声和网络形象
诚信的重要性就不用多说了,用人德为先,寻找靠谱的微信外包机构同样如此。除了向同行打听这家公司的专业度和诚信度,或者到百度上搜索这家公司的名字,看看这个公司是不是在自己的百度百科词条下面紧跟着就是一堆离职员工的讨薪控诉,或者一些企划经理的帖子骂他们狗屎傻逼饭桶。
最主要的,是要上新浪微博和腾讯微博去看这家公司的官方微博做得怎样,是不是三十多万粉丝,但平均每条微博的内容转发数为0,或者转发者都是自己公司的员工,在下面留言是不是及时有回复。一个真正重视口碑和名声的微信外包机构会很珍惜自己的羽毛,重视自己在社会化媒体上的用户关系经营的。那些页面大气,内容精致原创,和用户互动热烈,观点对口,这个代运营公司值得谈一谈。
4、观察团队素养
微信外包机构的最大资产和财富就是人才,有实力的外包机构聘请的人员素质都不会差,对于外包团队成员的观察是必不可少的一环。看团队成员是否具备一定的职业成熟度。至少谈吐要过得去吧,形象可以不要求帅漂亮,但至少要顺眼,着装得体,上得了台面。跟他们闲聊时可以再问问他们微博和微信的差别,看他们能掰出多少不同来,说得系统全面的,可以合作。
在互联网的浪潮下,不少传统公司都纷纷开通了自己的微信公众平台,因此微信公众平台的二次开发也如火如荼,那么请务必记住以下这几条,以后有做微信外包的机构找你时,就拿这几条去观察,虽然无法保证你会赚钱,但至少能保证你不会被坑蒙拐骗了还帮流氓数钱。
首先查看他们做过哪些案例,这是最容易想到的,但要从几个步骤入手。
1、案例究竟是不是他们做的,这个可以要求把牛逼吹上天的微信外包机构给个甲方的联系方式,要座机哦,去调查,问问,那个外包机构是不是他们的年度服务商,所讲的案例的数字是不是真的,例如一周涨了50万微信粉丝,微信上一个月流水过百万,是不是这个外包机构干出来的。要是说对方负责人休假电话不方便给啊,对方联系人已经离职啊啥的,这些借口别听,可以送客了。
顺道给大家普及个常识,大品牌公司在选择年度服务商的时候会通过比稿的方式来选择外包机构,比稿通过的,成为年度服务商,比稿不通过的,会拿到比稿费,他们比稿里所陈述的方案会被选上的外包机构拿来用。
因此,不少外包服务机构会到处吹嘘某公司是他们服务的,并在网站上挂着一大堆牛逼品牌的LOGO,其中90%以上都只是跟他们的工作人员见过一面,或者是外包产业链里最低端的分包商而已,例如很多没见过世面的公司,经常会因此而激动地向别人吹嘘那谁谁谁是他们服务过的客户,事实上嘛,前面告诉过你啦。
2、他们所说的案例做得怎样,既然是案例,肯定有微信公众号,上去看看他们公众号的内容做得如何,微博上给微信拉粉的素材创作水平,天天都有促销信息、配图难看、行文如嚼蜡、样式死板、最近还顶风作案诱导用户把内容分享到朋友圈的,你想被封号就找他们吧。
3、看名声和网络形象
诚信的重要性就不用多说了,用人德为先,寻找靠谱的微信外包机构同样如此。除了向同行打听这家公司的专业度和诚信度,或者到百度上搜索这家公司的名字,看看这个公司是不是在自己的百度百科词条下面紧跟着就是一堆离职员工的讨薪控诉,或者一些企划经理的帖子骂他们狗屎傻逼饭桶。
最主要的,是要上新浪微博和腾讯微博去看这家公司的官方微博做得怎样,是不是三十多万粉丝,但平均每条微博的内容转发数为0,或者转发者都是自己公司的员工,在下面留言是不是及时有回复。一个真正重视口碑和名声的微信外包机构会很珍惜自己的羽毛,重视自己在社会化媒体上的用户关系经营的。那些页面大气,内容精致原创,和用户互动热烈,观点对口,这个代运营公司值得谈一谈。
4、观察团队素养
微信外包机构的最大资产和财富就是人才,有实力的外包机构聘请的人员素质都不会差,对于外包团队成员的观察是必不可少的一环。看团队成员是否具备一定的职业成熟度。至少谈吐要过得去吧,形象可以不要求帅漂亮,但至少要顺眼,着装得体,上得了台面。跟他们闲聊时可以再问问他们微博和微信的差别,看他们能掰出多少不同来,说得系统全面的,可以合作。
多页签使用webview下拉刷新的实现思路
原先使用demo中" 选项卡切换+下拉刷新"(pullrefresh_with_tab.html) 发现卡顿非常严重,在数据量多或者多滑动几次后出现卡顿情况。
最近有个新思路,使用demo中“默认实现”来实现 选项卡切换的功能,缺点是不能实现选项卡的滑动切换,但是卡顿现象消失。
大体思路:父页面放置选项卡,子页面 设置多个mui-scroll ,通过选项卡的tap事件,来控制 子页面的mui-scroll的显示和隐藏。
原先使用demo中" 选项卡切换+下拉刷新"(pullrefresh_with_tab.html) 发现卡顿非常严重,在数据量多或者多滑动几次后出现卡顿情况。
最近有个新思路,使用demo中“默认实现”来实现 选项卡切换的功能,缺点是不能实现选项卡的滑动切换,但是卡顿现象消失。
大体思路:父页面放置选项卡,子页面 设置多个mui-scroll ,通过选项卡的tap事件,来控制 子页面的mui-scroll的显示和隐藏。
收起阅读 »论软件外包中后期维护的重要性
在软件外包过程中,发包方应该在外包协议中要求承包方在系统计划的生命周期中始终提供支持和维护,而不是仅仅负责最初的开发项目。
这就要求在软件外包的时候,不仅要考虑眼前的使用,还要考虑到软件的长期使用,所以外包协议中一定要明确软件的维护期,要求承包方在包括维护期内始终提供支持。
另外,即使需要为承包方提供足够的经济激励,也要让承包方留住最初的开发人员,并让这些人维护、补充你的应用程序。
同时在软件外包协议中还应明确,承包方应确保即使进入维护期,也会有开发人员来做维护。我们应该与最初开发者保持联系,以确保应用程序能够尽快得到修补和维护。因为如果让新手来维护,他们通常速度很慢,而且常犯错误。
软件外包的承包方虽然允许有新人加入维护团队,但承包方必须确保新手是逐渐加入的,这样他们才能得到足够的督导和锻炼。而且对于维护团队,也应该给他们足够的经济激励,使他们有动力快速完成维护任务。
如果承包方对于承担长期的软件维护有困难的话,可以用一种比较安全的方法来替代,让本公司的员工在项目中扮演学徒的角色,熟悉和并了解软件架构。随着时间推移,随着内部员工逐渐学习提高,就可以考虑将软件维护的责任从软件外包团队手中转交给公司内部的人员。
在软件外包过程中,发包方应该在外包协议中要求承包方在系统计划的生命周期中始终提供支持和维护,而不是仅仅负责最初的开发项目。
这就要求在软件外包的时候,不仅要考虑眼前的使用,还要考虑到软件的长期使用,所以外包协议中一定要明确软件的维护期,要求承包方在包括维护期内始终提供支持。
另外,即使需要为承包方提供足够的经济激励,也要让承包方留住最初的开发人员,并让这些人维护、补充你的应用程序。
同时在软件外包协议中还应明确,承包方应确保即使进入维护期,也会有开发人员来做维护。我们应该与最初开发者保持联系,以确保应用程序能够尽快得到修补和维护。因为如果让新手来维护,他们通常速度很慢,而且常犯错误。
软件外包的承包方虽然允许有新人加入维护团队,但承包方必须确保新手是逐渐加入的,这样他们才能得到足够的督导和锻炼。而且对于维护团队,也应该给他们足够的经济激励,使他们有动力快速完成维护任务。
如果承包方对于承担长期的软件维护有困难的话,可以用一种比较安全的方法来替代,让本公司的员工在项目中扮演学徒的角色,熟悉和并了解软件架构。随着时间推移,随着内部员工逐渐学习提高,就可以考虑将软件维护的责任从软件外包团队手中转交给公司内部的人员。
收起阅读 »






