React Native:Toggle抽屉&路线中的导航栏可见性

时间:2016-02-22 07:51:52

标签: javascript reactjs react-native navigator

我的应用需要Drawer& Navigator。为实现这一目标,我在index.ios.js中将Drawer组件作为父组件封装了其他所有组件。

现在我试图解决的用例是,我在一些路线中禁用抽屉或隐藏NavigationBar。为此,我要更新父组件的状态&在每个导航上重新渲染。但是,这似乎没有帮助。

index.io.js:

class MyApp extends Component{

  state = {drawerEnabled:true, navigationBarEnabled: true};

  constructor(){
    super();
    this.navigate = this.navigate.bind(this);
    this.navigateForDrawer = this.navigateForDrawer.bind(this);
    this.getNavigator = this.getNavigator.bind(this);
    this.renderScene = this.renderScene.bind(this);
  }

  navigateForDrawer(route) {
      this.refs.navigator.resetTo(route);
      this.refs.drawer.close();
  }

  getNavigationBar(){
    return (
      this.state.navigationBarEnabled ?
      <Navigator.NavigationBar
      style={styles.navBar}
      transitionStyles={Navigator.NavigationBar.TransitionStylesIOS}
      routeMapper={NavigationBarRouteMapper}
      /> : null);
  }

  getNavigator(){
    return (
      <Navigator
        ref="navigator"
        style={styles.navigator}
        initialRoute={{id:'Login'}}
        renderScene={this.renderScene}
        navigationBar={this.getNavigationBar()}
      />
    );
  }

  navigate(route, method){
    if(route)
    switch (route.id) {
        case 'Login':
        this.state = {drawerEnabled: false, navigationBarEnabled: false};
        break;

        case 'NewBooking':
        this.state = {drawerEnabled: true, navigationBarEnabled: true};
        break;

        case 'MyBookings':
        this.state = {drawerEnabled: true, navigationBarEnabled: true};
        break;

        case 'AboutUs':
        this.state = {drawerEnabled: true, navigationBarEnabled: true};
        break;

        case 'ConfirmBooking':
        this.state = {drawerEnabled: false, navigationBarEnabled: true};
        break;

        case 'BookingComplete':
        this.state = {drawerEnabled: false, navigationBarEnabled: true};
        break;
    }

      this.forceUpdate();
      this.refs.navigator[method](route);
  }

  renderScene(route, navigator){
    navigator.navigate = this.navigate;
    switch (route.id) {
        case 'Login':
        return <Login navigate={this.navigate}/>;

        case 'NewBooking':
        route.title = 'New Booking';
        return <NewBooking navigate={this.navigate}/>;

        case 'MyBookings':
        route.title = 'My Bookings';
        return <MyBookings navigate={this.navigate}/>;

        case 'AboutUs':
        route.title = 'About Us';
        return <AboutUs navigate={this.navigate}/>;

        case 'ConfirmBooking':
        route.title = 'Booking Summary';
        return <ConfirmBooking navigate={this.navigate} booking={route.booking}/>;

        case 'BookingComplete':
        route.title = 'Booking Complete';
        return <BookingComplete navigate={this.navigate} booking={route.booking}/>;

      default:
    }
  }

  render() {
    return (
      <Drawer
        ref="drawer"
        disabled={!this.state.drawerEnabled}
        content={<ControlPanel navigate={this.navigateForDrawer}/>}
        tapToClose={true}
        openDrawerOffset={0.2} // 20% gap on the right side of drawer
        panCloseMask={0.2}
        closedDrawerOffset={-3}
        styles={{
          main: {paddingLeft: 3}
        }}
        tweenHandler={(ratio) => ({
          main: { opacity:(2-ratio)/2 }
        })}
        >
        {this.getNavigator()}
      </Drawer>
    )
  }

}

导航到新路线:

this.props.navigate({id: 'ConfirmBooking', booking: booking}, 'push');

PS:我使用的抽屉是react-native-drawer

2 个答案:

答案 0 :(得分:3)

我认为你的意思是DrawerLayoutAndroid(or DrawerLayout)作为抽屉组件。

您可以在代码中使用以下行以编程方式在路线更改时打开或关闭抽屉:

this.refs.drawer.openDrawer(); // opens the drawer
this.refs.drawer.closeDrawer(); // closes the drawer

我会在导航功能中包含这些调用,这似乎是最好的地方。

答案 1 :(得分:1)

我必须使用某些页面上出现的导航栏执行相同操作,并执行以下操作:

