【uni-app】原生与自定义导航栏开发及动态修改

7/6/2020 uni-app

使用uni-app的原生导航栏和自定义导航栏两种形式,实现一个简单的二级搜索页面

# 需求

  • 一级搜索页输入关键词并点击搜索,将进入二级搜索页面并展示搜索结果。
  • 二级搜索页顶部导航不可输入,点击后返回一级搜索页
  • 使用nvue渲染,所以文件格式都为nvue

# UI方案

# 效果预览

search-input-01-effect

# 自定义导航栏

uni-app支持关闭原生导航栏+前端标签组件模拟导航栏进行开发,同时官方也强调自定义HTML组件模拟导航栏会有性能问题[1]

官方的uni ui提供了自定义导航栏组件NavBar 导航栏 (opens new window),其中提供了带搜索框的样式。

使用自定义导航栏的,优缺点如下:

  • 优点:更方便的自定义和动态修改
  • 缺点:官方说明中的性能问题

# 基本实现思路

  1. 页面style设置navigationStyle值为custom
  2. 手动HTML绘制导航栏或直接使用插件

# 主要代码

pages.json

pages.json中配置页面navigationStylecustom

//pages.json
{
    "pages":[
        {
    	"path": "pages/customTitle/customFirst",//一级搜索
    	"style": {
        "navigationStyle":"custom"
    	}
    },{
    	"path": "pages/customTitle/customSecond",//二级搜索
    	"style": {
        "navigationStyle":"custom"
    	}
    },
    ]
}

一级搜索页:

//customFirst.nvue
<template>
  <view>
    <!-- 顶部状态栏 -->
    <view :style="`height: ${iStatusBarHeight}px;`" class="status-bar"></view>
    
    <!-- 自定义导航栏 -->
    <uni-nav-bar :fixed="false" background-color="#1577fe" color="#ffffff" >
      <!-- 搜索框 -->
      <view class="input-view">
        <uni-icons class="input-uni-icon" type="search" size="22" color="#666666" />
        <input :focus="true" confirm-type="search" class="nav-bar-input" type="text" placeholder="输入搜索关键词" @confirm="search" v-model="keyword">
      </view>
      <!-- 左插槽 -->
      <view slot="left" @click="back">
        <uni-icons type="back" color="#FFFFFF"></uni-icons>
      </view>
      <!-- 右插槽 -->
      <view slot="right" @click="search">
        <text class="search">搜索</text>
      </view>
    </uni-nav-bar>
  </view>
</template>

二级搜索页:

实际配置与一级搜索页基本一致,只不过去除了右侧的搜索 。这里增加了一短代码<view>模拟关键词搜索结果

//customFirst.nvue
<template>
	<view>
     <!-- 顶部状态栏 -->
    <view :style="`height: ${iStatusBarHeight}px;`" class="status-bar"></view>
     <!-- 自定义导航栏 -->
    <uni-nav-bar :fixed="false" background-color="#1577fe" color="#ffffff"  >
    <!-- 搜索框 -->
    <view class="input-view" @click="back">
      <uni-icons class="input-uni-icon" type="search" size="22" color="#666666" />
      <input class="nav-bar-input" type="text" :placeholder="keyword">
    </view>
    <!-- 左插槽 -->
    <view slot="left" @click="back">
      <uni-icons type="back" color="#FFFFFF" ></uni-icons>
    </view>
   </uni-nav-bar>
 	<view class="">
     测试关键词: {{keyword}}
   </view>
  </view>
</template>

# 注意事项

  1. 页面设置为自定义导航栏时,需要设置一个status-bar的占位高度,否则内容会直接顶到顶部。nvue中可以通过获取状态栏高度+动态style的方式,其他模式可以直接使用uni-app的css变量 (opens new window) var(--status-bar-height)设置占位块的高度。
<!-- 顶部状态栏 -->
<view :style="`height: ${iStatusBarHeight}px;`" class="status-bar"></view>
<!-- 顶部状态栏 -->
  1. 使用navBar插件实现导航栏搜索框,如果需要修改搜索框两侧占位button的宽度,修改uni-nav-bar.vue插件源码中的样式中uni-navbar__header-btns-leftuni-navbar__header-btns-rightuni-navbar__header-btns的宽度width值即可。如果你使用slot插槽去设置左右占位button的宽度,也可以直接注释掉这几个样式的width

# 原生导航栏

原生导航栏中的searchInputbackbuttonbuttons都是在pages.json中完成配置的。具体可见官方文档:导航栏 (opens new window)

优点:页面UI实现起来很方便。只需要对searchInputbackbuttonbuttons进行样式的配置即可

缺点:

  • 想要动态修改搜索框里的值,会很麻烦。官方并没有提供这样的API;在APP端需要通过H5+的webview修改,而H5端则需要通过修改dom的操作来执行。
  • 需要对不同端分别进行配置。如需要在APP端和H5端实现原生搜索框,则需要在style中分别对h5app-plus进行配置。

