定製化你的ReactNative底部導航欄

小綠和小藍發表於2019-01-29

定製化你的ReactNative底部導航欄

前言

​ 接觸過ReactNative(以下簡稱RN)的大概都知道,react-navigation提供了兩種開箱即用的導航欄元件

分別是這樣的

createBottomTabNavigator

createMaterialBottomTabNavigator

儘管官方提供了導航欄的開箱即用方案,但是實際開發裡面,我們會遇到各種各樣的導航欄,各種各樣的動效,所以以上可能無法滿足我們的開發需求,我們就需要定製化的去做我們導航欄

例如我們UI給我的導航欄樣式

定製化你的ReactNative底部導航欄

我的內心: 這他麼中間凸起的我怎麼做,老子只是一個小前端,app很渣啊啊啊

定製化你的ReactNative底部導航欄

藉助可愛的google,我找到了解決方法

就是

定製化你的ReactNative底部導航欄

TabBarComponent

這個api在文件資料很少,所以想要知道怎麼用只能通過網路上的資源了

其中深受這篇文案的啟發

Let's Create A Custom Animated Tab Bar With React Native

這位外國友人(話說reactnative在國外似乎還有點火),藉助動畫庫react-native-pose,完成了這樣的效果

定製化你的ReactNative底部導航欄

雖然是英文部落格,但是配合翻譯基本閱讀無障礙,藉助他的部落格,我完成了ReactNative的自定義導航欄,效果如下

定製化你的ReactNative底部導航欄

自定義底部導航欄

  1. 自定義底部導航欄是基於createBottomTabNavigator,所以我們使用這個api來建立底部導航欄
  2. 指定createBottomTabNavigator的tabBarComponent
  3. tabBarComponent內部進行底部導航欄的編寫

增加底部導航器

import React from 'react'
import { createBottomTabNavigator } from 'react-navigation'
import Icon from '../Common/Icon' // 自定義圖示庫
import TabBar from '../Common/TabBar' // tabBarComponent 自定義元件
// 頁面
import Category from '../View/TabBar/Category/Category'
import Main from '../View/TabBar/Main/Main'
import My from '../View/TabBar/My/My'
import OrderList from '../View/TabBar/OrderList/OrderList'
import OnlineDoctor from '../View/TabBar/OnlineDoctor/OnlineDoctor'
export default createBottomTabNavigator(
  {
    // 首頁:
    one: {
      screen: Main,
      navigationOptions: () => {
        return {
          tabBarIcon: ({ tintColor }) => {
            var soureImge
            if (tintColor == '#CBCBCB') {
              soureImge = 'main'
            } else {
              soureImge = 'mainActive'
            }
            return <Icon name={soureImge} size={26} color={tintColor} />
          }
        }
      }
    },
    //分類:
     two: {
      screen: Category,
      navigationOptions: {
        tabBarIcon: ({ tintColor }) => {
          var soureImge
          if (tintColor == '#CBCBCB') {
            soureImge = 'Category'
          } else {
            soureImge = 'CategoryActive'
          }
          return <Icon name={soureImge} size={26} color={tintColor} />
        }
      }
    },
    //問診:
    three: {
      screen: OnlineDoctor,
      navigationOptions: {
        tabBarIcon: ({ tintColor }) => {
          var soureImge
          if (tintColor == '#CBCBCB') {
            soureImge = 'onLine'
          } else {
            soureImge = 'onLineActive'
          }
          return <Icon name={soureImge} size={48} color={tintColor} />
        }
      }
    },
    // 購物籃: 
    four: {
      screen: OrderList,
      navigationOptions: {
        tabBarIcon: ({ tintColor }) => {
          var soureImge
          if (tintColor == '#CBCBCB') {
            soureImge = 'OrderList'
          } else {
            soureImge = 'OrderListActive'
          }
          return <Icon name={soureImge} size={26} color={tintColor} />
        }
      }
    },
    //我的:
    five: {
      screen: My,
      navigationOptions: () => {
        return {
          tabBarIcon: ({ tintColor }) => {
            var soureImge
            if (tintColor == '#CBCBCB') {
              soureImge = 'My'
            } else {
              soureImge = 'MyActive'
            }
            return <Icon name={soureImge} size={26} color={tintColor} />
          }
        }
      }
    }
  },
  {
    initialRouteName: 'one', // 初始化頁面
    tabBarComponent: TabBar,
    tabBarOptions: {
      activeTintColor: '#F34C56',
      inactiveTintColor: '#CBCBCB'
    }
  }
)
        
