n***@gmail.com
n***@gmail.com
  • 发布:2017-06-23 17:03
  • 更新:2022-11-20 22:11
  • 阅读:127483

tab选项卡示例教程-基于subnview模式的原生tab(含底部凸起大图标)

分类:HTML5+

方案比较说明

关于tab bar选项卡,目前已经有多种实现方案,最常见的有div方案的和双webview方案的。

div方案
div方案的选项卡是普通的实现方案,所有都放在一个页面中执行,页面压力可想而知,在手机上会出现卡顿现象。SPA仅适用于非常简单的dom和非常少的页面;

双webview方案
双webview方案的实现思路就是一个包含tab bar 的父webview + n个对应tab的子webview。在5+App的manifest可以配置双Webview首页,可以让tab父页和第一个子tab的子页同时启动。
但Webview的渲染速度不如nview快,Webview太多对内存吃的太多,崩溃和白屏的概率就会增加。此模式也不是最优。
而下面我们介绍的这种方案,让显示速度,占用内存,性能消耗都达到了一种更优的状态!

基于subnview的原生tab选项卡方案
HBuilder8.8起提供了subnview,subnview顾名思义就是子原生view,也就是把一个全屏Webview的一部分区域切出来,渲染工作交给原生引擎渲染,不走Webview渲染。
此方案设计思路简单来说就是把首页的底部区域变成subnview,交给5+的原生引擎渲染,也就是说是要nview来绘制底部tab。
manifest里配置的首页作为第一个tab选项卡窗口,同时为这个Webview配置subnview,在这个subnview里编写nview的描述,就可以创建一个原生的选项卡,这个选项卡的绘制由5+引擎原生完成,在首页Webview渲染之前就由原生层渲染好了。
其余tab项子窗口在首页plus ready后创建,并同时监听tab点击。
原生view控件制作的tab,还可以轻松实现tab中部凸起大图标的效果,这种效果在双Webview模式下不好实现。

可见基于subnview的原生tab选项卡方案渲染更快、内存更少,并且自定义效果也更丰富。
当然也有一些缺点就是有点复杂,对于学习纯HTML5的程序员而言,首先需要学习subnview这些HTML5+独有的技术。
但nview真的是非常值得学习的技术,掌握nview后,你做出来的App体验绝对和原生一样好。

最新方案优化说明

经过一段时间的实践,发现原生tab选项卡方案存在部分问题,已修复如下问题:

  1. 添加输入框并点击之后webview窗口发生变化,出现白条
  2. 添加titleNView会造成某些tab点击失效
  3. 添加titleNView安卓上会出现遮盖子webview的现象
  4. 设置应用为沉浸式,安卓上中间图标会有下移现象

另外上文提到过webview太多对内存会吃太多,nview创建太多同样存在内存吃紧的问题,原来的方案中为了层次分明和便于监听点击事件总共创建了有6个nview,这与本示例的初衷相悖,所以现在缩减为两个nview。分别是:1.中间凸起图标nview 2.底部选项卡nview。

下面来详细说明此方案(最新方案)的实现方法。

示例效果说明

本示例实现的效果是 使用nativeObj绘制原生view控件类型的底部选项卡 + 底部中央悬浮球图标 + webview模式的选项卡窗口,适用于5+app 和流应用环境,通常应用于app 首页;

效果图展示

实现步骤

1.配置底部选项卡

应用首页配置底部选项卡可以直接在manifest.json 中找到 plus 节点,在里面创建如下示例:
以下给出一个tab的示例,完整示例请下载附件中demo查看。

"launchwebview": {   //首页webview窗口  
    "bottom": "0px",  
    "background": "#fff",  
    "subNViews": [  //subNViews节点下配置你的tab (此处只给出一个tab配置示例)  
            {  
                "id": "tabBar",  
                "styles": {  
                    "bottom": "0px",  
                    "left": "0",  
                    "height": "50px",  
                    "width": "100%",  
                    "backgroundColor": "#fff"  
                },  
                "tags": [  
                    {  
                        "tag": "font",  
                        "id": "indexIcon",  
                        "text": "\ue500",  
                        "position": {  
                            "top": "4px",  
                            "left": "0",  
                            "width": "25%",  
                            "height": "24px"  
                        },  
                        "textStyles": {  
                            "fontSrc": "_www/fonts/mui.ttf",  
                            "align": "center",  
                            "size": "24px"  
                        }  
                    }, {  
                        "tag": "font",  
                        "id": "indexText",  
                        "text": "首页",  
                        "position": {  
                            "top": "23px",  
                            "left": "0",  
                            "width": "25%",  
                            "height": "24px"  
                        },  
                        "textStyles": {  
                            "align": "center",  
                            "size": "10px"  
                        }  
                    }  
            ]  
        }  
    ]  
}

