【问题标题】:React Native two drawers on one screen在一个屏幕上 React Native 两个抽屉
【发布时间】:2018-12-25 17:46:12
【问题描述】:

我有一个应用程序需要能够使用两个抽屉导航器,一个在标题的左侧,一个在右侧。

我现在可以通过滑动手势打开两个抽屉,但是我需要能够以编程方式打开它。我发现navigation.openDrawer() 函数只适用于其中一个抽屉,而不适用于另一个,因为它只能使用我的抽屉导航器中的一个导航道具(以先到者为准)。

以下是我的渲染函数:

const LeftStack = createStackNavigator(
  {
    LeftDrawerStack
  },
  {
    navigationOptions: ({navigation}) => ({
      headerLeft: (
        <TouchableOpacity onPress={() => navigation.openDrawer()}>
          <Icon style={{marginLeft: 10}} name='menu'/>
        </TouchableOpacity>
      )
    })
  }
);

const RightStack = createStackNavigator(
  {
    RightDrawerStack
  },
  {
    navigationOptions: ({navigation}) => ({
      headerRight: (
        <TouchableOpacity onPress={() => navigation.openDrawer()}>
          <Icon style={{marginRight: 10}} name='ios-bulb'/>
        </TouchableOpacity>
      )
    })
  }
);

export const RouteStack = createStackNavigator(
  {
    screen: LoginScreen,
    navigationOptions: ({navigation}) => ({
      header: null
    }),
    LeftStack,
    RightStack
  }
);

这是我的抽屉路线:

export const LeftDrawerStack = createDrawerNavigator(
  {
    Dashboard: {
      screen: DashboardScreen
    },
    History: {
      screen: HistoryScreen
    },
    Privacy: {
      screen: PrivacyPolicyScreen
    },
    TermsAndConditions: {
      screen: TermsAndConditionsScreen
    }
  }, {
    initialRouteName: 'Dashboard',
    contentComponent: LeftDrawerScreen
  }
);

export const RightDrawerStack = createDrawerNavigator(
  {
    LeftDrawerStack,
    Settings: {
      screen: SettingsScreen
    }
  }, {
    drawerPosition: 'right',
    contentComponent: RightDrawerScreen
  }
);

这是我目前的导航图,但是两个汉堡菜单都在右侧打开同一个菜单,而不是在各自的两侧打开一个菜单。

我可能遗漏了一些部分,但如果我忘记了,我一定会发布更多信息!

【问题讨论】:

  • 在用户体验 (UX) 级别,为什么需要打开两个抽屉?它会占用整个屏幕,为什么不使用一个占用整个屏幕的抽屉,您可以将该抽屉分成两个以显示您的选项,但我仍然没有您想要实现的图片
  • 这是在 v5 中的操作方法:stackoverflow.com/questions/61742796/…

标签: javascript android ios react-native react-navigation


【解决方案1】:

我能够通过以下设置做到这一点(尝试将您的结构更改为这样):

const LeftDrawer = createDrawerNavigator(
{
    LeftDrawer: MyStackNavigator,
},
    {
        getCustomActionCreators: (route, stateKey) => { return { toggleLeftDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }) }; },
        drawerPosition: 'left',
        contentComponent: MyLeftDrawer
    }
);

const RightDrawer = createDrawerNavigator(
{
    Drawer: LeftDrawer,
},
    {
        getCustomActionCreators: (route, stateKey) => { return { toggleRightDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }) }; },
        drawerPosition: 'right',
        contentComponent: MyRightDrawer
    }
);

export const RootNavigator = createStackNavigator(
{
    Login: Login,
    Home: RightDrawer
},
    {
        initialRouteName: 'Login',
        navigationOptions: { header: null, gesturesEnabled: false }
    }
);

关键是getCustomActionCreators。它允许您从MyStackNavigator 中的任何屏幕调用该函数,如下所示:this.props.navigation.toggleLeftDrawer();。

【讨论】:

  • getCustomActionCreators 在版本 4.0.10 中不可用。有没有其他方法或等效方法来解决这个问题?
  • @WaweruwaKamau 好问题。恐怕我仍在使用 3.x,我不确定是否有新的替代方案,如果我发现我会在这里更新
  • @WaweruwaKamau 看看下面 Emerson Nascimento 的回答是否对您有帮助,或者使用“drawerToggleRoute”,如图所示 [这里] (github.com/react-navigation/react-navigation/issues/…) 和 [这里] (stackoverflow.com/a/57647292/9173778)。跨度>
【解决方案2】:

这是我的解决方案

第 1 步:创建两个抽屉式导航并将它们嵌套在一起

第 2 步:将第一个抽屉导航存储在其他位置(单例)

