4

我正在制作一个项目(react native,expo,react navigation 5),我想在底部选项卡中添加一个自定义“添加”按钮,但是因为......

导航器只能包含“屏幕”组件作为其直接子级

...我需要找到一种方法来传递我的自定义组件。

看起来很容易,我的意思是有文档:

...但是在查看这些和其他人的问题时,我要么只找到了一些复杂的例子,要么发现了如何在早期版本中实现这一点的例子。

最后,我找到了一个简单的解决方案,到目前为止它就像一个魅力(非常感谢任何关于为什么这可能是一个糟糕的想法的建议)。

如果有人在类似的泡菜中,我想我会发布我的解决方案。请参阅下面的答案。

4

4 回答 4

4

将组件放置在导航器之外,并使用 css 将其放置在选项卡上方。如示例所示,将选项卡的图标向左和向右调整。

就像我上面所说的,热烈欢迎有关如何以不同方式实现这一目标的建议,但还没有遇到任何问题(敲木头)。

这是它的样子:

在此处输入图像描述

这是兔子:

import React from 'react';
import { createMaterialBottomTabNavigator } from '@react-navigation/material-bottom-tabs';
import { Ionicons, MaterialIcons, FontAwesome } from '@expo/vector-icons';

import AddButton from '../../components/UI/AddButton';
import SpotlightProductsScreen from './SpotlightProductsScreen';
import ProductsScreen from './ProductsScreen';
import UserSpotlightScreen from './../user/UserSpotlightScreen';
import UserProductsScreen from './../user/UserProductsScreen';


const ProductsOverviewScreen = props => {

  const Tab = createMaterialBottomTabNavigator();

  return (
    <>
      <AddButton
        navigation={props.navigation}
        style={{
          position: 'absolute',
          zIndex: 99,
          bottom: 5,
          alignSelf: 'center',
          shadowColor: 'black',
          shadowOpacity: 0.15,
          shadowOffset: { width: 0, height: 2 },
          shadowRadius: 8,
          elevation: 3 //Because shadow only work on iOS, elevation is same thing but for android.
        }}
      />
      <Tab.Navigator
        initialRouteName="Spotlight"
        labeled={false}
        shifting={true}
        activeColor="#f0edf6"
        inactiveColor="#3e2465"
        barStyle={{ backgroundColor: 'rgba(127,63,191,.9)' }}
      >
        <Tab.Screen
          name="Spotlight"
          component={SpotlightProductsScreen}
          options={{
            tabBarIcon: ({ color }) => (
              <Ionicons
                name={
                  Platform.OS === 'android'
                    ? 'md-notifications'
                    : 'ios-notifications'
                }
                color={color}
                size={27}
                style={{
                  marginLeft: -35
                }}
              />
            )
          }}
        />
        <Tab.Screen
          name="Förråd"
          component={ProductsScreen}
          options={{
            tabBarIcon: ({ color }) => (
              <MaterialIcons
                name={'file-download'}
                color={color}
                size={27}
                style={{
                  marginLeft: -70
                }}
              />
            )
          }}
        />
        <Tab.Screen
          name="Mitt Förråd"
          component={UserProductsScreen}
          options={{
            tabBarIcon: ({ color }) => (
              <MaterialIcons
                name={'file-upload'}
                color={color}
                size={30}
                style={{
                  marginRight: -70
                }}
              />
            )
          }}
        />
        <Tab.Screen
          name="Min Sida"
          component={UserSpotlightScreen}
          options={{
            tabBarBadge: 4,
            tabBarIcon: ({ color }) => (
              <FontAwesome
                name={'user'}
                color={color}
                size={30}
                style={{
                  marginRight: -35
                }}
              />
            )
          }}
        />
      </Tab.Navigator>
    </>
  );
};

export default ProductsOverviewScreen;
于 2020-03-25T19:42:47.993 回答
3

你可以试试这个:

<Tab.Screen 
        name = "Button" 
        component={ScanStack} 
        options={{
        tabBarButton:()=>
        <View style={{position:'relative',bottom:35,alignItems:'center', justifyContent:'space-around',height:85}}>
          <Icon 
            name="barcode-scan"
            type = "material-community" 
            reverse
            color={'yellow'}
            reverseColor='black'
            containerStyle={{padding:0,margin:0,elevation:5}}
            onPress={()=>console.log('Hi')}
            size={30}/>
          <Text>Scan</Text>
        </View>
        }}/>