注意:
1.此处配置tab建议直接配置n个tab 对应n个tag而不是nview控件,尽量少的为webview添加nview控件,nview控件在提供原生渲染能力的同时也会消耗内存。
2.配置需要自己计算图标和文字的显示位置,以上只给出一个示例,具体详情方法可参考附件文件或者HTML5+ API 窗口原生子View控件样式WebviewSubNViewStyles

2.定义tab窗口

将入口页也就是首页作为第一个tab窗口页面,这样做就只需创建3个子webview,相比把tab窗口都作为子webview,占用内存更少,消耗性能也优化更多,而且创建子webview需要等待plusReady事件触发执行,在一些低端机上,子窗口出现会有明显延迟,所以很明显,相比之下直接将首页作为第一个tab窗口页面的方法更优!

首页显示了,接下来是在首页plusReady事件中创建其余tab选项卡对应的窗口,通过webviewObj.append()方法,添加为当前 webview 的 子 webview,方式类似 Hello MUI 实例中 tab bar选项卡 -> 底部选项卡-webview模式

var self = plus.webview.currentWebview();  
// 初始化首页tab窗口为首次显示  
var temp = {};  
temp[self.id] = "true";  
mui.extend(aniShow, temp);  

// 初始化首个tab 按钮,(此方法为自定义方法,详情见上传的附件中)  
toggleNview(self.getStyle().subNViews[0], 0);  

// 初始化其余tab窗口  
for(var i = 0, len = subpages.length; i < len; i++) {  
if(!plus.webview.getWebviewById(subpages[i])) {  
       // 创建子webview  
    var sub = plus.webview.create(subpages[i], subpages[i], subpage_style);  
    // 隐藏子窗口  
        sub.hide();     

    // 添加为当前webview 的子 webview ------此步骤很重要  
    self.append(sub);  
    }  
}

3.底部选项卡添加事件监听

目前底部已经生成四个选项卡,鉴于最新方案已更改为以subnview中创建tag的方式生成选项卡的原因,实现方法为在view控件上添加点击事件监听,然后通过判断点击位置实现tab切换。
部分代码如下:

/**  
 * 根据判断view控件点击位置判断切换的tab  
 */  
nview.addEventListener('click', function(e) {  
    var clientX = e.clientX;  

    if(clientX > 0 && clientX <= parseInt(pageW * 0.25)) {  
        currIndex = 0;  
    } else if(clientX > parseInt(pageW * 0.25) && clientX <= parseInt(pageW * 0.45)) {  
        currIndex = 1;  
    } else if(clientX > parseInt(pageW * 0.45) && clientX <= parseInt(pageW * 0.8)) {  
        currIndex = 2;  
    } else {  
        currIndex = 3;  
    }  
    // 匹配对应tab窗口      
    if(currIndex > 0) {  
        targetPage = plus.webview.getWebviewById(subpages[currIndex - 1]);  
    } else {  
        targetPage = plus.webview.currentWebview();  
    }  

    if(targetPage == activePage) {  
        return;  
    }  

    if(currIndex !== 3) {   
        //底部选项卡切换  
        util.toggleNview(currIndex);  
        // 子页面切换  
        util.changeSubpage(targetPage, activePage, aniShow);  
        //更新当前活跃的页面  
        activePage = targetPage;  
    } else {  
        //第四个tab 打开新窗口  
        plus.webview.open('html/new-webview.html', 'new', {}, 'slide-in-right', 200);  
    }  
});

说明:点击事件中包含tab选项卡以及tab窗口切换,详情见附件

