React 原生导航底部标签,浮动标签栏位于屏幕内容上方

React native navigation bottom tabs, floating tab bar is over the screen content

提问人:maggyy 提问时间:11/6/2023 最后编辑:maggyy 更新时间:11/6/2023 访问量:59

问:

我正在使用 React Native 和 React Navigation,并使用 position: 'absolute' 将底部标签栏设置为浮动。我还在外容器上设置了一个透明的背景,这样我就可以看到它后面的内容。但是,我面临着一个问题,即屏幕内容呈现在选项卡栏后面,使其无法访问。

为了解决这个问题,我尝试将 paddingBottom 添加到内容容器中,但随后在它应该保持透明的位置出现了灰色背景。看来我无法同时获得填充和透明效果。我已经广泛搜索,但没有找到解决方案。这是我代码的相关部分:

在这里查看图片

MainTabs.js

const Tab = createBottomTabNavigator();
const ICON_SIZE = 16;
const ICON_SIZE_FOCUSED = 24;
const ADDITIONAL_PADDING = 10;
const SIZE_DIFFERENCE = (ICON_SIZE_FOCUSED - ICON_SIZE) / 2 + ADDITIONAL_PADDING;

const MainTabs = () => {
  const startup = useSelector(state => state.globalPersist?.startup);

  const mainMenu = useMemo(() => startup?.mainMenu || {}, [startup]);

  const tabBar = props => (
    <CustomTabBar {...props} getAccessibilityLabel={({route}) => getTabBarAccessibilityLabel(route.name)} />
  );

  return (
    <Tab.Navigator
      tabBar={tabBar}
      tabBarOptions={{
        activeTintColor: NASColors.red,
        keyboardHidesTabBar: true,
        style: {
          position: 'absolute',
          marginBottom: 100,
          bottom: 100,
        },
      }}
      screenOptions={({route}) => ({
        tabBarAccessibilityLabel: getTabBarAccessibilityLabel(route.name),
        tabBarTestID: route.name,
        title: route.name,
        style: {
          position: 'absolute',
          marginBottom: 150,
          bottom: 100,
        },
        tabBarIcon: ({color, focused}) => <TabBarIconComponent route={route} color={color} focused={focused} />,
      })}>
      <Tab.Screen
        name={ScreenNames.MyTravels}
        component={MyTravelsStackScreen}
        options={{tabBarLabel: mainMenu?.[0]?.text}}
      />
      <Tab.Screen
        name={ScreenNames.Book}
        component={BookStackScreen}
        options={{
          tabBarLabel: mainMenu?.[1]?.text,
        }}
      />
      <Tab.Screen
        name={ScreenNames.Profile}
        component={ProfileStackScreen}
        options={{
          tabBarLabel: mainMenu?.[2]?.text,
        }}
      />
      <Tab.Screen
        name={ScreenNames.More}
        component={MoreStackScreen}
        options={{
          tabBarLabel: mainMenu?.[3]?.text,
        }}
      />
    </Tab.Navigator>
  );
};

const TabBarIconComponent = ({route, color, focused}) => {
  const iconMapping = {
    [ScreenNames.MyTravels]: SvgIcons.MyTravels,
    [ScreenNames.Book]: SvgIcons.Search,
    [ScreenNames.Profile]: SvgIcons.Profile,
    [ScreenNames.More]: SvgIcons.Dots,
  };

  const IconComponent = iconMapping[route.name];
  if (!IconComponent) {
    return null;
  }

  return (
    <View style={focused ? {paddingTop: SIZE_DIFFERENCE} : {}}>
      <IconComponent
        color={color}
        width={focused ? ICON_SIZE_FOCUSED : ICON_SIZE}
        height={focused ? ICON_SIZE_FOCUSED : ICON_SIZE}
      />
    </View>
  );
};

export default MainTabs;

自定义标签栏.js