# 基本实现思路

页面style设置配置searchInput(中间的搜索框)和buttons(左边的返回和右边的搜索按钮)

# 主要代码

//originFirst.nvue
<template>
	<view class=""></view>
</template>
//originSecond.nvue
<template>
	<view class="">keyword : {{keyword}}</view>
</template>

//pages.json
{
    pages:[
          {
      "path": "pages/originTitle/originTitle",
      "style": {
        "navigationBarBackgroundColor": "#1577fe",
        "navigationBarTitleText": "uni-app",
        "h5": {
          "titleNView": {
            "autoBackButton": true,//自动显示回退按钮
            "backButton": {//自定义回退按钮
              "background": "#FFFFFF",
              "color": "#FFFFFF",
              "colorPressed": "#FFFFFF"
            },
            "searchInput": {//自定义搜索框
              "align": "left",//文本对齐方式
              "placeholder": "搜索",//搜索框占位符
              "borderRadius": "5px",//角半径
              "backgroundColor": "#FFFFFF",//背景色
              "autoFocus": true//自动聚焦
            },
            "buttons": [{
              "color": "#FFFFFF",
              "fontSize": "14px",
              "text": "搜索"
            }]
          }
        },
        "app-plus": {
          "titleNView": {
            "backButton": {
              "color": "#FFFFFF",
              "colorPressed": "#FFFFFF"
            },
            "searchInput": {
              "align": "left",
              "placeholder": "搜索",
              "borderRadius": "5px",
              "backgroundColor": "#FFFFFF",
              "autoFocus": true
            },
            "buttons": [{
              "color": "#FFFFFF",
              "fontSize": "14px",
              "text": "搜索"
            }]
          }
        }
      }
    ]
}

# 动态修改

无论是自定义导航栏,还是uni-app的原生导航栏,都是可以进行样式修改的。

但是,就像之前提到过的那样,uni-app没有提供对searchInput进行修改的API,所以需要通过H5+等API或者直接修改dom等方式进行修改。

# 参数传递

回到需求中去,可以看到主要需要修改的内容为一级搜索页面和二级搜索页面的 searchInput 中展示的关键词。

这里,可以在一级页面和二级页面分别定义一个keyword,并通过uni-app的页面跳转传参来实现在不同页面keyword的赋值。

用户在一级搜索页面点击搜索,则执行search函数,通过带参数的uni.navigateTo实现一级页面到二级页面的传参。

而二级页面回到一级页面,uni.navigateBack无法传递参数,但是可以使用getCurrentPages()拿到上一个页面,调用页面的$set方法来修改上一个页面的keyword值。

//一级搜索页面
data(){
    return{
        keyword:''
    }
},
methods(){
    search() {//点击搜索后运行
        uni.navigateTo({
            url:`./customSecond?keyword=${this.keyword}`
        })
    }
}
//二级搜索页面
data(){
    return{
        keyword:''
    }
},
onLoad(e){
    this.keyword = e.keyword; //onLoad周期可以拿到上一个页面传过来的参数
},
methods(){
    back(){//点击导航栏的输入框,或者回退button后运行
        var pages = getCurrentPages();
		var prevPage = pages[pages.length - 2]; //上一个页面
		prevPage.$set(prevPage, "keyword", this.keyword)
		uni.navigateBack()
    }
}

# 自定义导航栏

实现了参数的传递后,自定义导航栏的参数修改可以说是非常简单了。

在本例中,一级页面中的v-model="keyword"把搜索框的值传递给keyword,当搜索框的输入值变化时,keyword值也会同步变化。

而进入二级搜索页面时,可以通过输入框的:placeholder="keyword",直接就可以把keyword值赋值给inputplaceholder,在onLoad钩子拿到值之后,被被渲染到页面中。

# 原生导航栏

uni-app提供了一些**页面生命周期函数 (opens new window)**,允许动态获取输入框的值,以及监听导航栏的相关事件

  • onNavigationBarSearchInputChanged:监听原生标题栏搜索输入框输入内容变化事件
  • onNavigationBarSearchInputConfirmed:监听原生标题栏搜索输入框搜索事件,用户点击软键盘上的“搜索”按钮时触发。
  • onNavigationBarSearchInputClicked:监听原生标题栏搜索输入框点击事件
  • onNavigationBarButtonTap:监听原生标题栏按钮点击事件,参数为Object
  • onBackPress:监听页面返回,返回 event = {from:backbutton、 navigateBack}backbutton 表示来源是左上角返回按钮或 android 返回键;navigateBack表示来源是 uni.navigateBack ;详细说明及使用:onBackPress 详解 (opens new window)

在一级搜索页面,可以在onNavigationBarSearchInputChanged钩子获取搜索框的输入值,并将其赋值给keyword;在onNavigationBarSearchInputConfirmedonNavigationBarButtonTap(点击搜索按钮)调用search函数,进行页面的跳转

在二级搜索页面的onNavigationBarSearchInputClicked,调用back()方法返回一级搜索页面。

# APP端——H5+ API

官方示例

官方的uni-app在App端动态修改原生导航栏 (opens new window)代码示例中,提供了相关的思路及api使用:

  1. 获取webView对象,通过webView的相关APIsetTitleNViewButtonStyle(修改导航栏按钮),setTitleNViewButtonBadge(修改导航栏按钮的角标),setTitleNViewSearchInputFocus(修改导航栏搜索框的focus状态)

  2. 通过currentWebviewgetStyle()setStyle()方法进行更新。

    详细的顶部标题栏相关的可修改样式,可以参考H5+ API--Webview

这篇官方的这篇示例文章是2018年的,代码中使用的webview的API已经更新,需要通过新的API去获取webView对象。示例代码更新后应该如下:

// #ifdef APP-PLUS  
var webView = this.$mp.page.$getAppWebview();  
var currentWebview = plus.webview.currentWebview();
// 修改buttons  
// index: 按钮索引, style {WebviewTitleNViewButtonStyles }  
webView.setTitleNViewButtonStyle(0, {  
    text: 'hello',  
});  

// 修改按钮上的角标  
// index: 按钮索引, text: 角标文本内容  
webView.setTitleNViewButtonBadge({  
    index: 0,  
    text: 10,  
});  

// 设置 searchInput的 focus  
// focus: true | false  
webView.setTitleNViewSearchInputFocus(true)  

// 设置 searchInput的 text  
webView.setTitleNViewSearchInputText(text)  

// searchInput 通过 webview 的 setStyle 方法进行更新  
var tn = currentWebview.getStyle().titleNView;  
if (tn.buttons) {    
uni.getSystemInfo({    
    success:function(res){    
        if (res.platform=="ios") { // 这里在HBuilderX 1.9.9版本有个bug,searchInput的I变小写了 ,临时绕过下。更高版本会修复此bug    
            tn.searchinput.placeholder = 'test';    
            currentWebview.setStyle({    
                titleNView: tn    
            });    
        } else{    
            tn.searchInput.placeholder = 'test'; //这里有个已知bug,HBuilderX 1.9.9上,当searchInput位于首页时,动态设置placehold会导致buttons的点击事件消失。更高版本会修复此bug    
            currentWebview.setStyle({    
                titleNView: tn    
            });    
        }    
    }    
})    
}    

// #endif

参考示例代码,APP端使用plus API修改一级页面的输入值:

var currentWebview = plus.webview.currentWebview();
currentWebview.setTitleNViewSearchInputText(this.keyword);

修改二级搜索页面的placeholder

var currentWebview = plus.webview.currentWebview();
currentWebview.setTitleNViewSearchInputFocus(false);
var tn = currentWebview.getStyle().titleNView;
if (tn) {
    uni.getSystemInfo({
        success: function(res) {
            if (res.platform == 'ios') {
                tn.searchInput.placeholder = this.keyword;
                currentWebview.setStyle({
                    titleNView: tn
                });
            } else {
                tn.searchInput.placeholder = this.keyword;
                currentWebview.setStyle({
                    titleNView: tn
                });
            }
        }
    });
}

# H5端——DOM操作修改

JS模拟事件更新input:

function changeInput(dom, st) {
    var evt = new InputEvent('input', {
        inputType: 'insertText',
        data: st,
        dataTransfer: null,
        isComposing: false
    });
    dom.value = st;
    dom.dispatchEvent(evt);
}

修改一级搜索页面搜索框值value

const page = document.querySelectorAll(".uni-input-input[type=search]")[0];
changeInput(page,this.keyword)

直接修改二级搜索页面搜索框的placeholder

const page = document.querySelectorAll(".uni-input-input[type=search]")[0];
pages.placeholder = this.keyword;

需要注意的是,在H5端修改dom,要在onReady进行更新,否则会被实例的默认值覆盖。

# 总结

对比下来,虽然uni-app声称原生导航框具有更高的性能,所有样式集中在pages.json中让页面代码看似让template中的代码简洁许多。但是使用起来颇为复杂一旦涉及到导航栏参数的修改,则需要调用各种API拿到参数值或者去修改参数值,对于不同端,还需要进行不同的操作。

而自定义导航栏,在动态设置方面给了很大的操作空间。同时,修改样式也更加简单。

所以,如果你的标题栏是静态配置的,那么原生导航栏是一个不错的选择;如果你需要对标题栏的内容进行任何程度的修改,那么还是建议使用自定义导航栏进行导航栏的开发。

# 引用

[1] uni-app导航栏开发指南 (opens new window)

[2] uni-app 页面生命周期函数 (opens new window)

[3] uni-app在App端动态修改原生导航栏 (opens new window)

[4] HTML5 中国产业联盟 > API Reference > webview (opens new window)

Home~in this corner~
Leina