// Drawer 1
import ContentLeftMenu from '@components/ContentLeftMenu';
import { createDrawerNavigator } from '@react-navigation/drawer';
import * as React from 'react';
import DrawerCart from './DrawerCart';
import { setNavigationDrawerHome } from './RootNavigation';
import { isTablet } from 'react-native-device-info';
import MainStack from './MainStack';

const Drawer = createDrawerNavigator();

export default function DrawerHome() {
  return (
    <Drawer.Navigator
      initialRouteName="DrawerCart"
      drawerContent={() => <ContentLeftMenu />}
      screenOptions={({ navigation, route }) => {
        setNavigationDrawerHome(navigation)
      }}
    >
      <Drawer.Screen name="DrawerCart" component={isTablet ? DrawerCart : MainStack} />
    </Drawer.Navigator>
  );
}
// Drawer 2
import CartScreen from '@features/cart/CartScreen';
import { createDrawerNavigator } from '@react-navigation/drawer';
import * as React from 'react';
import MainStack from './MainStack';

const Drawer = createDrawerNavigator();

export default function DrawerCart(props) {
  return (
    <Drawer.Navigator
      initialRouteName="MainStackCart"
      drawerContent={() => <CartScreen />}
      drawerPosition='right'
    >
      <Drawer.Screen name="MainStackCart" component={MainStack} />
    </Drawer.Navigator>
  );
}
// set navigation of drawer 1 with navigationDrawerHome
let navigationDrawerHome = null

export const setNavigationDrawerHome = (navigation) => {
  navigationDrawerHome = navigation
}

export const getNavigationDrawerHome = () => {
  return navigationDrawerHome
}

当我在 Mainstack 中使用时,我在左侧打开抽屉 1 时使用 navigationDrawerHome,而抽屉 2 只使用导航道具

const Stack = createStackNavigator();

function MainStack() {
  const navigationDrawerHome = getNavigationDrawerHome()
  return (
    <Stack.Navigator
      screenOptions={({ navigation, route }) => ({
        headerLeft: () => <HeaderLeft />,
        headerTitleAlign: 'center',
      })}>
      <Stack.Screen
        name="home_user_tab"
        component={HomeUserTabs}
        options={({ navigation, route }) => ({
          headerLeft: () => {
            return (
              <ButtonIcon
                onPress={() => navigationDrawerHome.openDrawer()}
                style={styles.buttonLeft}
                icon={images.ic_left_menu}
              />
            )
          },
          headerTitle: () => <LogoHeader />,
          headerRight: () => (<HeaderCardSearch
            navigation={navigation}
          />),
          // header: null
        })}
      />
    </Stack.Navigator>
  );
}
const HeaderCardSearch = (props) => {
  const { navigation } = props;
  return (
    <View style={styles.headerRight}>
      <ButtonIcon
        onPress={() => navigation.openDrawer()}
        style={styles.buttonCart}
        icon={images.ic_cart}
      />
      <ButtonIcon
        onPress={() => navigate('search')}
        style={styles.buttonSearch}
        icon={images.ic_search}
      />
    </View>
  )
};

【讨论】:

  • hey Anh 恐怕您的解决方案包含了您自己的实现细节,我无法提取您正在使用的机制。您指的是抽屉 1/2,但它们没有显示这些名称。真正的症结——在 HeaderCardSearch 中有一个对第二个 ButtonIcon 的“导航”的引用,它是什么/它来自哪里?如果您可以从此模式中编辑出您的实现,那就太好了,因为它看起来真的很有用,但我就是无法挑选出这种方法。谢谢
【解决方案3】:

我设法以这种方式以编程方式打开两个抽屉:

// Opens left drawer
this.props.navigation.openDrawer();

// Opens right drawer
this.props.navigation.dangerouslyGetParent().dangerouslyGetParent().openDrawer();

我的导航容器看起来像这样:

static container = (): NavigationContainer => createDrawerNavigator({
    right: createDrawerNavigator({
        left: DeviceControlContainer
    }, {
        contentComponent: HistoryDrawerContainer,
        overlayColor: drawerOverlayColor()
    })
}, {
    drawerPosition: 'right',
    overlayColor: drawerOverlayColor(),
    navigationOptions: DeviceControlScreen.navigationOptions,
    contentComponent: DeviceControlDrawer
});

【讨论】:

  • 谢谢,它有效!我很确定这不是一个好方法,但我也确信,经过一些研究,react-navigation v4 无法管理该用例,也就是打开一个特定的抽屉,所以......跨度>
【解决方案4】:

从 Anh Devit 解决方案中获得灵感,这是我的。我在没有 Stack Navigator 的情况下在整个应用程序中使用标题栏,所以不得不走一条稍微不同的路线。