首先,我编写了自己的NavBar,它只是一个flex:1的屏幕,并且有两个内部组件。第一个是导航栏,其中包含flex:1并包含三个内部组件,左键,标题和右键分别带有flex:1, flex:2, flex:1。这样我就可以在每个屏幕上都有一个自定义导航栏。

以下是代码:

React.createClass({

   propTypes: {

   /* Specifies the background color of the navigation bar*/
   backgroundColor: React.PropTypes.string,

   /* The view to be displayed */
   view: React.PropTypes.element,

   /* A component representing the left button */
   leftButton: React.PropTypes.element,

   /* Title component that shows up in the middle of the screen */
   title: React.PropTypes.element,

   /* Right button component */
   rightButton: React.PropTypes.element,

   /* including a modal ensures that the modal is centered in the screen */
   modal: React.PropTypes.element,

   /* Callback that is called from the Navigator bar screen. Any arguments
    * passed, represent the positioning and dimensions of the Navigation Bar
    */
   onLayout: React.PropTypes.func,

   /*The separator that shows up between the Navigation Bar and the view*/
   separator: React.PropTypes.element,
   },

   render(){
     return (
       <View style={styles.modalWrapper}>
         <View
           style={styles.container}>
              <View
             style={[styles.navBar, {backgroundColor: this.props.backgroundColor}]}
             onLayout={(e) =>{
             this.props.onLayout && this.props.onLayout(e)
             }}>
          <View
            style={styles.leftButton}>
            {this.props.leftButton}
          </View>
          <View
            style={styles.title}>
            {this.props.title }
          </View>
          <View
            style={styles.rightButton}>
            {this.props.rightButton}
          </View>
        </View>

        <View style={styles.viewWrapper}>
          {this.props.separator }
          {this.props.view}
        </View>
    </View>
    {this.props.modal}
  </View>);
  },
});

风格:

StyleSheet.create({
  modalWrapper: {
    width: device.width,
    height: device.height
  },
  container: {
    flex: 1,
    flexDirection: 'column',
  },
  navBar: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center'
  },
  viewWrapper: {
    flex: 8
  },
  rightButton: {
    top: STATUS_BAR_OFFSET,
    flex: 1,
    alignItems: 'flex-end',
    justifyContent: 'center',
    paddingRight: PADDING,
  },
  leftButton: {
    top: STATUS_BAR_OFFSET,
    flex: 1,
    alignItems: 'center',
    justifyContent: 'flex-start',
    paddingLeft: PADDING,
  },
  title: {
    top: STATUS_BAR_OFFSET,
    flex: 2,
    alignItems: 'center',
    justifyContent: 'center',
    paddingRight: PADDING / 2,
    paddingLeft: PADDING / 2,
  }
});

onStartShouldSetResponderCapture

接下来,虽然我还没有改变尝试这个,但我建议使用usint onStartShouldSetResponderCapture。如果您尝试运行以下示例,您将看到嵌套视图捕获触摸事件的顺序。如果没有孩子响应,则在最低子视图下,本机onStartShouldSetResponder开始响应,并且触摸事件会冒泡到顶部。但是,在最外层视图中首先调用onStartShouldSetResponderCapture

React.createClass({
  render() {
    return (
      <View onStartShouldSetResponderCapture={() => this.text('1')}
            onStartShouldSetResponder={() => this.text('4')}
            style={{flex:1}}>
        <View onStartShouldSetResponderCapture={() => this.text('2')}
              onStartShouldSetResponder={() => this.text('3')}
              style={{flex:1}}>
        </View>
      </View>
    );


  },

  text(text) {
    console.log(text);
    //return true; // comment this to see the order or stop at '1'
  }
});

所以在你的情况下我会做以下事情:

render() {
        return (
          <View onStartShouldSetResponderCapture={() => !this.state.drawerEnabled}>
            <Drawer
              ref="drawer"
              disabled={!this.state.drawerEnabled}
              content={<ControlPanel navigate={this.navigateForDrawer}/>}
              tapToClose={true}
              openDrawerOffset={0.2} // 20% gap on the right side of drawer
              panCloseMask={0.2}
              closedDrawerOffset={-3}
              styles={{
          main: {paddingLeft: 3}
        }}
              tweenHandler={(ratio) => ({
          main: { opacity:(2-ratio)/2 }
        })}
            >
              {this.getNavigator()}
            </Drawer>
          </View>
        );
      }

的setState

最后,我不会致电forceUpdate()而是致电this.setState({drawerEnabled: true})。另外,不要忘记在构造函数中绑定。