const TabButton = ({route, descriptors, isFocused, handleTabPress}) => {
  const {options} = descriptors[route.key];
  const label = options.tabBarLabel || options.title || route.name;

  return (
    <TouchableOpacity
      key={route.key}
      onPress={() => handleTabPress(isFocused, route)}
      style={styles.tabButton}
      accessibilityRole="button"
      accessibilityLabel={label}
      {...testID(route.name)}
      accessibilityState={{selected: isFocused}}>
      {options?.tabBarIcon?.({
        focused: isFocused,
        color: isFocused ? NASColors.red : NASColors.blue,
      })}
      <Text style={[styles.label, {color: isFocused ? NASColors.transparent : NASColors.blue}]}>{label}</Text>
    </TouchableOpacity>
  );
};

const FocusedLine = ({tabWidth, animatedValue, state, padding, containerWidth}) =>
  tabWidth > 0 && (
    <Animated.View
      style={[
        styles.focusedLine,
        {
          width: tabWidth * 0.6,
          transform: [
            {
              translateX: animatedValue.interpolate({
                inputRange: [0, state.routes.length - 1],
                outputRange: [padding + tabWidth * 0.2, containerWidth - tabWidth * 0.8 - padding],
              }),
            },
          ],
        },
      ]}
    />
  );

const CustomTabBar = ({state, descriptors, navigation}) => {
  const isKeyboardVisible = useKeyboardVisibility();
  const animatedValue = useRef(new Animated.Value(state.index)).current;
  const [tabWidth, setTabWidth] = useState(0);
  const [containerWidth, setContainerWidth] = useState(0);
  const padding = 10;

  useEffect(() => {
    requestAnimationFrame(() => {
      Animated.timing(animatedValue, {
        toValue: state.index,
        duration: 400,
        easing: Easing.bezier(0.23, 1, 0.32, 1),
        useNativeDriver: true,
      }).start();
    });
  }, [animatedValue, state.index]);

  const handleTabPress = (isFocused, route) => {
    const event = navigation.emit({
      type: 'tabPress',
      target: route.key,
      canPreventDefault: true,
    });

    if (!event.defaultPrevented) {
      if (isFocused) {
        navigation.popToTop();
      } else {
        navigation.navigate(route.name);
      }
    }
  };

  const onLayout = event => {
    const {width} = event.nativeEvent.layout;
    setContainerWidth(width);
    setTabWidth((width - padding * 2) / state.routes.length);
  };

  if (isKeyboardVisible) return null;

  return (
    <View style={styles.outerContainer}>
      <LinearGradient
        colors={['rgba(241, 241, 241, 0)', 'rgba(241, 241, 241, 0.90)']}
        start={{x: 0.1, y: 0}}
        end={{x: 0.1, y: 0.2}}
        style={styles.gradientOverlay}
      />
      <View style={styles.innerContainer} onLayout={onLayout}>
        {state.routes.map((route, index) => (
          <TabButton
            key={route.key}
            route={route}
            descriptors={descriptors}
            isFocused={state.index === index}
            handleTabPress={handleTabPress}
          />
        ))}
        <FocusedLine
          tabWidth={tabWidth}
          animatedValue={animatedValue}
          state={state}
          padding={padding}
          containerWidth={containerWidth}
        />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  outerContainer: {
    flexDirection: 'row',
    position: 'absolute',
    justifyContent: 'center',
    paddingLeft: 10,
    paddingRight: 10,
    paddingBottom: 15,
    width: '100%',
    height: 100,
    bottom: 0,
  },
  innerContainer: {
    backgroundColor: NASColors.white,
    borderRadius: 4,
    flexDirection: 'row',
    justifyContent: 'space-around',
    alignItems: 'center',
    padding: 10,
    width: '97%',
    alignSelf: 'center',
    height: 65,
  },
  gradientOverlay: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    height: 125,
  },
  tabButton: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  focusedLine: {
    position: 'absolute',
    alignSelf: 'center',
    top: 50,
    height: 4,
    borderRadius: 1,
    backgroundColor: NASColors.blue,
  },
  label: {
    marginTop: 5,
    fontSize: 12,
    fontWeight: NASFontWeights.Medium,
  },
});

export default CustomTabBar;

react-native 导航 填充 样式 bottomtab

评论

0赞 user18309290 11/6/2023
如果需要,将 paddingBottom 添加到屏幕(例如,MyTravelsStackScreen)。
0赞 maggyy 11/6/2023
@user18309290是的,但是无论何时何地,当我添加填充时,这仍然会添加灰色背景

答: 暂无答案