最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React Native: Toggle drawer & navigation bar visibility in routes - Stack Overflow

programmeradmin2浏览0评论

I've an app that needs Drawer & Navigator. To achieve this, I've a Drawer ponent as parent in index.ios.js which encapsulates everything else.

Now the use case that I'm trying to solve is, I've to disable Drawer or hide NavigationBar in some routes. To achieve that, I'm updating parent ponent's state & re-rendering on every navigation. However, that doesn't seem to help.

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>
    )
  }

}

To Navigate to a new route :

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

PS: The drawer that I'm using is react-native-drawer

I've an app that needs Drawer & Navigator. To achieve this, I've a Drawer ponent as parent in index.ios.js which encapsulates everything else.

Now the use case that I'm trying to solve is, I've to disable Drawer or hide NavigationBar in some routes. To achieve that, I'm updating parent ponent's state & re-rendering on every navigation. However, that doesn't seem to help.

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>
    )
  }

}

To Navigate to a new route :

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

PS: The drawer that I'm using is react-native-drawer

Share Improve this question edited Feb 22, 2016 at 8:50 Gaurav Arora asked Feb 22, 2016 at 7:51 Gaurav AroraGaurav Arora 17.3k5 gold badges35 silver badges45 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

I assume that you mean DrawerLayoutAndroid (or DrawerLayout) as drawer ponent.

You could simply use the following line in your code to programmatically open or close the drawer on route change:

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

I would include these calls in your navigate function, this seems to be the best place.

I had to do the same thing with a navigator bar appearing on certain pages and I did the following:

First I wrote my own NavBar which is simply a screen with flex:1 and has two inner ponents. The first is the Navigation bar which has flex:1 and contains three inner ponents, left button, title and right button with flex:1, flex:2, flex:1 respectively. This way I can have a custom navigation bar on each screen.

Here's the code:

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 ponent representing the left button */
   leftButton: React.PropTypes.element,

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

   /* Right button ponent */
   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>);
  },
});

And the styles:

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

Next, while I haven't had a change to try this yet, I would suggest usint onStartShouldSetResponderCapture. If you try and run the following example you'll see the order that nested views capture touch events. Under react native onStartShouldSetResponder starts at the lowest child view and the the touch events bubble up to the top if no child responds. However, onStartShouldSetResponderCapture is called first on the outer most view.

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; // ment this to see the order or stop at '1'
  }
});

so in your case I would do the following:

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

Finally, rather than calling forceUpdate() I would call this.setState({drawerEnabled: true}). Also, don't forget to bind in your constructor.

发布评论

评论列表(0)

  1. 暂无评论