
oppo手机通讯录权限禁用闪退的解决方案
在oppo手机中 如果权限被禁用addressbook.find()这行会被执行,发生闪退,plus.contacts.getAddressBook方法无论权限同意与否都会执行成功的回调函数,所以解决闪退问题就需要判断权限是否被禁用,在H5+ 中plus.navigator.checkPermission()方法暂时不支持Android,废话不多说,一下方法可以询问到通讯录权限,进而解决oppo手机的闪退问题
var main = plus.android.runtimeMainActivity();
var a = plus.android.importClass('android.content.ContentProviderOperation');
var b = plus.android.importClass('android.content.ContentResolver');
var c = plus.android.importClass('android.database.Cursor');
var cc = plus.android.importClass('android.net.Uri');
var d = plus.android.importClass('android.test.AndroidTestCase');
var e = plus.android.importClass('android.util.Log');
var uri = cc.parse("content://com.android.contacts/contacts");
var arr = ["_id"];
var resolver = main.getContext().getContentResolver();
var cursor = resolver.query(uri, arr, null, null, null);
if (cursor.moveToNext() == false) {
alert("请打开权限")
}else{
alert("权限已打开")
}
在oppo手机中 如果权限被禁用addressbook.find()这行会被执行,发生闪退,plus.contacts.getAddressBook方法无论权限同意与否都会执行成功的回调函数,所以解决闪退问题就需要判断权限是否被禁用,在H5+ 中plus.navigator.checkPermission()方法暂时不支持Android,废话不多说,一下方法可以询问到通讯录权限,进而解决oppo手机的闪退问题
var main = plus.android.runtimeMainActivity();
var a = plus.android.importClass('android.content.ContentProviderOperation');
var b = plus.android.importClass('android.content.ContentResolver');
var c = plus.android.importClass('android.database.Cursor');
var cc = plus.android.importClass('android.net.Uri');
var d = plus.android.importClass('android.test.AndroidTestCase');
var e = plus.android.importClass('android.util.Log');
var uri = cc.parse("content://com.android.contacts/contacts");
var arr = ["_id"];
var resolver = main.getContext().getContentResolver();
var cursor = resolver.query(uri, arr, null, null, null);
if (cursor.moveToNext() == false) {
alert("请打开权限")
}else{
alert("权限已打开")
}

多图上传,uploader前后端实现,避免入坑
摸索了一个上午,终于实现uploader,为避免入坑,特分享如下
首先上效果图:

【说明】:
- 从相册或照相机取得图片后,append到<div id="imgs"></div>中
- 点击上传按纽,发送以下数据:A:各文本框的值,B:一个或数个图片文件
uploader的参数未有作出很详细的说明,前后端实现也没有完整的教材及案例,故从头端开始。
从相册或从相机取得照片,这个比较简单,就不作说明了
上传的方法里,有以下要注意:
前端没什么很多地方需要注意的,按示例基本上就可以了,关键后端讲一下:
接收带入的参数,直接用$_POST接收,接收图片文件用$_FILES接收。所以uploader向后台发送了两个数组,一个是$_POST,一个是$_FILES
最后入库操作
效果
摸索了一个上午,终于实现uploader,为避免入坑,特分享如下
首先上效果图:
【说明】:
- 从相册或照相机取得图片后,append到<div id="imgs"></div>中
- 点击上传按纽,发送以下数据:A:各文本框的值,B:一个或数个图片文件
uploader的参数未有作出很详细的说明,前后端实现也没有完整的教材及案例,故从头端开始。
从相册或从相机取得照片,这个比较简单,就不作说明了
上传的方法里,有以下要注意:
前端没什么很多地方需要注意的,按示例基本上就可以了,关键后端讲一下:
接收带入的参数,直接用$_POST接收,接收图片文件用$_FILES接收。所以uploader向后台发送了两个数组,一个是$_POST,一个是$_FILES
最后入库操作
效果
收起阅读 »
选项卡切换优化 - wap2app教程
体验差距
相比原生App,M站选项卡切换体验较差,主要体现在:原生App切换选项卡时,选项卡区域不变,仅内容区view变化;但M站选项卡切换时,会将整个页面重新加载,经常出现白屏现象。
优化思路
wap2app的优化方案是拆分选项卡区域和内容区,变成两个单独的webview;切换选项卡时,选项卡区webview仅切换高亮状态,然后通知内容区webview加载新的页面,这样就可以避免整体白屏现象。
进一步解释:
- wap2app客户端维护一个本地html文件,用于显示选项卡内容及高亮状态切换,下图中“选项卡区 - webview 1”
- 内容依然从服务端M站加载,下图中"可变内容区 - webview 2"
Tips:
- 因为已经存在本地的选项卡,需要M站稍作改造,判断在流应用环境下,不生成(或隐藏)选项卡DOM;
- wap2app目前仅支持首页显示选项卡优化实现,且需要HBuilder版本在8.8.5以上。
配置方案
选项卡的优化,分为客户端配置和M站改造
客户端配置
客户端的配置分为两个部分:
- 创建本地选项卡(client_index.html中实现)
- 配置选项卡页面规则(sitemap.json中实现)
创建本地选项卡
HBuilder中新建的wap2app项目中默认包含有一个client_index.html文件,我们需要在这个文件中生成本地选项卡菜单;首先下载 __wap2apptabbar.css、__wap2apptabbar.js 两个文件放到项目根目录,将如下注释代码恢复:
<!--使用本地选项卡时,将如下两行代码注释取消-->
<!--<link rel="stylesheet" type="text/css" href="./__wap2app_tabbar.css"/>-->
<!--<script src="./__wap2app_tabbar.js" type="text/javascript" charset="utf-8"></script>-->
然后初始化选项卡菜单,在body节点下增加类似如下代码(实际项目中需替换为自己M站地址及图标):
<script>
new TabBar({
list: [{
url: "http://m.exampple.com/",
text: "首页",
iconPath: 'home.png',
selectedIconPath: 'home-selected.png'
}, {
url: "http://m.exampple.com/list.html",
text: "示例",
iconPath: 'tab1.png', //本地图标
selectedIconPath: 'tab1-selected.png'
}, {
url: "http://m.exampple.com/about",
text: "关于",
iconPath: 'http://m.exampple.com/imgs/about.png',//网络图标
selectedIconPath: 'http://m.exampple.com/imgs/about-selected.png'
}]
});
</script>
选项卡构造函数中参数解释如下:
- url:点击选项卡需要跳转的url地址,需使用完整网络地址
- text:选项卡文字描述
- iconPath:选项卡默认图标,可以是本地地址(相对client_index.html的相对路径),也可以是网络地址
- selectedIconPath:选项卡高亮图标,可以是本地地址(相对client_index.html的相对路径),也可以是网络地址
选项卡文字颜色配置
因为是本地HTML实现的选项卡,因此可以在client_index.html中通过css自定义选项卡文字颜色,如下为示例代码:
<style type="text/css">
/*自定义选项卡文字颜色示例*/
.tab-item {
color: black;//选项卡文字默认为黑色
}
.tab-item.active {
color: blue;//选项卡文字高亮时为蓝色
}
</style>
Tips:自定义的css代码需要放在__wap2app_tabbar.css的引用之后
配置选项卡页面规则
另外,我们还需要在sitemap.json中配置选项卡关联关系,示例如下:
{
"webviewId": "__W2A__m.example.com",
"matchUrls": [
//URL匹配规则
],
"webviewParameter": {
"tabBar": {//选项卡配置,仅首页支持
"height": "50px",//选项卡高度,默认为50px
"list": [
{
"url": "http://m.exampple.com/" //tab1页面地址
}, {
"url": "http://m.exampple.com/list.html" //tab2页面地址
}, {
"url": "http://m.exampple.com/about.html" //tab3页面地址
}
]
}
}
}
Tips:如上示例中,tab1的页面url地址,需满足首页matchUrls的匹配规则
M站改造
M站需判断在流应用环境下,不生成(或隐藏)选项卡DOM,实现方案参考:去除M站DOM元素
体验差距
相比原生App,M站选项卡切换体验较差,主要体现在:原生App切换选项卡时,选项卡区域不变,仅内容区view变化;但M站选项卡切换时,会将整个页面重新加载,经常出现白屏现象。
优化思路
wap2app的优化方案是拆分选项卡区域和内容区,变成两个单独的webview;切换选项卡时,选项卡区webview仅切换高亮状态,然后通知内容区webview加载新的页面,这样就可以避免整体白屏现象。
进一步解释:
- wap2app客户端维护一个本地html文件,用于显示选项卡内容及高亮状态切换,下图中“选项卡区 - webview 1”
- 内容依然从服务端M站加载,下图中"可变内容区 - webview 2"
Tips:
- 因为已经存在本地的选项卡,需要M站稍作改造,判断在流应用环境下,不生成(或隐藏)选项卡DOM;
- wap2app目前仅支持首页显示选项卡优化实现,且需要HBuilder版本在8.8.5以上。
配置方案
选项卡的优化,分为客户端配置和M站改造
客户端配置
客户端的配置分为两个部分:
- 创建本地选项卡(client_index.html中实现)
- 配置选项卡页面规则(sitemap.json中实现)
创建本地选项卡
HBuilder中新建的wap2app项目中默认包含有一个client_index.html文件,我们需要在这个文件中生成本地选项卡菜单;首先下载 __wap2apptabbar.css、__wap2apptabbar.js 两个文件放到项目根目录,将如下注释代码恢复:
<!--使用本地选项卡时,将如下两行代码注释取消-->
<!--<link rel="stylesheet" type="text/css" href="./__wap2app_tabbar.css"/>-->
<!--<script src="./__wap2app_tabbar.js" type="text/javascript" charset="utf-8"></script>-->
然后初始化选项卡菜单,在body节点下增加类似如下代码(实际项目中需替换为自己M站地址及图标):
<script>
new TabBar({
list: [{
url: "http://m.exampple.com/",
text: "首页",
iconPath: 'home.png',
selectedIconPath: 'home-selected.png'
}, {
url: "http://m.exampple.com/list.html",
text: "示例",
iconPath: 'tab1.png', //本地图标
selectedIconPath: 'tab1-selected.png'
}, {
url: "http://m.exampple.com/about",
text: "关于",
iconPath: 'http://m.exampple.com/imgs/about.png',//网络图标
selectedIconPath: 'http://m.exampple.com/imgs/about-selected.png'
}]
});
</script>
选项卡构造函数中参数解释如下:
- url:点击选项卡需要跳转的url地址,需使用完整网络地址
- text:选项卡文字描述
- iconPath:选项卡默认图标,可以是本地地址(相对client_index.html的相对路径),也可以是网络地址
- selectedIconPath:选项卡高亮图标,可以是本地地址(相对client_index.html的相对路径),也可以是网络地址
选项卡文字颜色配置
因为是本地HTML实现的选项卡,因此可以在client_index.html中通过css自定义选项卡文字颜色,如下为示例代码:
<style type="text/css">
/*自定义选项卡文字颜色示例*/
.tab-item {
color: black;//选项卡文字默认为黑色
}
.tab-item.active {
color: blue;//选项卡文字高亮时为蓝色
}
</style>
Tips:自定义的css代码需要放在__wap2app_tabbar.css的引用之后
配置选项卡页面规则
另外,我们还需要在sitemap.json中配置选项卡关联关系,示例如下:
{
"webviewId": "__W2A__m.example.com",
"matchUrls": [
//URL匹配规则
],
"webviewParameter": {
"tabBar": {//选项卡配置,仅首页支持
"height": "50px",//选项卡高度,默认为50px
"list": [
{
"url": "http://m.exampple.com/" //tab1页面地址
}, {
"url": "http://m.exampple.com/list.html" //tab2页面地址
}, {
"url": "http://m.exampple.com/about.html" //tab3页面地址
}
]
}
}
}
Tips:如上示例中,tab1的页面url地址,需满足首页matchUrls的匹配规则
M站改造
M站需判断在流应用环境下,不生成(或隐藏)选项卡DOM,实现方案参考:去除M站DOM元素
收起阅读 »
关于Android离线打包使用地图的那些坑
1.参数中provider选择参数baidu需要单独引用jar包,具体见sdk中excel,例如用百度地图要单独引百度定位的包
- Android手机设置中有一个定位模式的选项,如果是低功耗、只使用gps,那么provider使用system参数在用户不打开gps开关的时候会进入err回调
- 定位如果返回的是5e-324,定位在了非洲旁边的海里,注意logcat里是不是有一条绿色提示Authentication Error errorcode: 230,假如有,更改一下eclipse的custom debug keystore,或者把你默认的custom debug keystore的相关参数改到百度地图api的debug密钥里
1.参数中provider选择参数baidu需要单独引用jar包,具体见sdk中excel,例如用百度地图要单独引百度定位的包
- Android手机设置中有一个定位模式的选项,如果是低功耗、只使用gps,那么provider使用system参数在用户不打开gps开关的时候会进入err回调
- 定位如果返回的是5e-324,定位在了非洲旁边的海里,注意logcat里是不是有一条绿色提示Authentication Error errorcode: 230,假如有,更改一下eclipse的custom debug keystore,或者把你默认的custom debug keystore的相关参数改到百度地图api的debug密钥里