在组件中必须使用有效的反应组件,我尝试使用component={()=>null} 但控制台中出现警告

结果

于 2020-06-21T05:06:01.630 回答
0

我遇到了同样的问题,我需要向选项卡导航器添加一个与屏幕无关的自定义组件,但我尝试的一切都失败了。就我而言,我正在尝试使用 createMaterialTopTabNavigator。

React Navigation 5 的文档有点粗糙,没有很多 React Navigation 5 的示例,但经过多次尝试,我可以创建一个自定义组件并自己设置样式,这样我就可以混合路由创建的选项卡和自定义选项卡嵌入在选项卡导航器中的按钮。

import * as React from 'react';
import { View } from 'react-native'
import {
  NavigationHelpersContext,
  useNavigationBuilder,
  TabRouter,
  TabActions,
  createNavigatorFactory,
} from '@react-navigation/native';
import styled from 'styled-components'
import Colors from '../constants/Colors';

const customTabNavigator = ({ 
  initialRouteName, 
  children, 
  screenOptions, 
  tabContainerStyle, 
  contentStyle, 
  leftIcon,
  rightIcon 
}) => {
  const { state, navigation, descriptors } = useNavigationBuilder(TabRouter, {
    children,
    screenOptions,
    initialRouteName,
  });

  return (
    <NavigationHelpersContext.Provider value={navigation}>
      <OuterWrapper style={tabContainerStyle}>
        { leftIcon }
        <TabWrapper>
          {state.routes.map((route, i) => {
            return (
              <Tab
                key={route.key}
                onPress={() => {
                  const event = navigation.emit({
                    type: 'tabPress',
                    target: route.key,
                    canPreventDefault: true,
                  });

                  if (!event.defaultPrevented) {
                    navigation.dispatch({
                      ...TabActions.jumpTo(route.name),
                      target: state.key,
                    });
                  }
                }}
                style={descriptors[route.key].options.tabStyle}
              >
                { descriptors[route.key].options.label ?? <Label active={state.index === i}>{descriptors[route.key].options.title || route.name}</Label> }
              </Tab>
            )
          })}
        </TabWrapper>
        { rightIcon }
      </OuterWrapper>
      <View style={[{ flex: 1 }, contentStyle]}>
        {descriptors[state.routes[state.index].key].render()}
      </View>
    </NavigationHelpersContext.Provider>
  );
}

const OuterWrapper = styled.View`
  height: 55px;
  flex-direction: row;
  justify-content: space-between;
  background-color: ${Colors.grey1};
`
const TabWrapper = styled.View`
  flex: 1;
  flex-direction: row;
  justify-content: space-evenly;
`
const Tab = styled.TouchableOpacity`
  padding: 0 24px;
  justify-content: center;
  height: 100%;
`
const Label = styled.Text`
  font-family: Futura-Medium;
  font-size: 26px;
  color: ${({ active }) => active ? Colors.grey6 : Colors.grey3};
`

export default createNavigatorFactory(customTabNavigator)
import customTabNavigator from './customTabNavigator'
import * as React from 'react';
import { View, Image } from 'react-native'

import {
  ProjectsScreen,
  RenderScreen,
  EventsScreen,
  CameraScreen
} from '../screens';

import Colors from '../constants/Colors'

import logo from '../assets/images/icon.png'
import { Ionicons } from '@expo/vector-icons';
import { TouchableOpacity } from 'react-native-gesture-handler';

const TopTab = customTabNavigator();
const INITIAL_ROUTE_NAME = 'Home';