複製程式碼

工具函式

圖示沒有使用圖示庫,直接搞一個圖示庫比較得心應手

../Common/Icon.js

import React from 'react'
import { Image } from 'react-native'
import { TabIcon } from './Image'

const Icon = ({ name, style, size }) => {
  const icon = TabIcon[name]
  return (
    <Image
      source={icon}
      style={[{ width: size, height: size }, style]}
    />
  )
}

export default Icon
複製程式碼

而對於圖片則進行統一管理

../Common/Image.js

/**
 * 所有的圖片資源都從這裡統一管理
 */
// 底部導航欄的圖片資源
export const TabIcon = {
  main: require('..'),
  mainActive: require('..'),
  Category: require('..'),
  CategoryActive: require('..'),
  onLine: require('..'),
  onLineActive: require('..'),
  OrderList: require('..'),
  OrderListActive: require('..'),
  My: require('..'),
  MyActive: require('..'),
}
複製程式碼

自定義底部導航器

萬事俱備,下面就是自定義底部導航器了,就和定義React元件一樣

import React from 'react'
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  TouchableNativeFeedback,
  Dimensions
} from 'react-native'
import posed from 'react-native-pose' // react-native 動畫庫

const Scaler = posed.View({ // 定義點選縮放
  active: { scale: 1 },
  inactive: { scale: 0.9 }
})

const TabBar = props => {
  const {
    renderIcon,
    getLabelText,
    activeTintColor,
    inactiveTintColor,
    onTabPress,
    onTabLongPress,
    getAccessibilityLabel,
    navigation
  } = props

  const { routes, index: activeRouteIndex } = navigation.state
  return (
    <Scaler style={Styles.container}>
      {routes.map((route, routeIndex) => {
        const isRouteActive = routeIndex === activeRouteIndex
        const tintColor = isRouteActive ? activeTintColor : inactiveTintColor
        return (
          <TouchableNativeFeedback
            key={routeIndex}
            style={Styles.tabButton}
            onPress={() => {
              onTabPress({ route })
            }}
            onLongPress={() => {
              onTabLongPress({ route })
            }}
            accessibilityLabel={getAccessibilityLabel({ route })}
          >
            {route.key == 'three' ? ( // 對特殊圖示進行特殊處理
              <Scaler
                style={Styles.scalerOnline}
                pose={isRouteActive ? 'active' : 'inactive'}
              >
                {renderIcon({ route, focused: isRouteActive, tintColor })}
                <Text style={Styles.iconText}>{getLabelText({ route })}</Text>
              </Scaler>
            ) : ( // 普通圖示普通處理
              <Scaler
                style={Styles.scaler}
                pose={isRouteActive ? 'active' : 'inactive'}
              >
                {renderIcon({ route, focused: isRouteActive, tintColor })}
                <Text style={Styles.iconText}>{getLabelText({ route })}</Text>
              </Scaler>
            )}
          </TouchableNativeFeedback>
        )
      })}
    </Scaler>
  )
}

const Styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    height: 53,
    borderWidth: 1,
    borderRadius: 1,
    borderColor: '#EEEEEE',
    shadowOffset: { width: 5, height: 10 },
    shadowOpacity: 0.75,
    elevation: 1
  },
  tabButton: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  spotLight: {
    width: tabWidth,
    height: '100%',
    justifyContent: 'center',
    alignItems: 'center'
  },
  spotLightInner: {
    width: 48,
    height: 48,
    backgroundColor: '#ee0000',
    borderRadius: 24
  },
  scaler: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  scalerOnline: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'flex-end',
  },
  iconText: {
    fontSize: 12,
    lineHeight: 20
  }
})

export default TabBar
複製程式碼

最後實現的效果就是

定製化你的ReactNative底部導航欄

如果你也有這樣的需求,可以看看老外發布的那篇部落格

Let's Create A Custom Animated Tab Bar With React Native

最後: 快要過年了,祝大家新年快樂

相關文章