到这里其实绘制底部选项卡和webview模式的子页面,并且给选项卡添加了监听事件,已经初步完成。
但是小伙伴们还想在这样的模式下给底部中央一个凸起的悬浮大图标,可以怎么做呢,其实原理是一样的,也可以在manifest.json中配置,但是考虑到水平中央的图标涉及屏幕分辨率动态计算目前放在首页plusReady事件中配置,也给开发者一种可以手动绘制view控件的方法,如果你想在非首页采用这种方法呢。

4.利用nativeObj绘制凸起图标参考 HTML 5+ API plus.nativeObj.View
实现步骤如下:
1.首页plusReady事件中创建实例对象,最新方案中将背景nview和图标nview整合到了一起。
示例如下:

// 设置水平居中位置  
var leftPos = Math.ceil((window.innerWidth - 60) / 2);  

/**  
 * 简单封装了绘制原生view控件方法 drawNative()  
 * 绘制内容支持font(文本,字体图标),图片img , 矩形区域rect  
 *  
 */  
function drawNative(id, styles, tags) {  
    var view = new plus.nativeObj.View(id, styles, tags);  
    return view  
}  

/**   
 * drawNativeIcon 绘制带边框的半圆,  
 * 实现原理:  
 *   id为bg的tag 创建带边框的圆  
 *   id为bg2的tag 创建白色矩形遮住圆下半部分,只显示凸起带边框部分  
 *   id为iconBg的红色背景图  
 *   id为icon的字体图标  
 *   注意创建先后顺序,创建越晚的层级越高  
 */  
var drawNativeIcon = util.drawNative('icon', {  
    bottom: '5px',  
    left: leftPos + 'px',  
    width: '60px',  
    height: '60px'  
}, [{  
    tag: 'rect',  
    id: 'bg',  
    position: {  
        top: '1px',  
        left: '0px',  
        width: '100%',  
        height: '100%'  
    },  
    rectStyles: {  
        color: '#fff',  
        radius: '50%',  
        borderColor: '#ccc',  
        borderWidth: '1px'  
    }  
}, {  
    tag: 'rect',  
    id: 'bg2',  
    position: {  
        bottom: '-0.5px',  
        left: '0px',  
        width: '100%',  
        height: '45px'  
    },  
    rectStyles: {  
        color: '#fff'  
    }  
}, {  
    tag: 'rect',  
    id: 'iconBg',  
    position: {  
        top: '5px',  
        left: '5px',  
        width: '50px',  
        height: '50px'  
    },  
    rectStyles: {  
        color: '#d74b28',  
        radius: '50%'  
    }  
}, {  
    tag: 'font',  
    id: 'icon',  
    text: '\ue600', //此为字体图标Unicode码'\e600'转换为'\ue600'  
    position: {  
        top: '0px',  
        left: '5px',  
        width: '50px',  
        height: '100%'  
    },  
    textStyles: {  
        fontSrc: '_www/fonts/iconfont.ttf',  
        align: 'center',  
        color: '#fff',  
        size: '30px'  
    }  
}]);  
// 添加到当前父webview中,------此步骤很重要  
self.append(drawNativeIcon);

2.view控件添加监听事件

//自定义监听图标点击事件  
drawNativeIcon.addEventListener('click', function(e) {  
    mui.alert('你点击了图标,你在此可以打开摄像头或者新窗口等自定义点击事件。','悬浮球点击事件');  
});

原生view控件使用说明

  1. 以上描述创建并绘制原生view控件有两种方法:
    a.如果是首页应用,可以在manifest.json文件中相应节点配置,渲染快,配置方法简单
    b.通过new plus.nativeObj.View(),创建View实例对象,然后将该实例对象append到当前窗口(plus.webview.currentWebview)
  2. 这两种方法都需要开发者在首页plusReady事件中去针对view控件添加监听事件,比如切换对应窗口这种。
  3. view控件支持绘制类型:图片、矩形区域、文本(包括字体图标)
  4. 字体图标比如该悬浮球字体,来自iconfont.cn 阿里巴巴字体库,字体一般为Unicode(16进制)码:'\e600',但是配置的时候,需要填写为"\ue600"才能被原生层正确解析
  5. 参考 HTML 5+ API plus.nativeObj.View

