React Native: переключение видимости ящика и панели навигации на маршрутах
У меня есть приложение, которое нужно Drawer
& Navigator
, Для этого у меня есть родительский компонент Drawer в 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: Ящик, который я использую, является реагирующим
2 ответа
Я предполагаю, что вы имеете в виду DrawerLayoutAndroid ( или DrawerLayout) в качестве компонента ящика.
Вы можете просто использовать следующую строку в своем коде, чтобы программно открыть или закрыть ящик при изменении маршрута:
this.refs.drawer.openDrawer(); // opens the drawer
this.refs.drawer.closeDrawer(); // closes the drawer
Я бы включил эти вызовы в вашу функцию навигации, это, кажется, лучшее место.
Я должен был сделать то же самое с панелью навигации, появляющейся на определенных страницах, и я сделал следующее:
Сначала я написал свой собственный 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})
, Кроме того, не забудьте связать в своем конструкторе.