对于我的RootNavigator,我将标题放在我的第一个抽屉上方。我使用useState,所以当我将导航对象作为道具传递时,AppHeader 可以在抽屉初始化后正确重新渲染。

import React, { useState } from "react";
import { NavigationContainer } from "@react-navigation/native";

import LeftDrawer from "./LeftDrawer";
import AppHeader from "./AppHeader";

const RootNavigator = () => {
  const [navigationLeftDrawer, setNavigationLeftDrawer] = useState(null);
  const [navigationRightDrawer, setNavigationRightDrawer] = useState(null);

  return (
    <NavigationContainer>
      <AppHeader
        navigationLeftDrawer={navigationLeftDrawer}
        navigationRightDrawer={navigationRightDrawer}
      />
      <LeftDrawer
        navigationLeftDrawer={(navigation) =>
          setNavigationLeftDrawer(navigation)
        }
        navigationRightDrawer={(navigation) =>
          setNavigationRightDrawer(navigation)
        }
      />
    </NavigationContainer>
  );
};

export default RootNavigator;

我在这里创建我的LeftDrawer,RightDrawer 将嵌套在其中。注意在抽屉初始化时设置navigationLeftDrawer。此外,navigationRightDrawer必须作为参数而不是道具传递。

import React, { useContext } from "react";
import {
  createDrawerNavigator,
  DrawerContentScrollView,
  DrawerItem,
} from "@react-navigation/drawer";

import RightDrawer from "./RightDrawer";

const LeftDrawer = ({ navigationLeftDrawer, navigationRightDrawer }) => {
  const Drawer = createDrawerNavigator();

  return (
    <Drawer.Navigator
      screenOptions={({ navigation }) => {
        navigationLeftDrawer(navigation);
      }}
      drawerContent={(props) => {
        const { navigate } = props.navigation;
        return (<RightDrawerItems />)
        );
      }}
    >
      <Drawer.Screen
        name="AccountDrawer"
        component={RightDrawer}
        initialParams={{ navigationRightDrawer }}
      />
    </Drawer.Navigator>
  );

export default LeftDrawer;

同样在RightDrawer 中,将navigationRightDrawer 传递给正确的导航对象。

import React from "react";

const RightDrawer = ({ route }) => {
  const Drawer = createDrawerNavigator();
  const { navigationRightDrawer } = route.params;

  return (
    <Drawer.Navigator
      drawerPosition="right"
      screenOptions={({ navigation }) => {
        navigationRightDrawer(navigation);
      }}
      drawerContent={(props) => {
        const { navigate } = props.navigation;
        return ( <RightDrawerItems />);
      }}
    >
      {userAuth ? (
        <>
          <Drawer.Screen name={routes.dashboard} component={DashboardScreen} />
        </>
      ) : (
        <Drawer.Screen name={routes.home} component={HomeStack} />
      )}
      <Drawer.Screen name={routes.about} component={AboutScreen} />
      <Drawer.Screen name={routes.language} component={LanguageScreen} />
    </Drawer.Navigator>
  );
};

export default RightDrawer;

最后,AppHeader:

import React, { useContext } from "react";

const AppHeader = ({ navigationRightDrawer, navigationLeftDrawer }) => {

  if (navigationRightDrawer == null || navigationLeftDrawer == null)
    return null;

  return (
    <View style={styles.container}>
      <Icon
        name="menu"
        color={colors.white}
        onPress={() => {
          navigationRightDrawer.closeDrawer();
          navigationLeftDrawer.toggleDrawer();
        }}
      />
      {userAuth ? (
        <TouchableWithoutFeedback
          onPress={() => {
            navigationLeftDrawer.closeDrawer();
            navigationRightDrawer.toggleDrawer();
          }}
        >
          <Image
            style={styles.image}
            source={{ uri: userProfile.profilePicture }}
          />
        </TouchableWithoutFeedback>
      ) : (
        <Icon
          name="person"
          color={colors.secondary}
          onPress={() => navigationLeftDrawer.navigate(routes.login)}
        />
      )}
    </View>
  );
};

export default AppHeader;

【讨论】:

  • 嘿 Hakim 在未能遵循 Anh 的解决方案后,我尝试研究您修改后的版本,不幸的是,我和他的一样困惑!您的 LeftDrawer 组件输出 - 这似乎是一个错误 - 您的 RightDrawer 组件也是如此。任何一个组件都会导入这些项目。您还可以解构道具以在每个组件中提取一次“导航”,但永远不要使用它。我希望您的模式有效,但看起来您已经编辑了代码以删除自己的实现细节,但留下了部分代码,我担心它不完整
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-01
  • 2021-06-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多