该示例补充说明

  1. 中央悬浮大图标都是通过字体加纯色背景实现
  2. 其背后带边线半球区域也是通过两个矩形区域的view控件叠加拼成的,具体实现方法可以在附件中查看
  3. 本示例字体文件分别采用mui字体文件和阿里巴巴字体库
  4. 第四个选项卡点击打开新窗口,为示例教程,开发者可自行定义
  5. 屏幕旋转后,需手动重绘nview,参考nview的api。虽复杂也可以实现。也可以在manifest里禁用掉旋转。

扩展链接

字体图标绘制原生view控件
如何更新通过nativeObj.View创建的view控件教程

下载附件中的文件,直接解压真机运行即可体验

67 关注 分享
DCloud_heavensoft 赵梦欢 4***@qq.com 5个萝卜 Neil_HL w***@163.com ahua 9***@qq.com 幽壑潜蛟 4***@qq.com c***@126.com 邓荣勇 lufei 妙妙 sonpu 5***@qq.com 逐鹿实验室 可苦可乐 独木为夕 洛上千栀 5***@qq.com 阿拉斌 1***@qq.com 8***@qq.com 小哥哥 492886274 hylong hanshang x***@163.com 一枚小前端 2***@qq.com 1***@qq.com nativelearning 4***@qq.com iMaldway 8***@qq.com 5***@qq.com y***@163.com 1***@qq.com 7***@qq.com 6***@qq.com x***@163.com 小小刀 j***@126.com gzg 李虎 wlzygxw uni_wu 1***@163.com SimpleJalon

要回复文章请先登录注册

5***@qq.com

5***@qq.com

回复 n***@gmail.com :
遇到了闪退的问题,将utils.js 中changeSubpage plus.webview.getLaunchWebview 修改为plus.webview.currentWebview既可。。。
2018-01-30 17:43
叶雨

叶雨

在荣耀8上,圆球直接跑到左边去了!咋回事
2018-01-29 10:39
一枚小前端

一枚小前端

回复 4***@qq.com :
这两种方式都行,为了性能,可以在一个tabBar中写4个tab。我也是刚看到的,不对的地方还望指正。
2018-01-26 11:04
arjen

arjen

回复 n***@gmail.com :
用subnview创建的tabbar发现侧滑菜单出来了遮罩遮不住tabbar,请问怎么解决啊?
2018-01-25 18:11
一枚小前端

一枚小前端

回复 青柠脉动 :
嗯,谢谢,我也找到了,原来不管电脑浏览器和手机浏览器,甚至微信公众号端都不是5+运行环境,所以那些基于5+环境的原生api是没法用了。
2018-01-25 16:03
7***@qq.com

7***@qq.com

demo中的util.js文件写得有点小问题,方法changeSubpage中,aniShow[targetPage]改为aniShow[targetPage.id],temp[targetPage]改为temp[targetPage.id]
2018-01-23 15:24
青柠脉动

青柠脉动

回复 一枚小前端 :
我刚刚试了,手机上可以,电脑端无效
2018-01-23 11:17
4***@qq.com

4***@qq.com

上面的实例只写了一个tab ,有没有完整的demo啊,,,或者如果我需要4个tab,,我是要写4个tabBar,,,还是在tabBar里面的tags里面写4个 tag啊?????????????
2018-01-22 13:32
一枚小前端

一枚小前端

为什么打开底部的菜单不显示,是不是subnview只能在手机上调试显示,电脑上不显示呢?
2018-01-19 15:16
hanshang

hanshang

我使用的本页介绍的“基于subnview模式的原生tab”做APP选项卡,有两个问题:
1.首页为第一个tab窗口页面,进入后验证token,如没有则进入login页。此时首页及其余tab窗口由于在无token状态下加载页面,登陆账号后,因页面加载完毕,所以不现实相关getjson得到的数据。目前使用login页fire参数,重新加载首页,请问有没有更好的办法?
2.首页为第一个tab窗口页面,使用tab-top-webview-main.html双webview作为第二个页面,此时进入APP会出差,一直显示等待加载的loading窗口,请问有什么解决办法?
2018-01-18 10:48