export default function MainNavigator({ navigation, route }) {

  navigation.setOptions({ headerTitle: getHeaderTitle(route) });
  return (
      <TopTab.Navigator
        initialRouteName={INITIAL_ROUTE_NAME}
        leftIcon={(
          <TouchableOpacity style={{ height: "100%", justifyContent: "center" }} onPress={() => alert("Whatever")}>
            <Image source={logo} style={{ resizeMode: "center", width: 70, height: 40 }} />
          </TouchableOpacity>
        )}
      >
        <TopTab.Screen
          name="Home"
          component={ProjectsScreen}
          options={{
            title: 'Proyectos',
          }}
        />
        <TopTab.Screen
          name="Preview"
          component={EventsScreen}
          options={{
            title: 'Eventos',
          }}
        />
        <TopTab.Screen
          name="Render"
          component={RenderScreen}
          options={{
            title: 'Mi cuenta',
          }}
        />
        <TopTab.Screen
          name="Camera"
          component={CameraScreen}
          options={{
            title: "Camera",
            label: (
              <View style={{ width: 36, height: 32, backgroundColor: Colors.grey3, borderRadius: 3, alignItems: "center", justifyContent: "center" }}>
                <Ionicons name="md-camera" style={{ color: Colors.grey5 }} size={25} />
              </View>
            ),
            tabStyle: { flexDirection: "row", alignItems: "center", justifyContent: "flex-end", flex: 1  }
          }}
        />
      </TopTab.Navigator>
  );
}

function getHeaderTitle(route) {
  const routeName = route.state?.routes[route.state.index]?.name ?? INITIAL_ROUTE_NAME;

  switch (routeName) {
    case 'Home':
      return 'Montar vídeo';
    case 'Preview':
      return 'Previsualizar vídeo';
    case 'Render':
      return 'Renderizar';
    case 'Gallery':
      return 'Galería'
    case 'Camera':
      return 'Camera'
  }
}

对于https://reactnavigation.org/docs/custom-navigators的示例,我添加了不同的样式和两个新的道具,leftIcon 和 rightIcon。该道具接收一个组件,用于将其渲染到选项卡包装器的相应侧。并且这个组件可以是 TouchableWhatevers,带有与屏幕无关的自定义 onPress:P

我希望它有帮助,我几乎把自己从窗户扔出去,直到我成功了,哈哈

于 2020-05-28T18:29:59.767 回答
0

第 1 步:- 在 bottomTabNav.js 到 component-prop 中给出一个不返回任何内容的屏幕

import React from 'react'
const AddMoreScreen = () => {
  return  null
}
export default AddMoreScreen
//I created AddMoreScreen.js component 

第 2 步:- 如第 1 步所述,我提供了一个不向组件道具呈现任何内容的屏幕,稍后在选项道具中,我将点击 tabBarButton 对象并返回一个自定义按钮给它

  ...
 <Tab.Screen
    name={"Add"}
    component={AddMoreScreen}
    options={{
      tabBarButton: ()=> <AddMoreModal />
    }}
  />
  ...

step-3:- 最后是我们的中心按钮代码,在我的情况下,如果我按下按钮,必须从底部出现一个模式,只需根据您的要求更改以下代码。AddMoreModal.js

export default function AddMoreModal() {
const [modalVisible, setModalVisible] = useState(false);

return (
<View style={{ marginTop: 15, marginLeft: 10, marginRight: 10, }}>
  <TouchableOpacity
    onPress={() => {
      setModalVisible(true);
    }}
  >
    <View style={styles.buttonStyle}>
      <Image
        source={icons.quickAddOutlined}
        style={styles.bottomTabImage}
      />
      <Text style={styles.bottomTabText}>Add</Text>
    </View>
  </TouchableOpacity>

  <View style={styles.container}>
    <Modal
      backdropOpacity={0.3}
      isVisible={modalVisible}
      onBackdropPress={() => setModalVisible(false)}
      style={styles.contentView}
    >
      <View style={styles.content}>
        <Text style={styles.contentTitle}>Hi !</Text>
        <Text>Welcome to CRAZY MIDDLE BUTTON! forgotten by react-native navigation</Text>
      </View>
    </Modal>
  </View>
</View>
);
}


const styles = StyleSheet.create({
content: {
backgroundColor: "white",
padding: 22,
justifyContent: "center",
alignItems: "center",
borderTopRightRadius: 17,
borderTopLeftRadius: 17,
},
contentTitle: {
fontSize: 20,
marginBottom: 12,
},
contentView: {
justifyContent: "flex-end",
margin: 0,
},
buttonStyle: {
marginBottom: 0,
alignItems:'center',
justifyContent:'center'
},
bottomTabImage: {
width: 25,
height: 25,
},
bottomTabText: {
marginTop: 4,
fontSize: FONTS.body3.fontSize,
fontFamily: FONTS.body3.fontFamily,
color: COLORS.fontGray90,
},
});

第4步:- 在此处输入图像描述

于 2021-09-20T07:47:54.447 回答