HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

深入理解高度。获取屏幕、webview、软键盘高度

Native.JS Webview 软键盘 输入法 高度

如果没有手动调整过webview的高度的话,默认情况下,屏幕的高度=顶部状态栏的高度+webview的高度。
如果软键盘打开,则屏幕的高度=顶部状态栏的高度+webview的高度+软键盘的高度。
HTML5 规范目前没有提供状态栏高度和软键盘高度的直接的查询方法,不过有了Native.js,我们还是能查询到所有这些高度的数值。

背景知识介绍:

手机屏幕有真实的物理分辨率,比如小米note的高度是1920px。  
但在网页里,一个10px的字体,并不会小的看不清,因为webview提供了逻辑分辨率的概念。  
如果不在meta里设置,默认下小米note的放大系数scale是3,就是会放大3倍显示。  
也就是对于HTML而言,小米note的高度是1920/3=640px。  
如果网页是全屏的,没有顶部状态栏,那么一个640px高的div将撑满屏幕高度。  

获取屏幕、顶部状态栏和软键盘的高度

1. 屏幕的高度

获取屏幕的高度很简单,HTML自带了screen.height,直接就可以得到屏幕的整体高度,单位是px,物理分辨率值(不是HTML的逻辑分辨率)。  
HTML5Plus里提供了plus.screen.resolutionHeight方法,也是屏幕高度,但这个值是逻辑分辨率的高度。参考[http://html5plus.org/doc/zh_cn/device.html#plus.screen](http://html5plus.org/doc/zh_cn/device.html#plus.screen)  
screen.height = plus.screen.resolutionHeight*plus.screen.scale  

2. 获取webview高度

虽然webview的getStyle可以得到webview的高度,但开发者如果不手动设置的话,默认值是100%,这个值对本文探讨的问题没有意义。我们需要px的物理高度。  
在Android里通过如下js代码可以得到webview的高度:plus.android.invoke(plus.android.currentWebview(),"getHeight")   
这是一段Native.js代码,由于Android的webview原生对象就有getHeight()方法,所以就可以直接这样调用。  
当然webview原生对象还有很多方法属性都可以调,具体参阅[Native.js入门](http://ask.dcloud.net.cn/docs/#http://ask.dcloud.net.cn/article/88)  
图1(此图中,物理分辨率是蓝色字体,逻辑分辨率是黑色字体) 对于iOS,就没必要使用Native.js这么复杂的技术了,iOS的屏幕高度是固定的几个,直接判断屏幕高度和设备型号就可以了。

3. 获取状态栏高度

顶部状态栏比较复杂,如果webview设了全屏,那么状态栏高度就为0。  
如果设置了沉浸式状态栏,状态栏透明了,虽然状态栏存在理论高度,但webview高度是全屏的。  
plus.navigator.getStatusbarHeight(),这个api专门获取状态栏高度。  

4. 获取输入法高度

当弹出输入法时,在Android上,webview的高度会自动减少,留出空间给软键盘。  
那么输入法高度=屏幕高度-状态栏高度-webview高度  
在小米note上,默认输入法高度是 863px。  
图2(此图中,物理分辨率是蓝色字体,逻辑分辨率是黑色字体)

5. 流应用任务栏高度

在流应用环境中,比如iOS流应用,会有一个流应用任务栏,这个任务栏的出现会挤压webview的高度。  
如果开发者要设置一个元素居底,请使用bottom方法,而不是通过屏幕高度-webview高度设top,以避免出现流应用任务栏时高度错位。  

说说HTML里的几个高度

其实HTML自己也提供了很多高度相关的api,除了screen.height还有document.body.clientHeight,document.body.scrollHeight,感觉也很容易混淆。  
document.body.clientHeight是网页内容区的有效高度,符合盒模型。document.body.scrollHeight是网页可滚动区域的高度。  
如图2所示:  
- 此时document.body.clientHeight小于webview高度,而document.body.scrollHeight等于webview高度,即document.body.scrollHeight=plus.android.invoke(plus.android.currentWebview(),"getHeight") 。  
- 如果数字不是1-5,而是1-10,那么document.body.clientHeight会继续增加,但内容不够多,不会滚动,document.body.scrollHeight不变,仍是webview高度。  
- 如果网页内容非常多,数字到了40,那document.body.clientHeight会继续增加,并且只要发生了滚动,则document.body.clientHeight=document.body.scrollHeight  

其他相关知识。

  • 监控软键盘是否弹出,Android上可以使用HTML自带的resize事件。
    但是要判断横竖屏,因为横竖屏切换也会造成resize,所以高度变而宽度不变,就是软键盘弹出或消失了。
  • 强制弹出键盘,也有Native.js示例,http://ask.dcloud.net.cn/question/2324
  • hello uni-app和hello mui都有聊天示例模板,有底部输入框跟随键盘顶起的示例,可以参考下。
继续阅读 »

如果没有手动调整过webview的高度的话,默认情况下,屏幕的高度=顶部状态栏的高度+webview的高度。
如果软键盘打开,则屏幕的高度=顶部状态栏的高度+webview的高度+软键盘的高度。
HTML5 规范目前没有提供状态栏高度和软键盘高度的直接的查询方法,不过有了Native.js,我们还是能查询到所有这些高度的数值。

背景知识介绍:

手机屏幕有真实的物理分辨率,比如小米note的高度是1920px。  
但在网页里,一个10px的字体,并不会小的看不清,因为webview提供了逻辑分辨率的概念。  
如果不在meta里设置,默认下小米note的放大系数scale是3,就是会放大3倍显示。  
也就是对于HTML而言,小米note的高度是1920/3=640px。  
如果网页是全屏的,没有顶部状态栏,那么一个640px高的div将撑满屏幕高度。  

获取屏幕、顶部状态栏和软键盘的高度

1. 屏幕的高度

获取屏幕的高度很简单,HTML自带了screen.height,直接就可以得到屏幕的整体高度,单位是px,物理分辨率值(不是HTML的逻辑分辨率)。  
HTML5Plus里提供了plus.screen.resolutionHeight方法,也是屏幕高度,但这个值是逻辑分辨率的高度。参考[http://html5plus.org/doc/zh_cn/device.html#plus.screen](http://html5plus.org/doc/zh_cn/device.html#plus.screen)  
screen.height = plus.screen.resolutionHeight*plus.screen.scale  

2. 获取webview高度

虽然webview的getStyle可以得到webview的高度,但开发者如果不手动设置的话,默认值是100%,这个值对本文探讨的问题没有意义。我们需要px的物理高度。  
在Android里通过如下js代码可以得到webview的高度:plus.android.invoke(plus.android.currentWebview(),"getHeight")   
这是一段Native.js代码,由于Android的webview原生对象就有getHeight()方法,所以就可以直接这样调用。  
当然webview原生对象还有很多方法属性都可以调,具体参阅[Native.js入门](http://ask.dcloud.net.cn/docs/#http://ask.dcloud.net.cn/article/88)  
图1(此图中,物理分辨率是蓝色字体,逻辑分辨率是黑色字体) 对于iOS,就没必要使用Native.js这么复杂的技术了,iOS的屏幕高度是固定的几个,直接判断屏幕高度和设备型号就可以了。

3. 获取状态栏高度

顶部状态栏比较复杂,如果webview设了全屏,那么状态栏高度就为0。  
如果设置了沉浸式状态栏,状态栏透明了,虽然状态栏存在理论高度,但webview高度是全屏的。  
plus.navigator.getStatusbarHeight(),这个api专门获取状态栏高度。  

4. 获取输入法高度

当弹出输入法时,在Android上,webview的高度会自动减少,留出空间给软键盘。  
那么输入法高度=屏幕高度-状态栏高度-webview高度  
在小米note上,默认输入法高度是 863px。  
图2(此图中,物理分辨率是蓝色字体,逻辑分辨率是黑色字体)

5. 流应用任务栏高度

在流应用环境中,比如iOS流应用,会有一个流应用任务栏,这个任务栏的出现会挤压webview的高度。  
如果开发者要设置一个元素居底,请使用bottom方法,而不是通过屏幕高度-webview高度设top,以避免出现流应用任务栏时高度错位。  

说说HTML里的几个高度

其实HTML自己也提供了很多高度相关的api,除了screen.height还有document.body.clientHeight,document.body.scrollHeight,感觉也很容易混淆。  
document.body.clientHeight是网页内容区的有效高度,符合盒模型。document.body.scrollHeight是网页可滚动区域的高度。  
如图2所示:  
- 此时document.body.clientHeight小于webview高度,而document.body.scrollHeight等于webview高度,即document.body.scrollHeight=plus.android.invoke(plus.android.currentWebview(),"getHeight") 。  
- 如果数字不是1-5,而是1-10,那么document.body.clientHeight会继续增加,但内容不够多,不会滚动,document.body.scrollHeight不变,仍是webview高度。  
- 如果网页内容非常多,数字到了40,那document.body.clientHeight会继续增加,并且只要发生了滚动,则document.body.clientHeight=document.body.scrollHeight  

其他相关知识。

  • 监控软键盘是否弹出,Android上可以使用HTML自带的resize事件。
    但是要判断横竖屏,因为横竖屏切换也会造成resize,所以高度变而宽度不变,就是软键盘弹出或消失了。
  • 强制弹出键盘,也有Native.js示例,http://ask.dcloud.net.cn/question/2324
  • hello uni-app和hello mui都有聊天示例模板,有底部输入框跟随键盘顶起的示例,可以参考下。
收起阅读 »

自动+手动在线升级

升级 更新

文章在这
贴错了
http://ask.dcloud.net.cn/question/5022

文章在这
贴错了
http://ask.dcloud.net.cn/question/5022

手动检测版本更新的代码

更新 升级

官方提供了自动检测更新的代码,我这里稍微更改了一下,可以手动检测更新。

不过建议使用App资源在线升级更新
我这里也提供了自动+手动的App资源在线升级

<ul class="mui-table-view" style="margin-top: 25px;">  
                <li class="mui-table-view-cell">  
                    <a id="onlineupgrade">  
                    在线升级 <span class="mui-pull-right" id="supgrade" style="color: red;display: none;">  
                        New  
                    </span>  
                    </a>  
                </li>  
            </ul>
    mui.plusReady(function() {  

            var supgrade = document.getElementById("supgrade");  

                //有新版本显示提示  
            mui.initUpdate(function() {  
                supgrade.style.display = "inline-block";  
            });  
            document.getElementById("onlineupgrade").addEventListener('tap', function() {  
                if (supgrade.style.display != "none") {  
                    mui.initUpdate();  
                }  
            });  
        });
(function($) {  
    //正式上线了请更改app更新地址  
    var server = "http://www.dcloud.io/helloh5/update.json", //获取升级描述文件服务器地址  
        localDir = "update",  
        localFile = "update.json", //本地保存升级描述目录和文件名  
        keyUpdate = "updateCheck", //取消升级键名  
        keyAbort = "updateAbort", //忽略版本键名  
        checkInterval = 3600000, //升级检查间隔,单位为ms,1小时为60*60*1000=3600000, 如果每次启动需要检查设置值为0  
        dir = null;  

    /**  
     * 准备升级操作  
     * 创建升级文件保存目录  
     * @param{Function} 有回调函数是手动检查折升级  
     */  
    $.initUpdate = function(callback) {  

        // 打开doc根目录  
        plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {  
            fs.root.getDirectory(localDir, {  
                create: true  
            }, function(entry) {  
                dir = entry;  
                checkUpdate(callback);  
            }, function(e) {  
                console.log("准备升级操作,打开update目录失败:" + e.message);  
            });  
        }, function(e) {  
            console.log("准备升级操作,打开doc目录失败:" + e.message);  
        });  
    }  

    /**  
     * 检测程序升级  
     * @author liuyf  2015-04-27  
     * @description 手动检测是否升级  是否已过期等标记在手动下无效  
     *  
     * @param{Function} 有回调函数是手动检查折升级  
     * */  
    function checkUpdate(callback) {  
        // 判断升级检测是否过期  
        var lastcheck = plus.storage.getItem(keyUpdate);  
        if (lastcheck) {  
            // 取消已过期,删除取消标记  
            plus.storage.removeItem(keyUpdate);  
        }  
        // 读取本地升级文件]  
        dir.getFile(localFile, {  
            create: false  
        }, function(fentry) {  
            fentry.file(function(file) {  
                var reader = new plus.io.FileReader();  
                reader.onloadend = function(e) {  
                    //检测升级的时候不删除本地升级文件  
                    if (!callback) {  
                        fentry.remove();  
                    }  
                    var data = null;  
                    try {  
                        data = JSON.parse(e.target.result);  
                    } catch (e) {  
                        console.log("读取本地升级文件,数据格式错误!");  
                    }  
                    checkUpdateData(data, callback);  
                }  
                reader.readAsText(file);  
            }, function(e) {  
                console.log("读取本地升级文件,获取文件对象失败:" + e.message);  
                fentry.remove();  
            });  
        }, function(e) {  
            // 失败表示文件不存在,从服务器获取升级数据  
            getUpdateData();  
        });  
    }  

    /**  
     * 检查升级数据  
     * @author liuyf 2015-04-27  
     * @description 手动升级 是否存在版本号等逻辑应舍弃  
     * @param{Function} 有回调函数是手动检查升级  
     */  
    function checkUpdateData(j, callback) {  
        //当前客户端版本号  
        var curVer = plus.runtime.version,  
            inf = j[plus.os.name];  

        if (inf) {  
            var srvVer = inf.version;  
            // 判断是否需要升级  
            if (compareVersion(curVer, srvVer)) {  
                if (!callback) {  
                    // 提示用户是否升级  
                    plus.ui.confirm(inf.note, function(i) {  
                        if (0 == i)  
                            plus.runtime.openURL(inf.url);  
                        else {  
                            plus.storage.setItem(keyUpdate, (new Date()).getTime().toString());  
                        }  
                    }, inf.title, ["立即更新", "取  消"]);  
                } else {  
                    callback();  
                }  
            }  
        }  
    }  

    /**  
     * 从服务器获取升级数据,并存储到本地;  
     */  
    function getUpdateData() {  
        mui.getJSON(server, {}, function(data) {  
            //appid一致,才将服务器上的版本数据保存到本地  
            if (data.appid == plus.runtime.appid) {  
                // 保存到本地文件中  
                dir.getFile(localFile, {  
                    create: true  
                }, function(fentry) {;  
                    fentry.createWriter(function(writer) {  
                        writer.onerror = function() {  
                            console.log("获取升级数据,保存文件失败!");  
                        }  
                        writer.write(data);  
                    }, function(e) {  
                        console.log("获取升级数据,创建写文件对象失败:" + e.message);  
                    });  
                }, function(e) {  
                    console.log("获取升级数据,打开保存文件失败:" + e.message);  
                });  
            }  
        });  
    }  

    /**  
     * 比较版本大小,如果新版本nv大于旧版本ov则返回true,否则返回false  
     * @param {String} ov  
     * @param {String} nv  
     * @return {Boolean}  
     */  
    function compareVersion(ov, nv) {  
        if (!ov || !nv || ov == "" || nv == "") {  
            return false;  
        }  
        var b = false,  
            ova = ov.split(".", 4),  
            nva = nv.split(".", 4);  
        for (var i = 0; i < ova.length && i < nva.length; i++) {  
            var so = ova[i],  
                no = parseInt(so),  
                sn = nva[i],  
                nn = parseInt(sn);  
            if (nn > no || sn.length > so.length) {  
                return true;  
            } else if (nn < no) {  
                return false;  
            }  
        }  
        if (nva.length > ova.length && 0 == nv.indexOf(ov)) {  
            return true;  
        }  
    }  
})(mui);
继续阅读 »

官方提供了自动检测更新的代码,我这里稍微更改了一下,可以手动检测更新。

不过建议使用App资源在线升级更新
我这里也提供了自动+手动的App资源在线升级

<ul class="mui-table-view" style="margin-top: 25px;">  
                <li class="mui-table-view-cell">  
                    <a id="onlineupgrade">  
                    在线升级 <span class="mui-pull-right" id="supgrade" style="color: red;display: none;">  
                        New  
                    </span>  
                    </a>  
                </li>  
            </ul>
    mui.plusReady(function() {  

            var supgrade = document.getElementById("supgrade");  

                //有新版本显示提示  
            mui.initUpdate(function() {  
                supgrade.style.display = "inline-block";  
            });  
            document.getElementById("onlineupgrade").addEventListener('tap', function() {  
                if (supgrade.style.display != "none") {  
                    mui.initUpdate();  
                }  
            });  
        });
(function($) {  
    //正式上线了请更改app更新地址  
    var server = "http://www.dcloud.io/helloh5/update.json", //获取升级描述文件服务器地址  
        localDir = "update",  
        localFile = "update.json", //本地保存升级描述目录和文件名  
        keyUpdate = "updateCheck", //取消升级键名  
        keyAbort = "updateAbort", //忽略版本键名  
        checkInterval = 3600000, //升级检查间隔,单位为ms,1小时为60*60*1000=3600000, 如果每次启动需要检查设置值为0  
        dir = null;  

    /**  
     * 准备升级操作  
     * 创建升级文件保存目录  
     * @param{Function} 有回调函数是手动检查折升级  
     */  
    $.initUpdate = function(callback) {  

        // 打开doc根目录  
        plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {  
            fs.root.getDirectory(localDir, {  
                create: true  
            }, function(entry) {  
                dir = entry;  
                checkUpdate(callback);  
            }, function(e) {  
                console.log("准备升级操作,打开update目录失败:" + e.message);  
            });  
        }, function(e) {  
            console.log("准备升级操作,打开doc目录失败:" + e.message);  
        });  
    }  

    /**  
     * 检测程序升级  
     * @author liuyf  2015-04-27  
     * @description 手动检测是否升级  是否已过期等标记在手动下无效  
     *  
     * @param{Function} 有回调函数是手动检查折升级  
     * */  
    function checkUpdate(callback) {  
        // 判断升级检测是否过期  
        var lastcheck = plus.storage.getItem(keyUpdate);  
        if (lastcheck) {  
            // 取消已过期,删除取消标记  
            plus.storage.removeItem(keyUpdate);  
        }  
        // 读取本地升级文件]  
        dir.getFile(localFile, {  
            create: false  
        }, function(fentry) {  
            fentry.file(function(file) {  
                var reader = new plus.io.FileReader();  
                reader.onloadend = function(e) {  
                    //检测升级的时候不删除本地升级文件  
                    if (!callback) {  
                        fentry.remove();  
                    }  
                    var data = null;  
                    try {  
                        data = JSON.parse(e.target.result);  
                    } catch (e) {  
                        console.log("读取本地升级文件,数据格式错误!");  
                    }  
                    checkUpdateData(data, callback);  
                }  
                reader.readAsText(file);  
            }, function(e) {  
                console.log("读取本地升级文件,获取文件对象失败:" + e.message);  
                fentry.remove();  
            });  
        }, function(e) {  
            // 失败表示文件不存在,从服务器获取升级数据  
            getUpdateData();  
        });  
    }  

    /**  
     * 检查升级数据  
     * @author liuyf 2015-04-27  
     * @description 手动升级 是否存在版本号等逻辑应舍弃  
     * @param{Function} 有回调函数是手动检查升级  
     */  
    function checkUpdateData(j, callback) {  
        //当前客户端版本号  
        var curVer = plus.runtime.version,  
            inf = j[plus.os.name];  

        if (inf) {  
            var srvVer = inf.version;  
            // 判断是否需要升级  
            if (compareVersion(curVer, srvVer)) {  
                if (!callback) {  
                    // 提示用户是否升级  
                    plus.ui.confirm(inf.note, function(i) {  
                        if (0 == i)  
                            plus.runtime.openURL(inf.url);  
                        else {  
                            plus.storage.setItem(keyUpdate, (new Date()).getTime().toString());  
                        }  
                    }, inf.title, ["立即更新", "取  消"]);  
                } else {  
                    callback();  
                }  
            }  
        }  
    }  

    /**  
     * 从服务器获取升级数据,并存储到本地;  
     */  
    function getUpdateData() {  
        mui.getJSON(server, {}, function(data) {  
            //appid一致,才将服务器上的版本数据保存到本地  
            if (data.appid == plus.runtime.appid) {  
                // 保存到本地文件中  
                dir.getFile(localFile, {  
                    create: true  
                }, function(fentry) {;  
                    fentry.createWriter(function(writer) {  
                        writer.onerror = function() {  
                            console.log("获取升级数据,保存文件失败!");  
                        }  
                        writer.write(data);  
                    }, function(e) {  
                        console.log("获取升级数据,创建写文件对象失败:" + e.message);  
                    });  
                }, function(e) {  
                    console.log("获取升级数据,打开保存文件失败:" + e.message);  
                });  
            }  
        });  
    }  

    /**  
     * 比较版本大小,如果新版本nv大于旧版本ov则返回true,否则返回false  
     * @param {String} ov  
     * @param {String} nv  
     * @return {Boolean}  
     */  
    function compareVersion(ov, nv) {  
        if (!ov || !nv || ov == "" || nv == "") {  
            return false;  
        }  
        var b = false,  
            ova = ov.split(".", 4),  
            nva = nv.split(".", 4);  
        for (var i = 0; i < ova.length && i < nva.length; i++) {  
            var so = ova[i],  
                no = parseInt(so),  
                sn = nva[i],  
                nn = parseInt(sn);  
            if (nn > no || sn.length > so.length) {  
                return true;  
            } else if (nn < no) {  
                return false;  
            }  
        }  
        if (nva.length > ova.length && 0 == nv.indexOf(ov)) {  
            return true;  
        }  
    }  
})(mui);
收起阅读 »

IOS离线打包的那些梗,搞定了真高兴!

这几天要发布新版本,打开Hbuilder准备在线打包,然后上次发布的悲剧重演,试过所有的p12证书之后,还是报错。
于是拿脑袋砸墙5分钟--------疼!
想起来前几天好不容易下到的xcode6.3(之前测试过离线,demo怎么跑都跑不出来,后来找个高手才发现,我的xcode是4.2的,离线打包必须要xcode 5.0,然后下了起码10个xcode的img,才找到个好用的),不如离线打包吧!
就这么愉快的决定了,于是就有了下面的各种梗:

> 我的操作步骤是这样的:
1.下载最新的官方包 HTML 5+ SDK 更新日志

  1. 拷贝官方的项目出来
  2. 按照官方的要求修改必要的参数 (附官方的说明:iOS离线打包秘籍
  3. 联真机测试 (我的以前弄的,请百度之)
  4. 打包测试 (菜单位置Product->Archive)
  5. 提交审核

1.manifest.json的各种错误

一定要检查json的格式是否正确,我碰到的情况是用apple自己带的编辑器(不是xcode)编辑了之后,居然json就破了
这是xcode报错的内容截图没保留,大概的内容是有关键字(jsonvalue)的,要是你碰到这个问题,请检查manifest.json。
必须要对的地方如下图:

要是你改错了,哪怕是各双引号,那就是这样的:


友情提示:
1.拷贝demo里的过来用,再改改,因为云端打包需要的参数都在里面,但离线打包的时候是不需要的

  1. 直接用xcode改,防止编码问题,双引号一直不对的话,请用ctrl+C和ctrl+V
  2. 网上有json检查工具,看填写的是否完整
    4.要是你的入口文件不是index.html,请别忘记修改哦

2. xcode闪退(这个奇葩问题请翻到最后)

真是奇葩~
真是奇葩~~
真是奇葩~~~
真是奇葩~~~~
真是奇葩~

3. input点选出来的是英文菜单

想象中的菜单是这样的:


现实是这样的:

解决的办法是这样的:
需要添加一行对中文的支持,就是图中用红框框出来的部分

4. 头部的样式设置错误

想象中的样子是这样的:

现实是这样的:

解决的办法是这样的:
需要添加一行,就是图中用红框框出来的部分,注意选成no
官方的状态栏设置的介绍navigator用于管理浏览器运行环境信息

5. 精简文件大小

好不容易Archive过了,才发现文件真大啊,居然又30多MB
就不停的去除库,对IOS的开发不了解,我看着不爽就删除,不敢瞎说,只知道库还是很大的

6. 64位支持

看到这个界面的时候我好高兴啊,但是苹果叔叔却不停的跳提示


解决的办法是这样的,看到红框的部分了嘛?改成no就行了

7. PUSH的问题

这个问题,是在一路绿灯之后,好高兴啊,去developer center写新版本特点了,然后...


苹果给我发了一封邮件,内容如下:

于是又开始了一路的排除bug
解决的办法是把这部分的代码注释掉

百度地图的部分也可以注释掉(要是你没用到的话)

-----------------------------奇葩故事分隔线-----------------------------

终于到最后的了,我们讲讲奇葩的故事

事情发生在1号房APP真机调试正常之后,当时的状况如下:

1号房APP:真机运行正常,打包时候xcode闪退
Hello5Demo:真机运行正常,打包正常
新建空项目:真机运行正常,打包正常

先是修改xcode里的参数无数,一点结果也没有,xcode一如既往的闪退,我那个胸闷啊。
不过大神就是大神,很淡定的说:“我们再找个人问问”,于是又出现一位美女大神,也很淡定,发布APP无数,看了几个参数之后,淡淡的说:“把你写的东西都删了~~”
当时,我说就听到这话的当时,我的下巴掉下来了,心里默念“那我不都白写了,我的青春啊~~”
“再把你写的一部分一部分的加进去~~”
好吧,我承认淡定有时候要人命啊!

接下来就是纯体力的活动:
开始清空Pandora/apps/yihaofang/www/目录下的所有文件
打包正常....
加入根目录下的独立文件
打包正常....
加入css目录
打包正常....
加入js目录
打包正常....
加入img目录
打包正常....
加入html目录
xcode闪退!

原因找到了,但是,但是这个目录没法用话,我的APP还是白写了
继续革命啊
清空html目录
xcode闪退!

到这里的时候,我看到大神们也露出了惊讶的表情,难道一个目录的名称会导致xcode闪退!
大神冷静了一下说:“改各文件夹名看看”
改目录名为htmla
打包正常....
在htmla中加入所有文件
打包正常....

幸福的泪花在我眼眶中打转(请原谅我的文艺青年本质),这就行啦?!
于是我作出了一个重要的决定
把htmla改成html
打包正常....

这就是这朵奇葩,详细原因不知道,只知道是奇葩~~
-----------------------------奇葩故事分隔线-----------------------------

其他会遇到的问题
各种证书的问题,因为之前发布过一次,这次发布就没有这么多的问题,隐约记得当时也是鸡毛一地
重点看下面这篇文章,仔细操作就能过
iOS证书(.p12)和描述文件(.mobileprovision)申请

最后,特别感谢几位朋友,排名不分先后
Hbuiler:有了这么方便的工具,web工程师转做APP也是妥妥的
两位大神:没有你们,也许很久之后1号房才能上线
Hbuilder热心回答我问题的 DCloud-App-Array(colour), DCloud_iOS_XTY(我)

继续阅读 »

这几天要发布新版本,打开Hbuilder准备在线打包,然后上次发布的悲剧重演,试过所有的p12证书之后,还是报错。
于是拿脑袋砸墙5分钟--------疼!
想起来前几天好不容易下到的xcode6.3(之前测试过离线,demo怎么跑都跑不出来,后来找个高手才发现,我的xcode是4.2的,离线打包必须要xcode 5.0,然后下了起码10个xcode的img,才找到个好用的),不如离线打包吧!
就这么愉快的决定了,于是就有了下面的各种梗:

> 我的操作步骤是这样的:
1.下载最新的官方包 HTML 5+ SDK 更新日志

  1. 拷贝官方的项目出来
  2. 按照官方的要求修改必要的参数 (附官方的说明:iOS离线打包秘籍
  3. 联真机测试 (我的以前弄的,请百度之)
  4. 打包测试 (菜单位置Product->Archive)
  5. 提交审核

1.manifest.json的各种错误

一定要检查json的格式是否正确,我碰到的情况是用apple自己带的编辑器(不是xcode)编辑了之后,居然json就破了
这是xcode报错的内容截图没保留,大概的内容是有关键字(jsonvalue)的,要是你碰到这个问题,请检查manifest.json。
必须要对的地方如下图:

要是你改错了,哪怕是各双引号,那就是这样的:


友情提示:
1.拷贝demo里的过来用,再改改,因为云端打包需要的参数都在里面,但离线打包的时候是不需要的

  1. 直接用xcode改,防止编码问题,双引号一直不对的话,请用ctrl+C和ctrl+V
  2. 网上有json检查工具,看填写的是否完整
    4.要是你的入口文件不是index.html,请别忘记修改哦

2. xcode闪退(这个奇葩问题请翻到最后)

真是奇葩~
真是奇葩~~
真是奇葩~~~
真是奇葩~~~~
真是奇葩~

3. input点选出来的是英文菜单

想象中的菜单是这样的:


现实是这样的:

解决的办法是这样的:
需要添加一行对中文的支持,就是图中用红框框出来的部分

4. 头部的样式设置错误

想象中的样子是这样的:

现实是这样的:

解决的办法是这样的:
需要添加一行,就是图中用红框框出来的部分,注意选成no
官方的状态栏设置的介绍navigator用于管理浏览器运行环境信息

5. 精简文件大小

好不容易Archive过了,才发现文件真大啊,居然又30多MB
就不停的去除库,对IOS的开发不了解,我看着不爽就删除,不敢瞎说,只知道库还是很大的

6. 64位支持

看到这个界面的时候我好高兴啊,但是苹果叔叔却不停的跳提示


解决的办法是这样的,看到红框的部分了嘛?改成no就行了

7. PUSH的问题

这个问题,是在一路绿灯之后,好高兴啊,去developer center写新版本特点了,然后...


苹果给我发了一封邮件,内容如下:

于是又开始了一路的排除bug
解决的办法是把这部分的代码注释掉

百度地图的部分也可以注释掉(要是你没用到的话)

-----------------------------奇葩故事分隔线-----------------------------

终于到最后的了,我们讲讲奇葩的故事

事情发生在1号房APP真机调试正常之后,当时的状况如下:

1号房APP:真机运行正常,打包时候xcode闪退
Hello5Demo:真机运行正常,打包正常
新建空项目:真机运行正常,打包正常

先是修改xcode里的参数无数,一点结果也没有,xcode一如既往的闪退,我那个胸闷啊。
不过大神就是大神,很淡定的说:“我们再找个人问问”,于是又出现一位美女大神,也很淡定,发布APP无数,看了几个参数之后,淡淡的说:“把你写的东西都删了~~”
当时,我说就听到这话的当时,我的下巴掉下来了,心里默念“那我不都白写了,我的青春啊~~”
“再把你写的一部分一部分的加进去~~”
好吧,我承认淡定有时候要人命啊!

接下来就是纯体力的活动:
开始清空Pandora/apps/yihaofang/www/目录下的所有文件
打包正常....
加入根目录下的独立文件
打包正常....
加入css目录
打包正常....
加入js目录
打包正常....
加入img目录
打包正常....
加入html目录
xcode闪退!

原因找到了,但是,但是这个目录没法用话,我的APP还是白写了
继续革命啊
清空html目录
xcode闪退!

到这里的时候,我看到大神们也露出了惊讶的表情,难道一个目录的名称会导致xcode闪退!
大神冷静了一下说:“改各文件夹名看看”
改目录名为htmla
打包正常....
在htmla中加入所有文件
打包正常....

幸福的泪花在我眼眶中打转(请原谅我的文艺青年本质),这就行啦?!
于是我作出了一个重要的决定
把htmla改成html
打包正常....

这就是这朵奇葩,详细原因不知道,只知道是奇葩~~
-----------------------------奇葩故事分隔线-----------------------------

其他会遇到的问题
各种证书的问题,因为之前发布过一次,这次发布就没有这么多的问题,隐约记得当时也是鸡毛一地
重点看下面这篇文章,仔细操作就能过
iOS证书(.p12)和描述文件(.mobileprovision)申请

最后,特别感谢几位朋友,排名不分先后
Hbuiler:有了这么方便的工具,web工程师转做APP也是妥妥的
两位大神:没有你们,也许很久之后1号房才能上线
Hbuilder热心回答我问题的 DCloud-App-Array(colour), DCloud_iOS_XTY(我)

收起阅读 »

分享图片压缩上传demo,可以选择一张或多张图片也可以拍摄照片

图片裁剪 图片压缩

2016-08-05更新:
下方的代码是比较OLD的了,是通过js进行图片的剪切 旋转 再生成,效率较低。
后来又整合了一个利用native.js本地接口的压缩代码 ,链接在这
。页面中有详细的说明,需要的童鞋们可以参考以下。

代码整合了
1.多串君

  1. 伟子
    两个人的demo,
    其中resize原来的filereader在5 中更改为plus.io.FileReader()方不报错。
    如有错误,请不吝批评指正。

更新日志:
2015-05-09 1020450921@qq.com
1.修复了ios下无法获取图片宽高的问题:
ios下不在img.onload中是获取不到文件对象的。

  1. 新增了最大宽度(高度)的判别,按比例压缩。
    具体代码如下:

    
    <!DOCTYPE html>  
    <html>  
    
    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title></title>  
        <link href="http://ask.dcloud.net.cn/../../../css/mui.min.css" rel="stylesheet" />  
        <style type="text/css">  
            body {  
                background-color: #efeff4;  
            }  
            .mui-content {} .mui-content a {  
                color: #8F8F94;  
            }  
            .mui-content a.active {  
                color: #007aff;  
            }  
            .mui-title {  
                font-family: simhei;  
            }  
            .btn_1 {  
                position: absolute;  
                bottom: 100px;  
                left: 10px;  
                right: 10px;  
            }  
            .btn_2 {  
                position: absolute;  
                bottom: 20px;  
                left: 10px;  
                right: 10px;  
            }  
            .mui-btn-block {  
                width: 90%;  
                margin: 0 auto;  
            }  
            body {  
                overflow: hidden;  
            }  
            .showimg {  
                margin: 20px 10px auto 10px;  
                text-align: center;  
            }  
        </style>  
    </head>  
    
    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">上传身份证照片</h1>  
            <a class="mui-pull-right mui-icon mui-icon-upload" onclick="imgupgrade()"></a>  
        </header>  
        <!--  
    作者:1020450921@qq.com  
    时间:2015-04-24  
    描述:参考 http://ask.dcloud.net.cn/question/2089  
    -->  
        <div class="mui-content">  
            <div class="showimg">  
    
            </div>  
            <button type="button" class="mui-btn mui-btn-primary mui-btn-block  btn_1" onclick="galleryImgs()">从相册中选择图片</button>  
            <br>  
            <button type="button" class="mui-btn mui-btn-success mui-btn-block btn_2" onclick="cameraimages()">拍照</button>  
        </div>  
    </body>  
    <script src="../../../js/mui.min.js"></script>  
    <script src="../../../js/binaryajax.js" type="text/javascript" charset="utf-8"></script>  
    <script src="../../../js/exif.js" type="text/javascript" charset="utf-8"></script>  
    <script src="../../../js/canvasResize.js" type="text/javascript" charset="utf-8"></script>  
    
    <script type="text/javascript">  
        mui.init();  
        mui.plusReady(function() {})  
    
               //上传单张图片  
        function galleryImg() {  
            //每次拍摄或选择图片前清空数组对象  
            f1.splice(0, f1.length);  
            document.getElementsByClassName("showimg")[0].innerHTML = null;  
            // 从相册中选择图片  
            mui.toast("从相册中选择一张图片");  
            plus.gallery.pick(function(path) {  
                showImg(path);  
            }, function(e) {  
                mui.toast("取消选择图片");  
            }, {  
                filter: "image",  
                multiple: false  
            });  
        }  
    
        function galleryImgs() {  
                //每次拍摄或选择图片前清空数组对象  
                f1.splice(0, f1.length);  
                document.getElementsByClassName("showimg")[0].innerHTML = null;  
                // 从相册中选择图片  
                mui.toast("从相册中选择不超过两张图片");  
                plus.gallery.pick(function(e) {  
                    if (e.files.length != 2) {  
                        mui.toast('请选择身份证正面和背面照片共两张');  
                        return false;  
                    }  
                    for (var i in e.files) {  
                        showImg(e.files[i]);  
                    }  
                }, function(e) {  
                    mui.toast("取消选择图片");  
                }, {  
                    filter: "image",  
                    multiple: true  
                });  
            }  
            // 拍照添加文件  
    
        function cameraimages() {  
                //每次拍摄或选择图片前清空数组对象  
                f1.splice(0, f1.length);  
                document.getElementsByClassName("showimg")[0].innerHTML = null;  
                var cmr = plus.camera.getCamera();  
                cmr.captureImage(function(p) {  
                    plus.io.resolveLocalFileSystemURL(p, function(entry) {  
                        var localurl = entry.toLocalURL(); //把拍照的目录路径,变成本地url路径,例如file:///........之类的。  
                        showImg(localurl);  
                    });  
                }, function(e) {  
                    mui.toast("很抱歉,获取失败 "   e);  
                });  
            }  
            // 全局数组对象,添加文件,用于压缩上传使用  
        var f1 = new Array();  

function showImg(url) {
// 兼容以“file:”开头的情况
if (0 != url.toString().indexOf("file://")) {
url = "file://" + url;
}
var div = document.getElementsByClassName("showimg")[0];
var img = new Image();
img.src = url; // 传过来的图片路径在这里用。
img.onclick = function() {
plus.runtime.openFile(url);
};
img.onload = function() {
var tmph = img.height;
var tmpw = img.width;
var isHengTu = tmpw > tmph;
var max = Math.max(tmpw, tmph);
var min = Math.min(tmpw, tmph);
var bili = min / max;
if (max > 1200) {
max = 1200;
min = Math.floor(bili * max);
}
tmph = isHengTu ? min : max;
tmpw = isHengTu ? max : min;
img.style.border = "1px solid rgb(200,199,204)";
img.style.margin = "10px";
img.style.width = "150px";
img.style.height = "150px";
img.onload = null;
plus.io.resolveLocalFileSystemURL(url, function(entry) {
entry.file(function(file) {
console.log(file.size + '--' + file.name);
canvasResize(file, {
width: tmpw,
height: tmph,
crop: false,
quality: 50, //压缩质量
rotate: 0,
callback: function(data, width, height) {
f1.push(data);
img.src = data;
div.appendChild(img);
plus.nativeUI.closeWaiting();
}
});
});
},
function(e) {
plus.nativeUI.closeWaiting();
console.log(e.message);
});
};
};

    function imgupgrade() {  
            mui.toast('后台联调时启用上传功能');  
            return;  
            var wt = plus.nativeUI.showWaiting();  
            var url = '后台地址';  
            var dataType = 'json';  
            //发送数据    
            var data = {  
                files1: f1 //base64数据          
            };  
            mui.post(url, data, success, dataType);  
        }  
        //成功响应的回调函数  
    var success = function(response) {  
        plus.nativeUI.closeWaiting();  
        if (response != null) {  
            alert("上传成功");  
        }  
    }  
</script>  

</html>



伟子的js代码我放在了附件中。
继续阅读 »

2016-08-05更新:
下方的代码是比较OLD的了,是通过js进行图片的剪切 旋转 再生成,效率较低。
后来又整合了一个利用native.js本地接口的压缩代码 ,链接在这
。页面中有详细的说明,需要的童鞋们可以参考以下。

代码整合了
1.多串君

  1. 伟子
    两个人的demo,
    其中resize原来的filereader在5 中更改为plus.io.FileReader()方不报错。
    如有错误,请不吝批评指正。

更新日志:
2015-05-09 1020450921@qq.com
1.修复了ios下无法获取图片宽高的问题:
ios下不在img.onload中是获取不到文件对象的。

  1. 新增了最大宽度(高度)的判别,按比例压缩。
    具体代码如下:

    
    <!DOCTYPE html>  
    <html>  
    
    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title></title>  
        <link href="http://ask.dcloud.net.cn/../../../css/mui.min.css" rel="stylesheet" />  
        <style type="text/css">  
            body {  
                background-color: #efeff4;  
            }  
            .mui-content {} .mui-content a {  
                color: #8F8F94;  
            }  
            .mui-content a.active {  
                color: #007aff;  
            }  
            .mui-title {  
                font-family: simhei;  
            }  
            .btn_1 {  
                position: absolute;  
                bottom: 100px;  
                left: 10px;  
                right: 10px;  
            }  
            .btn_2 {  
                position: absolute;  
                bottom: 20px;  
                left: 10px;  
                right: 10px;  
            }  
            .mui-btn-block {  
                width: 90%;  
                margin: 0 auto;  
            }  
            body {  
                overflow: hidden;  
            }  
            .showimg {  
                margin: 20px 10px auto 10px;  
                text-align: center;  
            }  
        </style>  
    </head>  
    
    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">上传身份证照片</h1>  
            <a class="mui-pull-right mui-icon mui-icon-upload" onclick="imgupgrade()"></a>  
        </header>  
        <!--  
    作者:1020450921@qq.com  
    时间:2015-04-24  
    描述:参考 http://ask.dcloud.net.cn/question/2089  
    -->  
        <div class="mui-content">  
            <div class="showimg">  
    
            </div>  
            <button type="button" class="mui-btn mui-btn-primary mui-btn-block  btn_1" onclick="galleryImgs()">从相册中选择图片</button>  
            <br>  
            <button type="button" class="mui-btn mui-btn-success mui-btn-block btn_2" onclick="cameraimages()">拍照</button>  
        </div>  
    </body>  
    <script src="../../../js/mui.min.js"></script>  
    <script src="../../../js/binaryajax.js" type="text/javascript" charset="utf-8"></script>  
    <script src="../../../js/exif.js" type="text/javascript" charset="utf-8"></script>  
    <script src="../../../js/canvasResize.js" type="text/javascript" charset="utf-8"></script>  
    
    <script type="text/javascript">  
        mui.init();  
        mui.plusReady(function() {})  
    
               //上传单张图片  
        function galleryImg() {  
            //每次拍摄或选择图片前清空数组对象  
            f1.splice(0, f1.length);  
            document.getElementsByClassName("showimg")[0].innerHTML = null;  
            // 从相册中选择图片  
            mui.toast("从相册中选择一张图片");  
            plus.gallery.pick(function(path) {  
                showImg(path);  
            }, function(e) {  
                mui.toast("取消选择图片");  
            }, {  
                filter: "image",  
                multiple: false  
            });  
        }  
    
        function galleryImgs() {  
                //每次拍摄或选择图片前清空数组对象  
                f1.splice(0, f1.length);  
                document.getElementsByClassName("showimg")[0].innerHTML = null;  
                // 从相册中选择图片  
                mui.toast("从相册中选择不超过两张图片");  
                plus.gallery.pick(function(e) {  
                    if (e.files.length != 2) {  
                        mui.toast('请选择身份证正面和背面照片共两张');  
                        return false;  
                    }  
                    for (var i in e.files) {  
                        showImg(e.files[i]);  
                    }  
                }, function(e) {  
                    mui.toast("取消选择图片");  
                }, {  
                    filter: "image",  
                    multiple: true  
                });  
            }  
            // 拍照添加文件  
    
        function cameraimages() {  
                //每次拍摄或选择图片前清空数组对象  
                f1.splice(0, f1.length);  
                document.getElementsByClassName("showimg")[0].innerHTML = null;  
                var cmr = plus.camera.getCamera();  
                cmr.captureImage(function(p) {  
                    plus.io.resolveLocalFileSystemURL(p, function(entry) {  
                        var localurl = entry.toLocalURL(); //把拍照的目录路径,变成本地url路径,例如file:///........之类的。  
                        showImg(localurl);  
                    });  
                }, function(e) {  
                    mui.toast("很抱歉,获取失败 "   e);  
                });  
            }  
            // 全局数组对象,添加文件,用于压缩上传使用  
        var f1 = new Array();  

function showImg(url) {
// 兼容以“file:”开头的情况
if (0 != url.toString().indexOf("file://")) {
url = "file://" + url;
}
var div = document.getElementsByClassName("showimg")[0];
var img = new Image();
img.src = url; // 传过来的图片路径在这里用。
img.onclick = function() {
plus.runtime.openFile(url);
};
img.onload = function() {
var tmph = img.height;
var tmpw = img.width;
var isHengTu = tmpw > tmph;
var max = Math.max(tmpw, tmph);
var min = Math.min(tmpw, tmph);
var bili = min / max;
if (max > 1200) {
max = 1200;
min = Math.floor(bili * max);
}
tmph = isHengTu ? min : max;
tmpw = isHengTu ? max : min;
img.style.border = "1px solid rgb(200,199,204)";
img.style.margin = "10px";
img.style.width = "150px";
img.style.height = "150px";
img.onload = null;
plus.io.resolveLocalFileSystemURL(url, function(entry) {
entry.file(function(file) {
console.log(file.size + '--' + file.name);
canvasResize(file, {
width: tmpw,
height: tmph,
crop: false,
quality: 50, //压缩质量
rotate: 0,
callback: function(data, width, height) {
f1.push(data);
img.src = data;
div.appendChild(img);
plus.nativeUI.closeWaiting();
}
});
});
},
function(e) {
plus.nativeUI.closeWaiting();
console.log(e.message);
});
};
};

    function imgupgrade() {  
            mui.toast('后台联调时启用上传功能');  
            return;  
            var wt = plus.nativeUI.showWaiting();  
            var url = '后台地址';  
            var dataType = 'json';  
            //发送数据    
            var data = {  
                files1: f1 //base64数据          
            };  
            mui.post(url, data, success, dataType);  
        }  
        //成功响应的回调函数  
    var success = function(response) {  
        plus.nativeUI.closeWaiting();  
        if (response != null) {  
            alert("上传成功");  
        }  
    }  
</script>  

</html>



伟子的js代码我放在了附件中。
收起阅读 »

App资源在线差量升级更新

update wgt wgtu 升级 差量升级

官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见

5+应用可使用以下方式进行升级

本文重点介绍5+应用资源差量升级,相对App资源独立升级,因为只需要下载更新的资源文件,所以升级包体积更小,升级速度会更快。
注意:需HBuilder5.7.0以上版本才有此功能

生成移动App资源差量升级包

顾名思义,差量升级包是针对某个历史版本到新版本的差量,所以对于升级服务器来讲需要保留所有历史版本,并且分别生成每个历史版本到新版本的差量升级包。
目前HBuilder IDE并未提供版本控制系统,所以需要按照以下规则手动生成差量升级包:

  • 差量升级包文件格式为wgtu
    在HTML5+移动App支持的差量升级包文件后缀名必须是wgtu,它是标准的zip压缩文件,可以使用任何zip工具对wgtu目录进行压缩,并修改文件后缀名称为wgtu。
  • wgtu目录结构
名称 类型 说明
www 目录 保存需要更新的数据文件,其下的目录结构与应用的目录结构一致,其下必须包含manifest.json文件,否则认为更新文件非法,不执行升级操作
update.xml 文件 保存应用升级操作的配置信息,参考update.xml文件格式
  • update.xml文件格式(注意:文件名必须全部小写)
    update.xml为标准的xml文件格式,定义wgtu文件升级配置信息,其文件格式如下:
元素名称 父元素 类型 描述 必须
wgtu 根节点
appid wgtu 属性 升级应用的appid
basis wgtu 节点 基础应用信息
version basis 属性 基础应用的版本信息(旧版本的版本号
remove wgtu 节点 升级需要删除的文件/目录列表
item remove 节点 需要删除的项
path item 属性 需要删除的项的路径,相对于应用根目录,如“image/a.png”表示image目录下的a.png文件。如果是目录则以‘/’结尾,如“res/img/”表示res目录下的img目录,删除时包括下的所有内容。
  • wgtu示例
    应用升级前的版本1.0,其应用目录结构为:

    应用升级后的版本1.1,其应用目录结构为:

    差量升级包wgtu文件的目录结构为(不包括最外层wgtu目录):

    应用升级更新文件manifest.json、index.html,新增detail.html、image/new.png,删除image/icon5.png。
    其中update.xml中的内容为:
    <?xml version="1.0" encoding="utf-8" standalone="no"?>  
    <wgtu appid="TEST" >  
    <basis version="1.0" />  
    <remove>  
    <item path="image/icon5.png" />  
    </remove>  
    </wgtu>

应用中差量更新资源

下载wgtu文件并更新:

    var url='http://demo.dcloud.net.cn/helloh5/update/HelloH5.wgtu';  
    plus.nativeUI.showWaiting("升级中...");  
    var dtask = plus.downloader.createDownload( url, {method:"GET"}, function(d,status){  
        if ( status == 200 ) {   
            console.log( "Download wgtu success: " + d.filename );  
            plus.runtime.install(d.filename,{},function(){  
                plus.nativeUI.closeWaiting();  
                plus.nativeUI.alert("Update wgtu success, restart now!",function(){  
                    plus.runtime.restart();  
                });  
            },function(e){  
                plus.nativeUI.closeWaiting();  
                alert("Update wgtu failed: "+e.message);  
            });  
        } else {  
            plus.nativeUI.closeWaiting();  
             alert( "Download wgtu failed: " + status );   
        }   
    } );  
    dtask.addEventListener('statechanged',function(d,status){  
        console.log("statechanged: "+d.state);  
    });  
    dtask.start();

注意

  1. wgtu包中的update.xml文件名称必须全部小写,并且和www在同一级目录中
  2. wgtu包中www目录下必须包含manifest.json文件,并且里面不能包含注释(HBuilder中默认带注释,需要手动删除所有注释)
  3. update.xml中的appid值是应用的AppID(如“H5F6AE111”),不是程序的包名(如“io.dcloud.H5F6AE1111”)
  4. wgtu包必须是使用zip格式压缩的文件(不能使用如rar等其它压缩格式)

App store应用更新说明
应用资源更新肯定是违反apple政策的,但目前看起来它也不管。你在官网案例那里下载Appstore版本的那些app,大多启动后都会提示更新,反正也都没下架。如果你不是很大的公司,apple不会理睬你。如果你是大公司,建议不要做整体更新,每次更新几个页面,也不要提示更新后需要重启,这样会安全点。

感谢 @大裤子 反馈建议更新补充文档,wgtu更新经验分享:http://ask.dcloud.net.cn/question/6353

继续阅读 »

官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见

5+应用可使用以下方式进行升级

本文重点介绍5+应用资源差量升级,相对App资源独立升级,因为只需要下载更新的资源文件,所以升级包体积更小,升级速度会更快。
注意:需HBuilder5.7.0以上版本才有此功能

生成移动App资源差量升级包

顾名思义,差量升级包是针对某个历史版本到新版本的差量,所以对于升级服务器来讲需要保留所有历史版本,并且分别生成每个历史版本到新版本的差量升级包。
目前HBuilder IDE并未提供版本控制系统,所以需要按照以下规则手动生成差量升级包:

  • 差量升级包文件格式为wgtu
    在HTML5+移动App支持的差量升级包文件后缀名必须是wgtu,它是标准的zip压缩文件,可以使用任何zip工具对wgtu目录进行压缩,并修改文件后缀名称为wgtu。
  • wgtu目录结构
名称 类型 说明
www 目录 保存需要更新的数据文件,其下的目录结构与应用的目录结构一致,其下必须包含manifest.json文件,否则认为更新文件非法,不执行升级操作
update.xml 文件 保存应用升级操作的配置信息,参考update.xml文件格式
  • update.xml文件格式(注意:文件名必须全部小写)
    update.xml为标准的xml文件格式,定义wgtu文件升级配置信息,其文件格式如下:
元素名称 父元素 类型 描述 必须
wgtu 根节点
appid wgtu 属性 升级应用的appid
basis wgtu 节点 基础应用信息
version basis 属性 基础应用的版本信息(旧版本的版本号
remove wgtu 节点 升级需要删除的文件/目录列表
item remove 节点 需要删除的项
path item 属性 需要删除的项的路径,相对于应用根目录,如“image/a.png”表示image目录下的a.png文件。如果是目录则以‘/’结尾,如“res/img/”表示res目录下的img目录,删除时包括下的所有内容。
  • wgtu示例
    应用升级前的版本1.0,其应用目录结构为:

    应用升级后的版本1.1,其应用目录结构为:

    差量升级包wgtu文件的目录结构为(不包括最外层wgtu目录):

    应用升级更新文件manifest.json、index.html,新增detail.html、image/new.png,删除image/icon5.png。
    其中update.xml中的内容为:
    <?xml version="1.0" encoding="utf-8" standalone="no"?>  
    <wgtu appid="TEST" >  
    <basis version="1.0" />  
    <remove>  
    <item path="image/icon5.png" />  
    </remove>  
    </wgtu>

应用中差量更新资源

下载wgtu文件并更新:

    var url='http://demo.dcloud.net.cn/helloh5/update/HelloH5.wgtu';  
    plus.nativeUI.showWaiting("升级中...");  
    var dtask = plus.downloader.createDownload( url, {method:"GET"}, function(d,status){  
        if ( status == 200 ) {   
            console.log( "Download wgtu success: " + d.filename );  
            plus.runtime.install(d.filename,{},function(){  
                plus.nativeUI.closeWaiting();  
                plus.nativeUI.alert("Update wgtu success, restart now!",function(){  
                    plus.runtime.restart();  
                });  
            },function(e){  
                plus.nativeUI.closeWaiting();  
                alert("Update wgtu failed: "+e.message);  
            });  
        } else {  
            plus.nativeUI.closeWaiting();  
             alert( "Download wgtu failed: " + status );   
        }   
    } );  
    dtask.addEventListener('statechanged',function(d,status){  
        console.log("statechanged: "+d.state);  
    });  
    dtask.start();

注意

  1. wgtu包中的update.xml文件名称必须全部小写,并且和www在同一级目录中
  2. wgtu包中www目录下必须包含manifest.json文件,并且里面不能包含注释(HBuilder中默认带注释,需要手动删除所有注释)
  3. update.xml中的appid值是应用的AppID(如“H5F6AE111”),不是程序的包名(如“io.dcloud.H5F6AE1111”)
  4. wgtu包必须是使用zip格式压缩的文件(不能使用如rar等其它压缩格式)

App store应用更新说明
应用资源更新肯定是违反apple政策的,但目前看起来它也不管。你在官网案例那里下载Appstore版本的那些app,大多启动后都会提示更新,反正也都没下架。如果你不是很大的公司,apple不会理睬你。如果你是大公司,建议不要做整体更新,每次更新几个页面,也不要提示更新后需要重启,这样会安全点。

感谢 @大裤子 反馈建议更新补充文档,wgtu更新经验分享:http://ask.dcloud.net.cn/question/6353

收起阅读 »

segment分段选择页面根据传入的querystring 激活相应的分段

具体需求描述是这样的:父页面有两个链接需要导向segment页并根据传入的querystring激活不同的段落。
使用原始的url带参的方式,没有使用自定义扩展参数的方式,不过建议使用自定义扩展参数的方式。

mui.openwindow({  
     extras:{  
      'x_id':'10'//自定义扩展参数,可以用来处理页面间传值  
    }  
})

在B页面这样去获取

var self=plus.webview.currentWebview();  
var id=self.x_id;

具体的实现代码如下:
父页面html,打开链接依然使用mui.openwindow.

<ul>  
                    <li><a class="mui-inline" href="http://ask.dcloud.net.cn/myxx.html?model=item1mobile" style="color:rgb(0, 0, 0) ;">商家XX</a></li>  
                    <li>20</li>  
                </ul>  
                <ul>  
                    <li ><a class="mui-inline" href="http://ask.dcloud.net.cn/myxx.html?model=item2mobile" style="color:rgb(0, 0, 0) ;">个人XX</a></li>  
                    <li>10</li>  
                </ul>

子页面:

<!DOCTYPE html>  
<html>  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title>我的XX</title>  
        <link href="http://ask.dcloud.net.cn/../css/mui.min.css" rel="stylesheet" />  
        <style type="text/css">  
            .mui-segmented-control .mui-control-item {  
                line-height: 28px;  
            }  
            .mui-title {  
                font-family: simhei;  
            }  
            .gray_color {  
                color: #8f8f94;  
                font-size: 0.8em;  
            }  
        </style>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">我的XX</h1>  
        </header>  
        <div class="mui-content">  
            <div class="mui-content-padded tab_title" style="margin: 0.65em 3em;">  
                <div id="segmentedControl" class="mui-segmented-control">  
                    <a class="mui-control-item mui-active" href="#item1mobile">  

                        商家XX  
                    </a>  
                    <a class="mui-control-item" href="#item2mobile">  

                        个人XX  

                    </a>  
                </div>  
            </div>  

            <div id="item1mobile" class="mui-control-content mui-active">  
                <ul class="mui-table-view">  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月19日XXXXX<span class='mui-pull-right gray_color'>3月18号</span>  
                                <p class='mui-ellipsis'>您在日历XXX中设定的XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月15日XXX<span class='mui-pull-right gray_color'>3月14号</span>  
                                <p class='mui-ellipsis'>您在XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月11日XXX<span class='mui-pull-right gray_color'>3月10号</span>  
                                <p class='mui-ellipsis'>您在日历XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                </ul>  
            </div>  
            <div id="item2mobile" class="mui-control-content">  
                <ul class="mui-table-view">  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                订单消息<span class='mui-pull-right gray_color'>4月12号</span>  
                                <p class='mui-ellipsis'>xxxxxxxx</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                订单消息<span class='mui-pull-right gray_color'>4月10号</span>  
                                <p class='mui-ellipsis'>xxxxxxx</p>  
                            </div>  
                        </a>  
                    </li>  
                </ul>  
            </div>  
        </div>  
        <script src="../js/mui.min.js"></script>  
        <script type="text/javascript" charset="utf-8">  
            mui.init();  
            mui.plusReady(function() {  
                               //我使用plus,有时候会报undefined错误  
                console.log(plus.webview.currentWebview().id)  
            })  
             mui.ready(function() {  
                var urlx = this.document.URL.toString();  
                var modelx = "item1mobile";  
                if (urlx.lastIndexOf('?') != -1) {  
                    modelx = urlx.substr(urlx.lastIndexOf('?')   1, urlx.length).split('=')[1];  
                }  
                var ar = document.getElementById("segmentedControl").querySelectorAll(".mui-control-item");  
                var reg = new RegExp('(\\s|^)'   'mui-active'   '(\\s|$)');  
                var item1 = document.getElementById("item1mobile");  
                var item2 = document.getElementById("item2mobile");  
                //重置  
                ar[0].className = ar[0].className.replace(reg, '');  
                ar[1].className = ar[1].className.replace(reg, '');  
                item1.className = item1.className.replace(reg, '');  
                item1.className = item2.className.replace(reg, '');  
                if (modelx == "item1mobile") {  
                    ar[0].className  = " mui-active";  
                    ar[1].className = ar[1].className.replace(reg, '');  
                    item1.className  = " mui-active";  
                    item2.className = item2.className.replace(reg, '');  
                } else {  
                    ar[0].className = ar[0].className.replace(reg, '');  
                    ar[1].className  = " mui-active";  
                    item2.className  = " mui-active";  
                    item1.className = item1.className.replace(reg, '');  
                }  
            });  
        </script>  
    </body>  

</html>
继续阅读 »

具体需求描述是这样的:父页面有两个链接需要导向segment页并根据传入的querystring激活不同的段落。
使用原始的url带参的方式,没有使用自定义扩展参数的方式,不过建议使用自定义扩展参数的方式。

mui.openwindow({  
     extras:{  
      'x_id':'10'//自定义扩展参数,可以用来处理页面间传值  
    }  
})

在B页面这样去获取

var self=plus.webview.currentWebview();  
var id=self.x_id;

具体的实现代码如下:
父页面html,打开链接依然使用mui.openwindow.

<ul>  
                    <li><a class="mui-inline" href="http://ask.dcloud.net.cn/myxx.html?model=item1mobile" style="color:rgb(0, 0, 0) ;">商家XX</a></li>  
                    <li>20</li>  
                </ul>  
                <ul>  
                    <li ><a class="mui-inline" href="http://ask.dcloud.net.cn/myxx.html?model=item2mobile" style="color:rgb(0, 0, 0) ;">个人XX</a></li>  
                    <li>10</li>  
                </ul>

子页面:

<!DOCTYPE html>  
<html>  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title>我的XX</title>  
        <link href="http://ask.dcloud.net.cn/../css/mui.min.css" rel="stylesheet" />  
        <style type="text/css">  
            .mui-segmented-control .mui-control-item {  
                line-height: 28px;  
            }  
            .mui-title {  
                font-family: simhei;  
            }  
            .gray_color {  
                color: #8f8f94;  
                font-size: 0.8em;  
            }  
        </style>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">我的XX</h1>  
        </header>  
        <div class="mui-content">  
            <div class="mui-content-padded tab_title" style="margin: 0.65em 3em;">  
                <div id="segmentedControl" class="mui-segmented-control">  
                    <a class="mui-control-item mui-active" href="#item1mobile">  

                        商家XX  
                    </a>  
                    <a class="mui-control-item" href="#item2mobile">  

                        个人XX  

                    </a>  
                </div>  
            </div>  

            <div id="item1mobile" class="mui-control-content mui-active">  
                <ul class="mui-table-view">  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月19日XXXXX<span class='mui-pull-right gray_color'>3月18号</span>  
                                <p class='mui-ellipsis'>您在日历XXX中设定的XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月15日XXX<span class='mui-pull-right gray_color'>3月14号</span>  
                                <p class='mui-ellipsis'>您在XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月11日XXX<span class='mui-pull-right gray_color'>3月10号</span>  
                                <p class='mui-ellipsis'>您在日历XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                </ul>  
            </div>  
            <div id="item2mobile" class="mui-control-content">  
                <ul class="mui-table-view">  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                订单消息<span class='mui-pull-right gray_color'>4月12号</span>  
                                <p class='mui-ellipsis'>xxxxxxxx</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                订单消息<span class='mui-pull-right gray_color'>4月10号</span>  
                                <p class='mui-ellipsis'>xxxxxxx</p>  
                            </div>  
                        </a>  
                    </li>  
                </ul>  
            </div>  
        </div>  
        <script src="../js/mui.min.js"></script>  
        <script type="text/javascript" charset="utf-8">  
            mui.init();  
            mui.plusReady(function() {  
                               //我使用plus,有时候会报undefined错误  
                console.log(plus.webview.currentWebview().id)  
            })  
             mui.ready(function() {  
                var urlx = this.document.URL.toString();  
                var modelx = "item1mobile";  
                if (urlx.lastIndexOf('?') != -1) {  
                    modelx = urlx.substr(urlx.lastIndexOf('?')   1, urlx.length).split('=')[1];  
                }  
                var ar = document.getElementById("segmentedControl").querySelectorAll(".mui-control-item");  
                var reg = new RegExp('(\\s|^)'   'mui-active'   '(\\s|$)');  
                var item1 = document.getElementById("item1mobile");  
                var item2 = document.getElementById("item2mobile");  
                //重置  
                ar[0].className = ar[0].className.replace(reg, '');  
                ar[1].className = ar[1].className.replace(reg, '');  
                item1.className = item1.className.replace(reg, '');  
                item1.className = item2.className.replace(reg, '');  
                if (modelx == "item1mobile") {  
                    ar[0].className  = " mui-active";  
                    ar[1].className = ar[1].className.replace(reg, '');  
                    item1.className  = " mui-active";  
                    item2.className = item2.className.replace(reg, '');  
                } else {  
                    ar[0].className = ar[0].className.replace(reg, '');  
                    ar[1].className  = " mui-active";  
                    item2.className  = " mui-active";  
                    item1.className = item1.className.replace(reg, '');  
                }  
            });  
        </script>  
    </body>  

</html>
收起阅读 »

C#配置支付宝信息

支付宝 Payment

参考支付宝官方的demo配置的方法,亲测可行.

        public object GetPayInfo(string _amount)//_amount:付款金额  
        {  
            string orderInfo = OrderInfo(_amount);  
            // 对订单做RSA 签名  
            string sign = RSAFromPkcs8.sign(orderInfo, Config.Private_key, Config.Input_charset); //支付宝提供的Config.cs  
            //仅需对sign做URL编码  
            sign = HttpUtility.UrlEncode(sign, Encoding.UTF8);  
            string payInfo = orderInfo + "&sign=\"" + sign + "\"&"  
                + getSignType();  
            return payInfo;  
        }  
        public string OrderInfo(string price)  
        {              
            Dictionary<string, string> payinfo = new Dictionary<string, string>();  
            payinfo.Add("service", "\"mobile.securitypay.pay\"");  
            payinfo.Add("partner", "\"" + Config.Partner + "\"");  
            payinfo.Add("seller_id", "\"" + Config.SELLER + "\"");  
            payinfo.Add("out_trade_no", "\"" + DateTime.Now.ToString("yyyyMMddHHmmssfff")+ "\"");  
            payinfo.Add("subject", "\"****\"");  
            payinfo.Add("body", "\"*****\"");  
            payinfo.Add("total_fee", "\"" + price.ToString() + "\"");  
            payinfo.Add("notify_url", "\"***********"");  
            payinfo.Add("payment_type", "\"1\"");  
            payinfo.Add("_input_charset", "\"UTF-8\"");  
            payinfo.Add("it_b_pay", "\"30m\"");              
            return Core.CreateLinkString(payinfo);  
        }  
         public String getSignType()  
        {  
            return "sign_type=\"RSA\"";  
        }

HB中的方法

                   function topay(rate) {  
                if (w) {  
                    return;  
                }  
                //检查是否请求订单中  
                w = plus.nativeUI.showWaiting();  
                mui.getJSON("http://************/api/Pay", {  
                    _amount: rate  
                }, function(data) {  
                    w.close();  
                    w = null;  
                    if (data) {  
                        plus.payment.request(pays["alipay"], data, function(result) {                             
                                        plus.nativeUI.alert("支付成功:感谢您的支持。", function() {  
                                            mui.back();  
                                        }, "****");                                   
                        }, function(error) {  
                            if (error.code == 62001) {  
                                mui.toast("您放弃支付.");  
                                mui.back();  
                            } else {  
                                plus.nativeUI.alert("支付失败", null, "请前往会员中心再次对该订单尝试支付,支付失败:" + error.code);  
                            }  
                        });  
                    }  
                });  
            }
继续阅读 »

参考支付宝官方的demo配置的方法,亲测可行.

        public object GetPayInfo(string _amount)//_amount:付款金额  
        {  
            string orderInfo = OrderInfo(_amount);  
            // 对订单做RSA 签名  
            string sign = RSAFromPkcs8.sign(orderInfo, Config.Private_key, Config.Input_charset); //支付宝提供的Config.cs  
            //仅需对sign做URL编码  
            sign = HttpUtility.UrlEncode(sign, Encoding.UTF8);  
            string payInfo = orderInfo + "&sign=\"" + sign + "\"&"  
                + getSignType();  
            return payInfo;  
        }  
        public string OrderInfo(string price)  
        {              
            Dictionary<string, string> payinfo = new Dictionary<string, string>();  
            payinfo.Add("service", "\"mobile.securitypay.pay\"");  
            payinfo.Add("partner", "\"" + Config.Partner + "\"");  
            payinfo.Add("seller_id", "\"" + Config.SELLER + "\"");  
            payinfo.Add("out_trade_no", "\"" + DateTime.Now.ToString("yyyyMMddHHmmssfff")+ "\"");  
            payinfo.Add("subject", "\"****\"");  
            payinfo.Add("body", "\"*****\"");  
            payinfo.Add("total_fee", "\"" + price.ToString() + "\"");  
            payinfo.Add("notify_url", "\"***********"");  
            payinfo.Add("payment_type", "\"1\"");  
            payinfo.Add("_input_charset", "\"UTF-8\"");  
            payinfo.Add("it_b_pay", "\"30m\"");              
            return Core.CreateLinkString(payinfo);  
        }  
         public String getSignType()  
        {  
            return "sign_type=\"RSA\"";  
        }

HB中的方法

                   function topay(rate) {  
                if (w) {  
                    return;  
                }  
                //检查是否请求订单中  
                w = plus.nativeUI.showWaiting();  
                mui.getJSON("http://************/api/Pay", {  
                    _amount: rate  
                }, function(data) {  
                    w.close();  
                    w = null;  
                    if (data) {  
                        plus.payment.request(pays["alipay"], data, function(result) {                             
                                        plus.nativeUI.alert("支付成功:感谢您的支持。", function() {  
                                            mui.back();  
                                        }, "****");                                   
                        }, function(error) {  
                            if (error.code == 62001) {  
                                mui.toast("您放弃支付.");  
                                mui.back();  
                            } else {  
                                plus.nativeUI.alert("支付失败", null, "请前往会员中心再次对该订单尝试支付,支付失败:" + error.code);  
                            }  
                        });  
                    }  
                });  
            }
收起阅读 »

这几天对HBuilder使用的感想

mui

HBuilder,刚推出来过后不久,我就开始尝鲜,使用的时候让我感觉很是惊艳,自动提示的让我很满足,什么link 、script、meta标签,刷刷就出来了,这个必须点赞。后来继续尝试WEB 、APP开发,发现能上传自动帮我打包,感觉很轻松,因为我尝试过配置Phongap的环境,一个android,就有些麻烦,更何况还有IOS的。
接着,我用HBuilder来编写PC端的网页,也把这个工具推荐给了我同事们使用,大家都觉得挺好的。
过了一段时间,今年,领导让给写一个APP,我说用原生来开发吧(我这时已经会Android开发了),领导say no,用HTML 5开发,要兼容IOS、Android、WP、微信(微信是一个超级APP,可以算是一个小平台了),另外我给你安排一个专家,帮你做好微信支付、二维码扫描等等功能,我说不用,我知道一个工具,并集成了UI、还有很多开发好的功能,领导很满意,让我先开发一个DEMO出来。
清明节3天假,我第一天就写的差不多了,迅速写好了DEMO大部分工作,也在我的IOS手机上调试完毕,完成度大概80%,遗留了一些优化的工作(不要小瞧这部分),很顺利,很开心。于是在我的Android(小米2s)机器上,我第二天继续调试,我擦!,怎么各种各样的情况出现了,首先领导很重视的二维码扫描功能,我在这部Android机上调了半天,硬是识别不出来,后来是我手不小心抖了下,才发现只有小抖几下才识别的出来。这个我在问答部分也提了,得到官方回复,他们的小米2上是正常的,我不知道会不会与“S”有关系,在二维码扫描方面,扫描速度还是有点不行~~
接着我继续界面的跳转,我比较郁闷,原生Anroid有singleTop、singleTask模式的,我就想让webview重复利用下,例如 首页,我肯定不想让它重复打开吧。查下API,找到了plus.webview.show 方法,不行,IOS的显示不出来,找到了webview.show方法(我想这个两个方法实现逻辑是一样的),结果IOS都无法显示出来,Android到挺正常的,继续查openClosed,IOS的还是不好使,偶那个神哪!在问答区,我发出了提问,官方回复使用zindex,OK,果然能显示出来!但是接着我再首页打开新的页面,mygod,出了个圈圈,就不见踪影,明白了,新打开的界面没有继承打开者zindex属性,得,我只好重新下openWindow方法,将currentWebview,的zindex赋值给要打开的页面,结果还是不行,我想明白了我要取的是topWebview的zindex, 只好查API,没有获取topWebView的方法,得,将show()方法也重写下,并自己记录下topWebview,IOS正常了,达到我要的效果了。
在Android上来试试,Y的,Android倒不干了,使用webview.getStyle.zindex,发现,额,程序刚开始运行的时候得到undifined,而IOS得到的是0。算了算了,给重写的函数加两个判断分支,如果是android,执行原来的方法。呼,脑子终于清静了。
四天的时间,其实还算是挺快的。但是挺有点烦人的。webview的show方法,官方说下一版修复。恩,我也觉得应该将这方面的栈结果顺序维护好。我使用过plus.webview.all()函数,好像如果用show方法,在被show的之后的webview 貌似没被清空。
时间紧迫,我还没对这些缺陷做准确的验证,再提出来。
因此,希望官方提供一个专门的缺陷报告填写页过来,问答的方式 不是感觉很严谨呵。另外,mui的js源码也阅读了下,额,API不够用,当然可以自己写,但是官方的理解肯定深刻一些。5+部分的可否给一些原理图神马的。HB整体的发展路线图、蓝图,也能给一个,让大家也讨论下,让小伙伴们都参加进来,给予意见吧。
我还是挺喜欢HB的,但是这些或多或少的问题能得到解决。

继续阅读 »

HBuilder,刚推出来过后不久,我就开始尝鲜,使用的时候让我感觉很是惊艳,自动提示的让我很满足,什么link 、script、meta标签,刷刷就出来了,这个必须点赞。后来继续尝试WEB 、APP开发,发现能上传自动帮我打包,感觉很轻松,因为我尝试过配置Phongap的环境,一个android,就有些麻烦,更何况还有IOS的。
接着,我用HBuilder来编写PC端的网页,也把这个工具推荐给了我同事们使用,大家都觉得挺好的。
过了一段时间,今年,领导让给写一个APP,我说用原生来开发吧(我这时已经会Android开发了),领导say no,用HTML 5开发,要兼容IOS、Android、WP、微信(微信是一个超级APP,可以算是一个小平台了),另外我给你安排一个专家,帮你做好微信支付、二维码扫描等等功能,我说不用,我知道一个工具,并集成了UI、还有很多开发好的功能,领导很满意,让我先开发一个DEMO出来。
清明节3天假,我第一天就写的差不多了,迅速写好了DEMO大部分工作,也在我的IOS手机上调试完毕,完成度大概80%,遗留了一些优化的工作(不要小瞧这部分),很顺利,很开心。于是在我的Android(小米2s)机器上,我第二天继续调试,我擦!,怎么各种各样的情况出现了,首先领导很重视的二维码扫描功能,我在这部Android机上调了半天,硬是识别不出来,后来是我手不小心抖了下,才发现只有小抖几下才识别的出来。这个我在问答部分也提了,得到官方回复,他们的小米2上是正常的,我不知道会不会与“S”有关系,在二维码扫描方面,扫描速度还是有点不行~~
接着我继续界面的跳转,我比较郁闷,原生Anroid有singleTop、singleTask模式的,我就想让webview重复利用下,例如 首页,我肯定不想让它重复打开吧。查下API,找到了plus.webview.show 方法,不行,IOS的显示不出来,找到了webview.show方法(我想这个两个方法实现逻辑是一样的),结果IOS都无法显示出来,Android到挺正常的,继续查openClosed,IOS的还是不好使,偶那个神哪!在问答区,我发出了提问,官方回复使用zindex,OK,果然能显示出来!但是接着我再首页打开新的页面,mygod,出了个圈圈,就不见踪影,明白了,新打开的界面没有继承打开者zindex属性,得,我只好重新下openWindow方法,将currentWebview,的zindex赋值给要打开的页面,结果还是不行,我想明白了我要取的是topWebview的zindex, 只好查API,没有获取topWebView的方法,得,将show()方法也重写下,并自己记录下topWebview,IOS正常了,达到我要的效果了。
在Android上来试试,Y的,Android倒不干了,使用webview.getStyle.zindex,发现,额,程序刚开始运行的时候得到undifined,而IOS得到的是0。算了算了,给重写的函数加两个判断分支,如果是android,执行原来的方法。呼,脑子终于清静了。
四天的时间,其实还算是挺快的。但是挺有点烦人的。webview的show方法,官方说下一版修复。恩,我也觉得应该将这方面的栈结果顺序维护好。我使用过plus.webview.all()函数,好像如果用show方法,在被show的之后的webview 貌似没被清空。
时间紧迫,我还没对这些缺陷做准确的验证,再提出来。
因此,希望官方提供一个专门的缺陷报告填写页过来,问答的方式 不是感觉很严谨呵。另外,mui的js源码也阅读了下,额,API不够用,当然可以自己写,但是官方的理解肯定深刻一些。5+部分的可否给一些原理图神马的。HB整体的发展路线图、蓝图,也能给一个,让大家也讨论下,让小伙伴们都参加进来,给予意见吧。
我还是挺喜欢HB的,但是这些或多或少的问题能得到解决。

收起阅读 »

分享一个利用融云开发伪IM的思路

IM 融云

在移动互联时代,IM基本都是必备品了,可惜的是DCloud在短期内貌似还没有加入IM系统的可能,最近研究了一下三方的IM模块,在这里和大家分享一个利用融云Web达成伪IM的思路。

1、为什么选择Web版本?
融云在Web版本外还提供iOS和Android版本的SDK,但是需要一定的原生开发能力来进行支持,虽然都是封装好的API接口,开发起来不是很难,但是在我的系统里,暂时是不需要除了文字外的其余多余的功能,为了一些莫名的功能,耗费时间和精力去研究SDK,实在是有些得不偿失,等以后有时间或者需求的时候再去研究,所以需要添加很多IM组件的小伙伴可以绕道了。

2、Web版本的优缺点?
优点:集成简单,只需要简单的几步,就可以集成一套IM系统在APP中。
(1)引入Web版本SDK

http://res.websdk.rong.io/RongIMClient{-版本号}-min.js

(2)初始化web sdk

RongIMClient.init("appkey");

(3)设置链接状态监听器

RongIMClient.setConnectionStatusListener({    
     onChanged: function (status) {}    
}); 

(4)链接融云服务器

RongIMClient.connect("token", {  
     onSuccess: function (userid) { },  
     onError: function (x) { }  
});

(5)设置消息监听器

RongIMClient.getInstance().setOnReceiveMessageListener({  
     onReceived: function (message) { }  
});

(6)得到RongIMClient实例对象,设置私人会话类型

var ins = RongIMClient.getInstance();  
var contype = RongIMClient.ConversationType.PRIVATE;

(7)发送

ins.sendMessage(contype, "targetId", RongIMClient.TextMessage.obtain("发送消息内容"), null, {  
       onSuccess: function () { },  
       onError: function (data) { }  
 });

缺点:无法PUSH,这个实在要吐槽一下,其实融云支持PUSH,但是我和他们交涉良久,他们总是认为Web版本不需要PUSH这样的东西,其实就是简单的在他们服务器上注册用户的deviceToken,就为了注册个deviceToken,就要分别集成iOS和Android两套SDK,实在是麻烦。我让他们加,未果;让他们把SDK再底层的API开放给我,我自己增加注册,也是未果。

3、为什么是伪IM?
这个和无法PUSH是相关的,因为无法PUSH,所以只有用户在打开APP的情况下才能收到对方发送的消息,无法忍受,那么如何解决呢?
方案如下:
(1)在用户退出APP,或者pause的时候,主动断开和融云服务器的连接。
(2)每次发出IM消息的时候,消息要通知到自己的服务器,自己的服务器去融云的服务器获取用户在线状态
(a)如果用户在线,不处理
(b)如果用户不在线,利用个推的PUSH提醒用户打开APP
(3)用户进入APP的时候,重连融云服务器,接受消息。

这样,就可以做到一个简单的IM系统了。服务器端就不在这里详细描述啦^_^

PS1:融云的Web版本其实不止是文字,还有其余的一些,譬如客服,图片等等,集成起来还是比较简单的,当然,如果你的需求比较高,那么还是集成iOS或者Android版本的SDK要更好些。
PS2:还有一些三方IM,我没有仔细的研究过,但是应该大同小异。

继续阅读 »

在移动互联时代,IM基本都是必备品了,可惜的是DCloud在短期内貌似还没有加入IM系统的可能,最近研究了一下三方的IM模块,在这里和大家分享一个利用融云Web达成伪IM的思路。

1、为什么选择Web版本?
融云在Web版本外还提供iOS和Android版本的SDK,但是需要一定的原生开发能力来进行支持,虽然都是封装好的API接口,开发起来不是很难,但是在我的系统里,暂时是不需要除了文字外的其余多余的功能,为了一些莫名的功能,耗费时间和精力去研究SDK,实在是有些得不偿失,等以后有时间或者需求的时候再去研究,所以需要添加很多IM组件的小伙伴可以绕道了。

2、Web版本的优缺点?
优点:集成简单,只需要简单的几步,就可以集成一套IM系统在APP中。
(1)引入Web版本SDK

http://res.websdk.rong.io/RongIMClient{-版本号}-min.js

(2)初始化web sdk

RongIMClient.init("appkey");

(3)设置链接状态监听器

RongIMClient.setConnectionStatusListener({    
     onChanged: function (status) {}    
}); 

(4)链接融云服务器

RongIMClient.connect("token", {  
     onSuccess: function (userid) { },  
     onError: function (x) { }  
});

(5)设置消息监听器

RongIMClient.getInstance().setOnReceiveMessageListener({  
     onReceived: function (message) { }  
});

(6)得到RongIMClient实例对象,设置私人会话类型

var ins = RongIMClient.getInstance();  
var contype = RongIMClient.ConversationType.PRIVATE;

(7)发送

ins.sendMessage(contype, "targetId", RongIMClient.TextMessage.obtain("发送消息内容"), null, {  
       onSuccess: function () { },  
       onError: function (data) { }  
 });

缺点:无法PUSH,这个实在要吐槽一下,其实融云支持PUSH,但是我和他们交涉良久,他们总是认为Web版本不需要PUSH这样的东西,其实就是简单的在他们服务器上注册用户的deviceToken,就为了注册个deviceToken,就要分别集成iOS和Android两套SDK,实在是麻烦。我让他们加,未果;让他们把SDK再底层的API开放给我,我自己增加注册,也是未果。

3、为什么是伪IM?
这个和无法PUSH是相关的,因为无法PUSH,所以只有用户在打开APP的情况下才能收到对方发送的消息,无法忍受,那么如何解决呢?
方案如下:
(1)在用户退出APP,或者pause的时候,主动断开和融云服务器的连接。
(2)每次发出IM消息的时候,消息要通知到自己的服务器,自己的服务器去融云的服务器获取用户在线状态
(a)如果用户在线,不处理
(b)如果用户不在线,利用个推的PUSH提醒用户打开APP
(3)用户进入APP的时候,重连融云服务器,接受消息。

这样,就可以做到一个简单的IM系统了。服务器端就不在这里详细描述啦^_^

PS1:融云的Web版本其实不止是文字,还有其余的一些,譬如客服,图片等等,集成起来还是比较简单的,当然,如果你的需求比较高,那么还是集成iOS或者Android版本的SDK要更好些。
PS2:还有一些三方IM,我没有仔细的研究过,但是应该大同小异。

收起阅读 »

点击小图浏览大图,双击缩放,赶快拿走

功能实现:
点击小图浏览大图,双击放大,滑动切换,上滑或下滑关闭,双指放大或缩小(到了最小是自动关闭),预加载缓冲。
调用的第三方插件,好处就是没有借用jquery,而且滑动,缩放效果超流畅
最底下有效果图和所需文件下载
1,引入CSS及JS

<link rel="stylesheet prefetch" href="../plugin/touchPhotos/photoswipe.css">  
<link rel="stylesheet prefetch" href="../plugin/touchPhotos/default-skin.css">  
<script src="../plugin/touchPhotos/stopExecutionOnTimeout.js"></script>  
<script src="../plugin/touchPhotos/photoswipe.min.js"></script>  
<script src="../plugin/touchPhotos/photoswipe-ui-default.min.js"></script>

2,小图片容器(这里要获取原图地址及图片尺寸),我是通过JSON来获取的,就写个JSON的写法了
//容器 my-simple-gallery,获取图片的宽度和高度

data += '<div class="my-simple-gallery">';  
for(var k=0; k<data.photos.length; k++){  
    data += '<figure itemprop="associatedMedia" itemscope=""><a href="'+data.photos[k].big+'" data-size="'+data.photos[k].size+'"><img src="'+data.photos[k].small+'" ></a></figure>';  
}  
data += '</div>';

自己讲data插入到ID里面了哈
然后就指定浏览图片的那个容器了
initPhotoSwipeFromDOM('.my-simple-gallery');

3.下面的代码写的有点恶心,直接拿去放到模板里面

<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true" style="">
<div class="pswpbg"></div>
<div class="pswp
scroll-wrap">
<div class="pswpcontainer" style="transform: translate3d(0px, 0px, 0px);">
<div class="pswp
item" style="display: block; transform: translate3d(-1792px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(288px, 56px, 0px) scale(1);"></div></div>
<div class="pswp
item" style="transform: translate3d(0px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(473px, 66.8125px, 0px) scale(0.146484375);"></div></div>
<div class="pswp
item" style="display: block; transform: translate3d(1792px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(447px, 44px, 0px) scale(0.6904296875);"></div></div>
</div>
<div class="pswp
ui pswpui--fit pswpui--hidden">
<div class="pswptop-bar">
<div class="pswp
counter">4 / 4</div>
<button class="pswpbutton pswpbutton--close" title="Close (Esc)"></button>
<div class="pswppreloader">
<div class="pswp
preloadericn">
<div class="pswp
preloadercut">
<div class="pswp
preloaderdonut"></div>
</div>
</div>
</div>
</div>
<div class="pswp
caption">
<div class="pswpcaptioncenter"></div>
</div>
</div>
</div>
</div>

4.处理大图的,直接拿去
var initPhotoSwipeFromDOM = function (gallerySelector) {
var parseThumbnailElements = function (el) {
var thumbElements = el.childNodes, numNodes = thumbElements.length, items = [], figureEl, childElements, linkEl, size, item;
for (var i = 0; i < numNodes; i++) {
if (window.CP.shouldStopExecution(1)) {
break;
}
figureEl = thumbElements[i];
if (figureEl.nodeType !== 1) {
continue;
}
linkEl = figureEl.children[0];
size = linkEl.getAttribute('data-size').split('x');
item = {
src: linkEl.getAttribute('href'),
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
};
if (figureEl.children.length > 1) {
item.title = figureEl.children[1].innerHTML;
}
if (linkEl.children.length > 0) {
item.msrc = linkEl.children[0].getAttribute('src');
}
item.el = figureEl;
items.push(item);
}
window.CP.exitedLoop(1);
return items;
};
var closest = function closest(el, fn) {
return el && (fn(el) ? el : closest(el.parentNode, fn));
};
var onThumbnailsClick = function (e) {
e = e || window.event;
e.preventDefault ? e.preventDefault() : e.returnValue = false;
var eTarget = e.target || e.srcElement;
var clickedListItem = closest(eTarget, function (el) {
return el.tagName && el.tagName.toUpperCase() === 'FIGURE';
});
if (!clickedListItem) {
return;
}
var clickedGallery = clickedListItem.parentNode, childNodes = clickedListItem.parentNode.childNodes, numChildNodes = childNodes.length, nodeIndex = 0, index;
for (var i = 0; i < numChildNodes; i++) {
if (window.CP.shouldStopExecution(2)) {
break;
}
if (childNodes[i].nodeType !== 1) {
continue;
}
if (childNodes[i] === clickedListItem) {
index = nodeIndex;
break;
}
nodeIndex++;
}
window.CP.exitedLoop(2);
if (index >= 0) {
openPhotoSwipe(index, clickedGallery);
}
return false;
};
var photoswipeParseHash = function () {
var hash = window.location.hash.substring(1), params = {};
if (hash.length < 5) {
return params;
}
var vars = hash.split('&');
for (var i = 0; i < vars.length; i++) {
if (window.CP.shouldStopExecution(3)) {
break;
}
if (!vars[i]) {
continue;
}
var pair = vars[i].split('=');
if (pair.length < 2) {
continue;
}
params[pair[0]] = pair[1];
}
window.CP.exitedLoop(3);
if (params.gid) {
params.gid = parseInt(params.gid, 10);
}
if (!params.hasOwnProperty('pid')) {
return params;
}
params.pid = parseInt(params.pid, 10);
return params;
};
var openPhotoSwipe = function (index, galleryElement, disableAnimation) {
var pswpElement = document.querySelectorAll('.pswp')[0], gallery, options, items;
items = parseThumbnailElements(galleryElement);
options = {
index: index,
galleryUID: galleryElement.getAttribute('data-pswp-uid'),
getThumbBoundsFn: function (index) {
var thumbnail = items[index].el.getElementsByTagName('img')[0], pageYScroll = window.pageYOffset || document.documentElement.scrollTop, rect = thumbnail.getBoundingClientRect();
return {
x: rect.left,
y: rect.top + pageYScroll,
w: rect.width
};
},
history: false,
focus: false
};
if (disableAnimation) {
options.showAnimationDuration = 0;
}
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
};
var galleryElements = document.querySelectorAll(gallerySelector);
for (var i = 0, l = galleryElements.length; i < l; i++) {
if (window.CP.shouldStopExecution(4)) {
break;
}
galleryElements[i].setAttribute('data-pswp-uid', i + 1);
galleryElements[i].onclick = onThumbnailsClick;
}
window.CP.exitedLoop(4);
var hashData = photoswipeParseHash();
if (hashData.pid > 0 && hashData.gid > 0) {
openPhotoSwipe(hashData.pid - 1, galleryElements[hashData.gid - 1], true);
}
};

继续阅读 »

功能实现:
点击小图浏览大图,双击放大,滑动切换,上滑或下滑关闭,双指放大或缩小(到了最小是自动关闭),预加载缓冲。
调用的第三方插件,好处就是没有借用jquery,而且滑动,缩放效果超流畅
最底下有效果图和所需文件下载
1,引入CSS及JS

<link rel="stylesheet prefetch" href="../plugin/touchPhotos/photoswipe.css">  
<link rel="stylesheet prefetch" href="../plugin/touchPhotos/default-skin.css">  
<script src="../plugin/touchPhotos/stopExecutionOnTimeout.js"></script>  
<script src="../plugin/touchPhotos/photoswipe.min.js"></script>  
<script src="../plugin/touchPhotos/photoswipe-ui-default.min.js"></script>

2,小图片容器(这里要获取原图地址及图片尺寸),我是通过JSON来获取的,就写个JSON的写法了
//容器 my-simple-gallery,获取图片的宽度和高度

data += '<div class="my-simple-gallery">';  
for(var k=0; k<data.photos.length; k++){  
    data += '<figure itemprop="associatedMedia" itemscope=""><a href="'+data.photos[k].big+'" data-size="'+data.photos[k].size+'"><img src="'+data.photos[k].small+'" ></a></figure>';  
}  
data += '</div>';

自己讲data插入到ID里面了哈
然后就指定浏览图片的那个容器了
initPhotoSwipeFromDOM('.my-simple-gallery');

3.下面的代码写的有点恶心,直接拿去放到模板里面

<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true" style="">
<div class="pswpbg"></div>
<div class="pswp
scroll-wrap">
<div class="pswpcontainer" style="transform: translate3d(0px, 0px, 0px);">
<div class="pswp
item" style="display: block; transform: translate3d(-1792px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(288px, 56px, 0px) scale(1);"></div></div>
<div class="pswp
item" style="transform: translate3d(0px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(473px, 66.8125px, 0px) scale(0.146484375);"></div></div>
<div class="pswp
item" style="display: block; transform: translate3d(1792px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(447px, 44px, 0px) scale(0.6904296875);"></div></div>
</div>
<div class="pswp
ui pswpui--fit pswpui--hidden">
<div class="pswptop-bar">
<div class="pswp
counter">4 / 4</div>
<button class="pswpbutton pswpbutton--close" title="Close (Esc)"></button>
<div class="pswppreloader">
<div class="pswp
preloadericn">
<div class="pswp
preloadercut">
<div class="pswp
preloaderdonut"></div>
</div>
</div>
</div>
</div>
<div class="pswp
caption">
<div class="pswpcaptioncenter"></div>
</div>
</div>
</div>
</div>

4.处理大图的,直接拿去
var initPhotoSwipeFromDOM = function (gallerySelector) {
var parseThumbnailElements = function (el) {
var thumbElements = el.childNodes, numNodes = thumbElements.length, items = [], figureEl, childElements, linkEl, size, item;
for (var i = 0; i < numNodes; i++) {
if (window.CP.shouldStopExecution(1)) {
break;
}
figureEl = thumbElements[i];
if (figureEl.nodeType !== 1) {
continue;
}
linkEl = figureEl.children[0];
size = linkEl.getAttribute('data-size').split('x');
item = {
src: linkEl.getAttribute('href'),
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
};
if (figureEl.children.length > 1) {
item.title = figureEl.children[1].innerHTML;
}
if (linkEl.children.length > 0) {
item.msrc = linkEl.children[0].getAttribute('src');
}
item.el = figureEl;
items.push(item);
}
window.CP.exitedLoop(1);
return items;
};
var closest = function closest(el, fn) {
return el && (fn(el) ? el : closest(el.parentNode, fn));
};
var onThumbnailsClick = function (e) {
e = e || window.event;
e.preventDefault ? e.preventDefault() : e.returnValue = false;
var eTarget = e.target || e.srcElement;
var clickedListItem = closest(eTarget, function (el) {
return el.tagName && el.tagName.toUpperCase() === 'FIGURE';
});
if (!clickedListItem) {
return;
}
var clickedGallery = clickedListItem.parentNode, childNodes = clickedListItem.parentNode.childNodes, numChildNodes = childNodes.length, nodeIndex = 0, index;
for (var i = 0; i < numChildNodes; i++) {
if (window.CP.shouldStopExecution(2)) {
break;
}
if (childNodes[i].nodeType !== 1) {
continue;
}
if (childNodes[i] === clickedListItem) {
index = nodeIndex;
break;
}
nodeIndex++;
}
window.CP.exitedLoop(2);
if (index >= 0) {
openPhotoSwipe(index, clickedGallery);
}
return false;
};
var photoswipeParseHash = function () {
var hash = window.location.hash.substring(1), params = {};
if (hash.length < 5) {
return params;
}
var vars = hash.split('&');
for (var i = 0; i < vars.length; i++) {
if (window.CP.shouldStopExecution(3)) {
break;
}
if (!vars[i]) {
continue;
}
var pair = vars[i].split('=');
if (pair.length < 2) {
continue;
}
params[pair[0]] = pair[1];
}
window.CP.exitedLoop(3);
if (params.gid) {
params.gid = parseInt(params.gid, 10);
}
if (!params.hasOwnProperty('pid')) {
return params;
}
params.pid = parseInt(params.pid, 10);
return params;
};
var openPhotoSwipe = function (index, galleryElement, disableAnimation) {
var pswpElement = document.querySelectorAll('.pswp')[0], gallery, options, items;
items = parseThumbnailElements(galleryElement);
options = {
index: index,
galleryUID: galleryElement.getAttribute('data-pswp-uid'),
getThumbBoundsFn: function (index) {
var thumbnail = items[index].el.getElementsByTagName('img')[0], pageYScroll = window.pageYOffset || document.documentElement.scrollTop, rect = thumbnail.getBoundingClientRect();
return {
x: rect.left,
y: rect.top + pageYScroll,
w: rect.width
};
},
history: false,
focus: false
};
if (disableAnimation) {
options.showAnimationDuration = 0;
}
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
};
var galleryElements = document.querySelectorAll(gallerySelector);
for (var i = 0, l = galleryElements.length; i < l; i++) {
if (window.CP.shouldStopExecution(4)) {
break;
}
galleryElements[i].setAttribute('data-pswp-uid', i + 1);
galleryElements[i].onclick = onThumbnailsClick;
}
window.CP.exitedLoop(4);
var hashData = photoswipeParseHash();
if (hashData.pid > 0 && hashData.gid > 0) {
openPhotoSwipe(hashData.pid - 1, galleryElements[hashData.gid - 1], true);
}
};

收起阅读 »

Android平台API等级配置 - minSdkVersion&targetSdkVersion

Android

此文档将不再维护,请参考新文档:https://uniapp.dcloud.io/tutorial/app-android-minsdkversion

<a id="minsdkversion"></a>

minSdkVersion

minSdkVersion用于指定应用兼容的最低Android版本(API等级)。
如果APP某些功能无法支持低版本Android系统的设备,可以配置minSdkVersion确保APP只能安装到指定Android版本以上的设备。HBuilder|HBuilderX中可在manifest.json中进行配置。

⚠️注意: minSdkVersion升级时只能增加不能降低。minSdkVersion高的apk无法被minSdkVersion低的apk覆盖安装需要注意!!

可视化界面配置

打开项目的manifest.json文件,在 "App常用其它设置" 项中 "Android设置" 下的 minSdkVersion编辑框中输入要支持的最低Android版本号:

源码视图配置

打开项目的manifest.json文件,切换到 "源码视图"

  • 5+APP项目
    在plus->distribute->google节点下添加“minSdkVersion”字段,并配置要支持的最低Android版本号:
    "plus": {  
        "distribute": {  
            "google":{  
                "minSdkVersion": 22  
            }  
        }  
    }
  • uni-app项目
    在"app-plus"->distribute->android节点下添加“minSdkVersion”字段,并配置要支持的最低Android版本号:
    "app-plus": {  
        "distribute": {  
            "android":{  
                "minSdkVersion": 22  
            }  
        }  
    }

Number类型,整数值,应用要求的最低系统版本,必须大于等于19(Android4.4)小于等于23(android 6.0),默认值为19
示例中设置值为22表示应用只能安装在Android5.1及以上设备。

<a id="targetsdkversion"></a>

targetSdkVersion

HBuilder3.2.13版本开始targetSdkVersion默认值由26调整为28
注意:某些uni原生插件可能没有适配好targetSdkVersion为28会引起部分功能异常,碰到这类情况请联系插件开发者进行适配

⚠️注意: targetSdkVersion升级时只能增加不能降低。targetSdkVersion高的apk无法被targetSdkVersion低的apk覆盖安装需要注意!!

targetSdkVersion用于指定应用适配的Android版本(API等级)。
在Android系统中设置低版本的targetSdkVersion会使APP兼容模式运行,也就可能无法用到新系统的特性,甚至在兼容模式下运行可能存在安全漏洞等问题。
随着Android系统的升级,一些应用市场会要求设置较高的targetSdkVersion才可以提交。HBuilder|HBuilderX中可在manifest.json中进行配置。

可视化界面配置

打开项目的manifest.json文件,在 "App常用其它设置" 项中 "Android设置" 下的 targetSdkVersion编辑框中输入要支持的最低Android版本号:

源码视图配置

打开项目的manifest.json文件,切换到 "源码视图"

  • 5+APP项目
    在plus->distribute->google节点下添加“targetSdkVersion”字段:
    "plus": {  
        "distribute": {  
            "google":{  
                "targetSdkVersion": 26  
            }  
        }  
    }
  • uni-app项目
    在"app-plus"->distribute->android节点下添加“targetSdkVersion”字段:
    "app-plus": {  
        "distribute": {  
            "android":{  
                "targetSdkVersion": 26  
            }  
        }  
    }

Number类型,整数值,云端打包默认的targetSdkVersion值为26

  • 5+App项目:最小值为19,最大值29
  • uni-app项目:最小值为26,最大值29

    HBuilderX2.8.3及以下版本targetSdkVersion最大值支持28
    HBuilderX2.8.4+版本targetSdkVersion最大值支持29

Android版本列表

API等级与Android版本对应列表如下:

API等级 Android版本号
14 Android4.0
15 Android4.0.3
16 Android4.1.2
17 Android4.2.2
18 Android4.3.1
19 Android4.4.2
20 Android4.4W.2
21 Android5.0.1
22 Android5.1
24 Android7.0
25 Android7.1.1
26 Android8.0
27 Android8.1
28 Android9.0
29 Android10.0(Android Q)
30 Android11.0

配置完成保存提交App云端打包后才能生效

继续阅读 »

此文档将不再维护,请参考新文档:https://uniapp.dcloud.io/tutorial/app-android-minsdkversion

<a id="minsdkversion"></a>

minSdkVersion

minSdkVersion用于指定应用兼容的最低Android版本(API等级)。
如果APP某些功能无法支持低版本Android系统的设备,可以配置minSdkVersion确保APP只能安装到指定Android版本以上的设备。HBuilder|HBuilderX中可在manifest.json中进行配置。

⚠️注意: minSdkVersion升级时只能增加不能降低。minSdkVersion高的apk无法被minSdkVersion低的apk覆盖安装需要注意!!

可视化界面配置

打开项目的manifest.json文件,在 "App常用其它设置" 项中 "Android设置" 下的 minSdkVersion编辑框中输入要支持的最低Android版本号:

源码视图配置

打开项目的manifest.json文件,切换到 "源码视图"

  • 5+APP项目
    在plus->distribute->google节点下添加“minSdkVersion”字段,并配置要支持的最低Android版本号:
    "plus": {  
        "distribute": {  
            "google":{  
                "minSdkVersion": 22  
            }  
        }  
    }
  • uni-app项目
    在"app-plus"->distribute->android节点下添加“minSdkVersion”字段,并配置要支持的最低Android版本号:
    "app-plus": {  
        "distribute": {  
            "android":{  
                "minSdkVersion": 22  
            }  
        }  
    }

Number类型,整数值,应用要求的最低系统版本,必须大于等于19(Android4.4)小于等于23(android 6.0),默认值为19
示例中设置值为22表示应用只能安装在Android5.1及以上设备。

<a id="targetsdkversion"></a>

targetSdkVersion

HBuilder3.2.13版本开始targetSdkVersion默认值由26调整为28
注意:某些uni原生插件可能没有适配好targetSdkVersion为28会引起部分功能异常,碰到这类情况请联系插件开发者进行适配

⚠️注意: targetSdkVersion升级时只能增加不能降低。targetSdkVersion高的apk无法被targetSdkVersion低的apk覆盖安装需要注意!!

targetSdkVersion用于指定应用适配的Android版本(API等级)。
在Android系统中设置低版本的targetSdkVersion会使APP兼容模式运行,也就可能无法用到新系统的特性,甚至在兼容模式下运行可能存在安全漏洞等问题。
随着Android系统的升级,一些应用市场会要求设置较高的targetSdkVersion才可以提交。HBuilder|HBuilderX中可在manifest.json中进行配置。

可视化界面配置

打开项目的manifest.json文件,在 "App常用其它设置" 项中 "Android设置" 下的 targetSdkVersion编辑框中输入要支持的最低Android版本号:

源码视图配置

打开项目的manifest.json文件,切换到 "源码视图"

  • 5+APP项目
    在plus->distribute->google节点下添加“targetSdkVersion”字段:
    "plus": {  
        "distribute": {  
            "google":{  
                "targetSdkVersion": 26  
            }  
        }  
    }
  • uni-app项目
    在"app-plus"->distribute->android节点下添加“targetSdkVersion”字段:
    "app-plus": {  
        "distribute": {  
            "android":{  
                "targetSdkVersion": 26  
            }  
        }  
    }

Number类型,整数值,云端打包默认的targetSdkVersion值为26

  • 5+App项目:最小值为19,最大值29
  • uni-app项目:最小值为26,最大值29

    HBuilderX2.8.3及以下版本targetSdkVersion最大值支持28
    HBuilderX2.8.4+版本targetSdkVersion最大值支持29

Android版本列表

API等级与Android版本对应列表如下:

API等级 Android版本号
14 Android4.0
15 Android4.0.3
16 Android4.1.2
17 Android4.2.2
18 Android4.3.1
19 Android4.4.2
20 Android4.4W.2
21 Android5.0.1
22 Android5.1
24 Android7.0
25 Android7.1.1
26 Android8.0
27 Android8.1
28 Android9.0
29 Android10.0(Android Q)
30 Android11.0

配置完成保存提交App云端打包后才能生效

收起阅读 »