【原创】离线打包webapp模式,实现从任意原生app界面跳转到h5的指定页面
背景:离线webapp模式打包,利用插件可以实现h5与原生之间双向调用,但是仅仅是基于启动sdk的那个Activity或者是Controller,有时候需要在任何一个原生的界面都能够调用h5的指定的页面,经过反复研究、多次请教官方的技术人员,终于得以实现,数据共享、插件调用都没有问题。
[有不清楚的地方联系qq:852085282,不喜勿喷!]
实现思路:
-
安卓: 新启动一个Activity,获取EntryProxy单例,动态修改为webview模式,根据指定的html页面路径,创建一个webview,添加到新的Activity上面。【注意在h5页面点击返回的时候,需要定制mui.back事件,调用plus.runtime.quit();来关闭这个Activity】
-
苹果: 新启动一个UIViewController,根据指定的html页面路径,创建一个PDRCoreAppFrame视图,作为新UIViewController的subview.【注意:在h5页面返回的时候,需要通过插件的方式通知原生app去获取当前显示在最上层的UIViewController做出关闭动作】
具体代码:
- 安卓:在需要的地方调用:
Intent intent = new Intent( this, WebviewActivity.class); String _url = "h5page.html";// 此处还可以在url里传递数据 intent.putExtra("url", _url); this.startActivity(intent);
关键的部分就是那个WebviewActivity.java, 具体见下方代码。
- 苹果:在需要的地方调用:
H5WebviewController *vc = [[H5WebviewController alloc] init]; vc.page = @"userpage.html?userid=1000011"; [self presentViewController:vc animated: YES completion:nil];// 此处也可以push的方式
关键的部分就是那个H5WebviewController类, 具体见下方代码。
封装的2个原生界面代码:
WebviewActivity.java 如下:
public class WebviewActivity extends Activity {
IWebview webview = null;
EntryProxy mEntryProxy = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
mEntryProxy = EntryProxy.getInstnace();
mEntryProxy.onCreate(this, savedInstanceState, SDK.IntegratedMode.WEBVIEW, null);
final FrameLayout rootView = new FrameLayout(this);
setContentView(rootView);
rootView.setBackgroundColor(0xffffffff);
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
webview.onRootViewGlobalLayout(rootView);
}
});
// 设置单页面集成的appid
String appid = ""+System.currentTimeMillis();
// 单页面集成时要加载页面的路径,可以是本地文件路径也可以是网络路径
String _url = this.getIntent().getStringExtra("url");
String url = "file:///android_asset/apps/xxxx/www/dist/html/" + _url;
webview = SDK.createWebview(this, url, appid, new IWebviewStateListener() {
@Override
public Object onCallBack(int pType, Object pArgs) {
switch (pType) {
case IWebviewStateListener.ON_WEBVIEW_READY:
// 准备完毕之后添加webview到显示父View中,设置排版不显示状态,避免显示webview时,html内容排版错乱问题
//((IWebview) pArgs).obtainFrameView().obtainMainView().setVisibility(View.INVISIBLE);
SDK.attach(rootView, ((IWebview) pArgs));
break;
case IWebviewStateListener.ON_PAGE_STARTED:
// 首页面开始加载事件
break;
case IWebviewStateListener.ON_PROGRESS_CHANGED:
// 首页面加载进度变化
break;
case IWebviewStateListener.ON_PAGE_FINISHED:
// 页面加载完毕,设置显示webview
// webview.obtainFrameView().obtainMainView().setVisibility(View.VISIBLE);
break;
}
return null;
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return mEntryProxy.onActivityExecute(this,
ISysEventListener.SysEventType.onCreateOptionMenu, menu);
}
@Override
public void onPause() {
super.onPause();
mEntryProxy.onPause(this);
}
@Override
public void onResume() {
super.onResume();
mEntryProxy.onResume(this);
}
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.getFlags() != 0x10600000) {// 非点击icon调用activity时才调用newintent事件
mEntryProxy.onNewIntent(this, intent);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mEntryProxy.destroy(this);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean _ret = mEntryProxy.onActivityExecute(this,
ISysEventListener.SysEventType.onKeyDown, new Object[] { keyCode, event });
return _ret ? _ret : super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
boolean _ret = mEntryProxy.onActivityExecute(this,
ISysEventListener.SysEventType.onKeyUp, new Object[] { keyCode, event });
return _ret ? _ret : super.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
boolean _ret = mEntryProxy.onActivityExecute(this,
ISysEventListener.SysEventType.onKeyLongPress, new Object[] { keyCode, event });
return _ret ? _ret : super.onKeyLongPress(keyCode, event);
}
public void onConfigurationChanged(Configuration newConfig) {
try {
int temp = this.getResources().getConfiguration().orientation;
if (mEntryProxy != null) {
mEntryProxy.onConfigurationChanged(this, temp);
}
super.onConfigurationChanged(newConfig);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mEntryProxy.onActivityExecute(this, ISysEventListener.SysEventType.onActivityResult,
new Object[] { requestCode, resultCode, data });
}
}
- H5WebviewController类如下:
> #define kStatusBarHeight 20.f @interface H5WebviewController() { } @end @implementation H5WebviewController
-
(void)loadView
{
[super loadView];
// 获取PDRCore句柄
PDRCore* pCoreHandle = [PDRCore Instance];
if (pCoreHandle != nil)
{
// 设置Core启动方式
// [pCoreHandle startAsWebClient];
//[pCoreHandle start];// 设置拼写Webview将要打开文件的url NSString* pFilePath = [NSString stringWithFormat:@"file://%@/%@%@", [NSBundle mainBundle].bundlePath, @"Pandora/apps/xxxxx/www/dist/html/", self.page]; CGRect StRect = CGRectMake(0, kStatusBarHeight, self.view.frame.size.width, self.view.frame.size.height - kStatusBarHeight); // 使用时间戳来区分 NSString *webViewID = [NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]]; PDRCoreAppFrame *appFrame = [[PDRCoreAppFrame alloc] initWithName:webViewID loadURL:pFilePath frame:StRect]; // 设置webview的Appframe [pCoreHandle.appManager.activeApp.appWindow registerFrame:appFrame]; // 将AppFrame设置为当前View的Subview [self.view addSubview:appFrame];
}
}
@end
背景:离线webapp模式打包,利用插件可以实现h5与原生之间双向调用,但是仅仅是基于启动sdk的那个Activity或者是Controller,有时候需要在任何一个原生的界面都能够调用h5的指定的页面,经过反复研究、多次请教官方的技术人员,终于得以实现,数据共享、插件调用都没有问题。
[有不清楚的地方联系qq:852085282,不喜勿喷!]
实现思路:
-
安卓: 新启动一个Activity,获取EntryProxy单例,动态修改为webview模式,根据指定的html页面路径,创建一个webview,添加到新的Activity上面。【注意在h5页面点击返回的时候,需要定制mui.back事件,调用plus.runtime.quit();来关闭这个Activity】
-
苹果: 新启动一个UIViewController,根据指定的html页面路径,创建一个PDRCoreAppFrame视图,作为新UIViewController的subview.【注意:在h5页面返回的时候,需要通过插件的方式通知原生app去获取当前显示在最上层的UIViewController做出关闭动作】
具体代码:
- 安卓:在需要的地方调用:
Intent intent = new Intent( this, WebviewActivity.class); String _url = "h5page.html";// 此处还可以在url里传递数据 intent.putExtra("url", _url); this.startActivity(intent);
关键的部分就是那个WebviewActivity.java, 具体见下方代码。
- 苹果:在需要的地方调用:
H5WebviewController *vc = [[H5WebviewController alloc] init]; vc.page = @"userpage.html?userid=1000011"; [self presentViewController:vc animated: YES completion:nil];// 此处也可以push的方式
关键的部分就是那个H5WebviewController类, 具体见下方代码。
封装的2个原生界面代码:
WebviewActivity.java 如下:
public class WebviewActivity extends Activity {
IWebview webview = null;
EntryProxy mEntryProxy = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
mEntryProxy = EntryProxy.getInstnace();
mEntryProxy.onCreate(this, savedInstanceState, SDK.IntegratedMode.WEBVIEW, null);
final FrameLayout rootView = new FrameLayout(this);
setContentView(rootView);
rootView.setBackgroundColor(0xffffffff);
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
webview.onRootViewGlobalLayout(rootView);
}
});
// 设置单页面集成的appid
String appid = ""+System.currentTimeMillis();
// 单页面集成时要加载页面的路径,可以是本地文件路径也可以是网络路径
String _url = this.getIntent().getStringExtra("url");
String url = "file:///android_asset/apps/xxxx/www/dist/html/" + _url;
webview = SDK.createWebview(this, url, appid, new IWebviewStateListener() {
@Override
public Object onCallBack(int pType, Object pArgs) {
switch (pType) {
case IWebviewStateListener.ON_WEBVIEW_READY:
// 准备完毕之后添加webview到显示父View中,设置排版不显示状态,避免显示webview时,html内容排版错乱问题
//((IWebview) pArgs).obtainFrameView().obtainMainView().setVisibility(View.INVISIBLE);
SDK.attach(rootView, ((IWebview) pArgs));
break;
case IWebviewStateListener.ON_PAGE_STARTED:
// 首页面开始加载事件
break;
case IWebviewStateListener.ON_PROGRESS_CHANGED:
// 首页面加载进度变化
break;
case IWebviewStateListener.ON_PAGE_FINISHED:
// 页面加载完毕,设置显示webview
// webview.obtainFrameView().obtainMainView().setVisibility(View.VISIBLE);
break;
}
return null;
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return mEntryProxy.onActivityExecute(this,
ISysEventListener.SysEventType.onCreateOptionMenu, menu);
}
@Override
public void onPause() {
super.onPause();
mEntryProxy.onPause(this);
}
@Override
public void onResume() {
super.onResume();
mEntryProxy.onResume(this);
}
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.getFlags() != 0x10600000) {// 非点击icon调用activity时才调用newintent事件
mEntryProxy.onNewIntent(this, intent);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mEntryProxy.destroy(this);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean _ret = mEntryProxy.onActivityExecute(this,
ISysEventListener.SysEventType.onKeyDown, new Object[] { keyCode, event });
return _ret ? _ret : super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
boolean _ret = mEntryProxy.onActivityExecute(this,
ISysEventListener.SysEventType.onKeyUp, new Object[] { keyCode, event });
return _ret ? _ret : super.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
boolean _ret = mEntryProxy.onActivityExecute(this,
ISysEventListener.SysEventType.onKeyLongPress, new Object[] { keyCode, event });
return _ret ? _ret : super.onKeyLongPress(keyCode, event);
}
public void onConfigurationChanged(Configuration newConfig) {
try {
int temp = this.getResources().getConfiguration().orientation;
if (mEntryProxy != null) {
mEntryProxy.onConfigurationChanged(this, temp);
}
super.onConfigurationChanged(newConfig);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mEntryProxy.onActivityExecute(this, ISysEventListener.SysEventType.onActivityResult,
new Object[] { requestCode, resultCode, data });
}
}
- H5WebviewController类如下:
> #define kStatusBarHeight 20.f @interface H5WebviewController() { } @end @implementation H5WebviewController
-
(void)loadView
{
[super loadView];
// 获取PDRCore句柄
PDRCore* pCoreHandle = [PDRCore Instance];
if (pCoreHandle != nil)
{
// 设置Core启动方式
// [pCoreHandle startAsWebClient];
//[pCoreHandle start];// 设置拼写Webview将要打开文件的url NSString* pFilePath = [NSString stringWithFormat:@"file://%@/%@%@", [NSBundle mainBundle].bundlePath, @"Pandora/apps/xxxxx/www/dist/html/", self.page]; CGRect StRect = CGRectMake(0, kStatusBarHeight, self.view.frame.size.width, self.view.frame.size.height - kStatusBarHeight); // 使用时间戳来区分 NSString *webViewID = [NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]]; PDRCoreAppFrame *appFrame = [[PDRCoreAppFrame alloc] initWithName:webViewID loadURL:pFilePath frame:StRect]; // 设置webview的Appframe [pCoreHandle.appManager.activeApp.appWindow registerFrame:appFrame]; // 将AppFrame设置为当前View的Subview [self.view addSubview:appFrame];
}
}
@end

http://mmw.591860.cn/index.php?id=123321
mui做的项目,有需要的可以下载学习,涉及微信推广,在线视频播放等内容
mui做的项目,有需要的可以下载学习,涉及微信推广,在线视频播放等内容

【分享】省市区三级联动
/* 调用方法
* selectAddr.init({
* sId:[...], 下拉框id数组
* deVal:'130102' 下拉框默认区值
* })
* 省市区选择插件
* varstion 1.0.0
* by Allen-Fei
* tipefi@126.com
* 基于js/region.js城市Json文件------手机 and PC端
*/
var selectAddr = {
init: function(o, callback) {
this.end = callback;
this.options = o;
this.addChange();
this.getJson();
this.data = {
dProvince: [],
dCity: [],
dCounty: []
};
this.addr = [];
},
//获取JSON城市数据
getJson: function() {
var that = this;
var val = this.options.deVal;
var sId = this.options.sId;
mui.getJSON('js/region.json', function(data) {
for(var i = 0; i < data.length; i++) {
var len = data[i].qybm.length;
if(len == 2) that.data.dProvince.push(data[i])
if(len == 4) that.data.dCity.push(data[i])
if(len == 6) that.data.dCounty.push(data[i])
}
that.addOption(that.data.dProvince, 0)
if(val) {
that.setSelect(that.data.dProvince, val.substring(0, 2), 0);
that.sProvince(val.substring(0, 2), true);
that.sCity(val.substring(0, 4), true);
that.sCounty(val, true);
}
});
},
//给select追加事件
addChange: function() {
var sId = this.options.sId;
for(var i = 0; i < sId.length; i++) {
if(i == 0) document.getElementById(sId[i]).setAttribute('onchange', 'selectAddr.sProvince(this.options[this.options.selectedIndex])')
if(i == 1) document.getElementById(sId[i]).setAttribute('onchange', 'selectAddr.sCity(this.options[this.options.selectedIndex])')
if(i == 2) document.getElementById(sId[i]).setAttribute('onchange', 'selectAddr.sCounty(this.options[this.options.selectedIndex])')
}
},
// 给下拉添加列表元素
addOption: function(d, n) {
var sId = this.options.sId;
if(n != 0) document.getElementById(sId[n]).innerHTML = '';
for(var i = 0; i < d.length; i++) {
var hoption = document.createElement('option');
var htext = document.createTextNode(d[i].qyjc);
hoption.appendChild(htext);
hoption.setAttribute('value', d[i].qybm)
document.getElementById(sId[n]).appendChild(hoption);
}
},
// 设置选中的值
setSelect: function(d, v, n) {
var sId = this.options.sId;
for(var i = 0; i < d.length; i++) {
if(v == d[i].qybm) {
if(n == 0) {
document.getElementById(sId[n])[i + 1].selected = true;
} else {
document.getElementById(sId[n])[i].selected = true;
}
}
}
},
// 选择省后运行(筛选出市列表)
sProvince: function(op, isdefault) {
var v = op instanceof Object ? op.value : op;
var d = this.data.dCity,
aCity = [],
sId = this.options.sId,
$s1 = document.getElementById(sId[1]);
this.addr = [];
for(var i = 0; i < d.length; i++) {
if(d[i].sjqybm == v) {
aCity.push(d[i]);
}
}
if(aCity.length > 0) {
this.addOption(aCity, 1)
} else { //当没有市级时,显示区县
var dt = this.data.dCounty;
for(var i = 0; i < dt.length; i++) {
if(dt[i].sjqybm == v) {
aCity.push(dt[i]);
}
}
this.addOption(aCity, 1)
}
this.setReturn(0);
if(isdefault) {
this.setSelect(aCity, this.options.deVal.substring(0, 4), 1);
return false;
}
if(sId.length == 3) this.sCity($s1.options[$s1.options.selectedIndex].value);
},
// 选择市后运行(筛选出区列表)
sCity: function(op, isdefault) {
var v = op instanceof Object ? op.value : op;
var d = this.data.dCounty,
aCounty = [],
sId = this.options.sId,
$s2 = document.getElementById(sId[2]);
for(var i = 0; i < d.length; i++) {
if(d[i].sjqybm == v) {
aCounty.push(d[i]);
}
}
this.addOption(aCounty, 2)
this.setReturn(1)
if(isdefault) { //如果值为字符串,则为默认值
this.setSelect(aCounty, this.options.deVal, 2);
return false;
}
if(v.length != 6) {
this.sCounty($s2.options[$s2.options.selectedIndex].value);
} else {
this.sCounty('');
}
},
// 选择区后运行
sCounty: function(op) {
if(op) {
var v = op instanceof Object ? op.value : op;
this.setReturn(2);
}
this.end(this.addr);
},
// 设置返回值
setReturn: function(n) {
var o = {},
sId = this.options.sId,
$s0 = document.getElementById(sId[0]),
$s1 = document.getElementById(sId[1]),
$s2 = document.getElementById(sId[2]);
switch(n) {
case 0:
o.text = $s0.options[$s0.options.selectedIndex].text;
o.value = $s0.options[$s0.options.selectedIndex].value;
break;
case 1:
o.text = $s1.options[$s1.options.selectedIndex].text;
o.value = $s1.options[$s1.options.selectedIndex].value;
break;
case 2:
o.text = $s2.options[$s2.options.selectedIndex].text;
o.value = $s2.options[$s2.options.selectedIndex].value;
break;
default:
break;
}
this.addr[n] = o;
}
}
城市JSON在附件,仅供参考
/* 调用方法
* selectAddr.init({
* sId:[...], 下拉框id数组
* deVal:'130102' 下拉框默认区值
* })
* 省市区选择插件
* varstion 1.0.0
* by Allen-Fei
* tipefi@126.com
* 基于js/region.js城市Json文件------手机 and PC端
*/
var selectAddr = {
init: function(o, callback) {
this.end = callback;
this.options = o;
this.addChange();
this.getJson();
this.data = {
dProvince: [],
dCity: [],
dCounty: []
};
this.addr = [];
},
//获取JSON城市数据
getJson: function() {
var that = this;
var val = this.options.deVal;
var sId = this.options.sId;
mui.getJSON('js/region.json', function(data) {
for(var i = 0; i < data.length; i++) {
var len = data[i].qybm.length;
if(len == 2) that.data.dProvince.push(data[i])
if(len == 4) that.data.dCity.push(data[i])
if(len == 6) that.data.dCounty.push(data[i])
}
that.addOption(that.data.dProvince, 0)
if(val) {
that.setSelect(that.data.dProvince, val.substring(0, 2), 0);
that.sProvince(val.substring(0, 2), true);
that.sCity(val.substring(0, 4), true);
that.sCounty(val, true);
}
});
},
//给select追加事件
addChange: function() {
var sId = this.options.sId;
for(var i = 0; i < sId.length; i++) {
if(i == 0) document.getElementById(sId[i]).setAttribute('onchange', 'selectAddr.sProvince(this.options[this.options.selectedIndex])')
if(i == 1) document.getElementById(sId[i]).setAttribute('onchange', 'selectAddr.sCity(this.options[this.options.selectedIndex])')
if(i == 2) document.getElementById(sId[i]).setAttribute('onchange', 'selectAddr.sCounty(this.options[this.options.selectedIndex])')
}
},
// 给下拉添加列表元素
addOption: function(d, n) {
var sId = this.options.sId;
if(n != 0) document.getElementById(sId[n]).innerHTML = '';
for(var i = 0; i < d.length; i++) {
var hoption = document.createElement('option');
var htext = document.createTextNode(d[i].qyjc);
hoption.appendChild(htext);
hoption.setAttribute('value', d[i].qybm)
document.getElementById(sId[n]).appendChild(hoption);
}
},
// 设置选中的值
setSelect: function(d, v, n) {
var sId = this.options.sId;
for(var i = 0; i < d.length; i++) {
if(v == d[i].qybm) {
if(n == 0) {
document.getElementById(sId[n])[i + 1].selected = true;
} else {
document.getElementById(sId[n])[i].selected = true;
}
}
}
},
// 选择省后运行(筛选出市列表)
sProvince: function(op, isdefault) {
var v = op instanceof Object ? op.value : op;
var d = this.data.dCity,
aCity = [],
sId = this.options.sId,
$s1 = document.getElementById(sId[1]);
this.addr = [];
for(var i = 0; i < d.length; i++) {
if(d[i].sjqybm == v) {
aCity.push(d[i]);
}
}
if(aCity.length > 0) {
this.addOption(aCity, 1)
} else { //当没有市级时,显示区县
var dt = this.data.dCounty;
for(var i = 0; i < dt.length; i++) {
if(dt[i].sjqybm == v) {
aCity.push(dt[i]);
}
}
this.addOption(aCity, 1)
}
this.setReturn(0);
if(isdefault) {
this.setSelect(aCity, this.options.deVal.substring(0, 4), 1);
return false;
}
if(sId.length == 3) this.sCity($s1.options[$s1.options.selectedIndex].value);
},
// 选择市后运行(筛选出区列表)
sCity: function(op, isdefault) {
var v = op instanceof Object ? op.value : op;
var d = this.data.dCounty,
aCounty = [],
sId = this.options.sId,
$s2 = document.getElementById(sId[2]);
for(var i = 0; i < d.length; i++) {
if(d[i].sjqybm == v) {
aCounty.push(d[i]);
}
}
this.addOption(aCounty, 2)
this.setReturn(1)
if(isdefault) { //如果值为字符串,则为默认值
this.setSelect(aCounty, this.options.deVal, 2);
return false;
}
if(v.length != 6) {
this.sCounty($s2.options[$s2.options.selectedIndex].value);
} else {
this.sCounty('');
}
},
// 选择区后运行
sCounty: function(op) {
if(op) {
var v = op instanceof Object ? op.value : op;
this.setReturn(2);
}
this.end(this.addr);
},
// 设置返回值
setReturn: function(n) {
var o = {},
sId = this.options.sId,
$s0 = document.getElementById(sId[0]),
$s1 = document.getElementById(sId[1]),
$s2 = document.getElementById(sId[2]);
switch(n) {
case 0:
o.text = $s0.options[$s0.options.selectedIndex].text;
o.value = $s0.options[$s0.options.selectedIndex].value;
break;
case 1:
o.text = $s1.options[$s1.options.selectedIndex].text;
o.value = $s1.options[$s1.options.selectedIndex].value;
break;
case 2:
o.text = $s2.options[$s2.options.selectedIndex].text;
o.value = $s2.options[$s2.options.selectedIndex].value;
break;
default:
break;
}
this.addr[n] = o;
}
}
城市JSON在附件,仅供参考
收起阅读 »
【分享】关于ajax的几个方法(方便自己下次用,包括错误处理、日志处理、加密处理)
/* 参数列表
* url 请求的地址后缀(必填)
* params 请求的参数(必填:如果没有参数,则传个空对象)
* onSuccess 请求成功的回调方法(选填)
* noData 请求成功但无数据的回调方法(选填)
* retry 请求自动重试的次数(选填)
* describe 请求的描述,用来提示错误信息(选填)
*/
function bpAjax(url, params, onSuccess, noData, retry, describe) {
plus.nativeUI.showWaiting();
var address = arguments[0];
var url = USERINFO.DL_HOST + arguments[0];
var onSuccess = arguments[2] ? arguments[2] : function() {};
var noData = arguments[3] ? arguments[3] : function() {};
var retry = arguments[4] ? arguments[4] : 3;
var params = params;
console.log(describe + '参数' + JSON.stringify(params));
if(!params.sign) params.sign = getRSA(params, describe) //当sign为空时,自动给键值排序生成sign
mui.ajax(url, {
data: params,
dataType: 'json', //服务器返回json格式数据
type: 'post', //HTTP请求类型
timeout: 10000, //超时时间设置为10秒;
success: function(data) {
plus.nativeUI.closeWaiting();
console.log(describe + '——' + data);
var d = JSON.parse(data);
if(d.msg == '1') {
onSuccess(d);
} else if(d.msg == '3') {
//localStorage.clear();
mui.toast(d.info);
onError('INVALID_TOKEN');
} else if(d.msg == '99'){
mui.toast(d.info);
}else {
noData(d);
}
},
error: function(xhr, type, errorThrown) {
plus.nativeUI.closeWaiting();
console.log(describe + '__' + errorThrown);
retry--;
if(retry > 0) return bpAjax(address, params, onSuccess, noData, retry, describe);
onError('FAILED_NETWORK', describe);
}
});
}
========================华丽的分隔线========================
/* 参数列表
* errcode 错误编码(必填)
* 可选值:'FAILED_NETWORK' 重连多次不成功网络不佳
* 可选值:'INVALID_TOKEN' 无效的token
*/
function onError(errcode, describe) {
switch(errcode) {
case 'FAILED_NETWORK':
mui.toast('当前网络不佳');
break;
case 'INVALID_TOKEN':
openWV('login.html');
break;
default:
console.log(describe + '——' + errcode);
}
}
========================华丽的分隔线========================
/* 需要RSA加密的对象,按对象键值排序加密 参数列表
* o参数为对象,
* 逻辑:需要RSA加密的对象,按对象键值排序加密,并返回
* DL_RED_PACKET 字符串是跟后台协定的
*/
function getRSA(o, describe) {
var encrypt = new JSEncrypt();
encrypt.setPublicKey(USERINFO.DL_PUBLIC_KEY); //设置公有key
var keys = Object.keys(o).sort();
if(arguments.length <= 0) return false;
var str = '';
for(var i = 0; i < keys.length; i++) {
if(keys[i] != 'sign') {
str += o[keys[i]];
}
}
var sign = '';
str = encodeURI(str + 'DL_RED_PACKET');
for(var i = 0; i <= parseInt(str.length / 117); i++) {
sign += encrypt.encrypt(str.substr(i * 117, 117))
}
return sign;
}
/* 参数列表
* url 请求的地址后缀(必填)
* params 请求的参数(必填:如果没有参数,则传个空对象)
* onSuccess 请求成功的回调方法(选填)
* noData 请求成功但无数据的回调方法(选填)
* retry 请求自动重试的次数(选填)
* describe 请求的描述,用来提示错误信息(选填)
*/
function bpAjax(url, params, onSuccess, noData, retry, describe) {
plus.nativeUI.showWaiting();
var address = arguments[0];
var url = USERINFO.DL_HOST + arguments[0];
var onSuccess = arguments[2] ? arguments[2] : function() {};
var noData = arguments[3] ? arguments[3] : function() {};
var retry = arguments[4] ? arguments[4] : 3;
var params = params;
console.log(describe + '参数' + JSON.stringify(params));
if(!params.sign) params.sign = getRSA(params, describe) //当sign为空时,自动给键值排序生成sign
mui.ajax(url, {
data: params,
dataType: 'json', //服务器返回json格式数据
type: 'post', //HTTP请求类型
timeout: 10000, //超时时间设置为10秒;
success: function(data) {
plus.nativeUI.closeWaiting();
console.log(describe + '——' + data);
var d = JSON.parse(data);
if(d.msg == '1') {
onSuccess(d);
} else if(d.msg == '3') {
//localStorage.clear();
mui.toast(d.info);
onError('INVALID_TOKEN');
} else if(d.msg == '99'){
mui.toast(d.info);
}else {
noData(d);
}
},
error: function(xhr, type, errorThrown) {
plus.nativeUI.closeWaiting();
console.log(describe + '__' + errorThrown);
retry--;
if(retry > 0) return bpAjax(address, params, onSuccess, noData, retry, describe);
onError('FAILED_NETWORK', describe);
}
});
}
========================华丽的分隔线========================
/* 参数列表
* errcode 错误编码(必填)
* 可选值:'FAILED_NETWORK' 重连多次不成功网络不佳
* 可选值:'INVALID_TOKEN' 无效的token
*/
function onError(errcode, describe) {
switch(errcode) {
case 'FAILED_NETWORK':
mui.toast('当前网络不佳');
break;
case 'INVALID_TOKEN':
openWV('login.html');
break;
default:
console.log(describe + '——' + errcode);
}
}
========================华丽的分隔线========================
/* 需要RSA加密的对象,按对象键值排序加密 参数列表
* o参数为对象,
* 逻辑:需要RSA加密的对象,按对象键值排序加密,并返回
* DL_RED_PACKET 字符串是跟后台协定的
*/
function getRSA(o, describe) {
var encrypt = new JSEncrypt();
encrypt.setPublicKey(USERINFO.DL_PUBLIC_KEY); //设置公有key
var keys = Object.keys(o).sort();
if(arguments.length <= 0) return false;
var str = '';
for(var i = 0; i < keys.length; i++) {
if(keys[i] != 'sign') {
str += o[keys[i]];
}
}
var sign = '';
str = encodeURI(str + 'DL_RED_PACKET');
for(var i = 0; i <= parseInt(str.length / 117); i++) {
sign += encrypt.encrypt(str.substr(i * 117, 117))
}
return sign;
}
收起阅读 »

新浪微博分享链接
新浪微博的分享链接不是单独出来的,并与图片产生冲突。
所以不要将分享的链接写到msg.href中,而是拼接在content中,如果链接经过新浪官方验证,则会显示概览信息(此信息与图片冲突)。
发此文还有个原因是告诉大家,新浪分享本来就是这样的,这个不是一个折衷的方案,好使,不用找别的了。
错怪官方这么久没修复这个东西 =。=
ps.回调地址没有的可以用默认 https://api.weibo.com/oauth2/default.html
站内相关问题参考:
http://ask.dcloud.net.cn/article/707
http://ask.dcloud.net.cn/question/8353 (此文基本囊括遇到问题)
新浪微博的分享链接不是单独出来的,并与图片产生冲突。
所以不要将分享的链接写到msg.href中,而是拼接在content中,如果链接经过新浪官方验证,则会显示概览信息(此信息与图片冲突)。
发此文还有个原因是告诉大家,新浪分享本来就是这样的,这个不是一个折衷的方案,好使,不用找别的了。
错怪官方这么久没修复这个东西 =。=
ps.回调地址没有的可以用默认 https://api.weibo.com/oauth2/default.html
站内相关问题参考:
http://ask.dcloud.net.cn/article/707
http://ask.dcloud.net.cn/question/8353 (此文基本囊括遇到问题)

查看流应用统计数据
开发者发布流应用后,可以登录DCloud开发者中心查看流应用的统计数据。
开发者登录后,会在首页展现所有已创建的应用列表,包括5+App、流应用、快应用等;
从列表中点击某个应用,可以查看该应用不同版本的的日活趋势、新增来源等统计数据。
应用趋势
开发者可以从多个维度查看具体应用的趋势数据,比如当日新增下载、新增激活、当日日活、启动次数等维度。
应用来源
流应用可以从多个场景触发,比如浏览器、应用市场、扫码等,开发者可以查看流应用的启动来源,分析各渠道推广效果。
开发者发布流应用后,可以登录DCloud开发者中心查看流应用的统计数据。
开发者登录后,会在首页展现所有已创建的应用列表,包括5+App、流应用、快应用等;
从列表中点击某个应用,可以查看该应用不同版本的的日活趋势、新增来源等统计数据。
应用趋势
开发者可以从多个维度查看具体应用的趋势数据,比如当日新增下载、新增激活、当日日活、启动次数等维度。
应用来源
流应用可以从多个场景触发,比如浏览器、应用市场、扫码等,开发者可以查看流应用的启动来源,分析各渠道推广效果。

ios蓝牙调戏一代小米手环
先上效果图
手环是小米一代光感版的,二代手环没有试过,地址有改变,应该调戏不了。
为什么说是调戏呢?
因为这一代小米手环有一些设置没有验证措施,随便一个手机都能读取手环的一些简单数据。
比如电量,步数,控制震动,其中很重要的控制震动竟然都不需要验证,希望二代手环有修复这个问题。
其他功能小米有限制,没有权限读取,要向小米申请,我就不折腾了。
我写得比较急,代码太难看了,就放两个截图吧。
Xcode上写的JS,这个乱真不怪我,Xcode会自动把js给缩进,开始时候还耐心的改一下,后面的又开始夸张的缩进,我就索性不管了。
插件引用了BabyBluetooth,放一下这个项目地址:https://github.com/coolnameismy/BabyBluetooth
BabyBluetooth还真好用。不用像原生CoreBluetooth那样凌乱了。
其实插件完全的按照官方文档还是能做出来的,虽然是门槛高一点,但是代码可控,用起来舒心。
先上效果图
手环是小米一代光感版的,二代手环没有试过,地址有改变,应该调戏不了。
为什么说是调戏呢?
因为这一代小米手环有一些设置没有验证措施,随便一个手机都能读取手环的一些简单数据。
比如电量,步数,控制震动,其中很重要的控制震动竟然都不需要验证,希望二代手环有修复这个问题。
其他功能小米有限制,没有权限读取,要向小米申请,我就不折腾了。
我写得比较急,代码太难看了,就放两个截图吧。
Xcode上写的JS,这个乱真不怪我,Xcode会自动把js给缩进,开始时候还耐心的改一下,后面的又开始夸张的缩进,我就索性不管了。
插件引用了BabyBluetooth,放一下这个项目地址:https://github.com/coolnameismy/BabyBluetooth
BabyBluetooth还真好用。不用像原生CoreBluetooth那样凌乱了。
其实插件完全的按照官方文档还是能做出来的,虽然是门槛高一点,但是代码可控,用起来舒心。
收起阅读 »