分析
页面包含TitleBar和TabNavigator组件,同时TabNavigator包含三个MessageScreen组件,而在MessageScreen中是FlatList组件,其中的Item是自定义的MessageItem组件。所以嵌套层次还是挺多的。而要实现动画中的效果,需要点击TitleBar中的编辑按钮,刷新MessageItem的状态。此处采用DeviceEventEmitter相对简单点。
当然,如果有更好的方法欢迎交流。
源码
MessageWrapperScreen.js
import React from 'react';
import {StyleSheet, Text, View, ToastAndroid, BackHandler, DeviceEventEmitter, PixelRatio} from 'react-native';
import {TabNavigator} from 'react-navigation';
import TitleBar from "../components/TitleBar";
import MessageScreen from "./MessageScreen";
import theme from "../style/theme";
import Constant from "../Constant";
import BottomButton from "../components/BottomButton";
const MessageNavigator = TabNavigator({
Tab1: {
screen: MessageScreen,
navigationOptions: ({navigation}) => ({
tabBarLabel: '全部消息'
})
},
Tab2: {
screen: MessageScreen,
navigationOptions: ({navigation}) => ({
tabBarLabel: '销售运营通知'
})
},
Tab3: {
screen: MessageScreen,
navigationOptions: ({navigation}) => ({
tabBarLabel: '小秘书通知'
})
}
}, {
lazy: true,
tabBarOptions: {
activeTintColor: theme.activeColor,
inactiveTintColor: theme.inactiveColor,
upperCaseLabel: false,
labelStyle: {
fontSize: 12,
marginTop: 0,
marginBottom: 0,
},
indicatorStyle: {
backgroundColor: theme.activeColor
},
style: {
backgroundColor: '#FFF',
height: 35,
},
},
});
/**
* 站内信Tab外层页面(包含TabNavigator)
*/
export default class MessageWrapperScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
isEditing: false,//是否正在编辑
}
}
render() {
const navigation = this.props.navigation;
return (
<View style={styles.container}>
<TitleBar
nav={navigation}
title="站内信"
rightText={this.state.isEditing ? "取消" : "编辑"}
handleRightClick={this.handleEdit}
handleBackClick={this.handleBackClick}
/>
<MessageNavigator navigation={navigation}/>
{this.state.isEditing
? (<View style={{flexDirection: 'row', height: 40}}>
<BottomButton
title="全部已读"
handleClick={this.handleAllRead}
/>
<View style={{width: 1/PixelRatio.get(), height: 40, backgroundColor: 'white'}}/>
<BottomButton
title="删除"
handleClick={this.handleDelete}
/>
</View>)
: null}
</View>
);
}
handleEdit = () => {
this._updateMessageList();
this.setState({
isEditing: !this.state.isEditing
});
};
handleBackClick = () => {
if (this.state.isEditing) {
this._updateMessageList();
this.setState({
isEditing: false,
});
} else {
this.props.navigation.goBack();
}
};
/**
* 发送
* @private
*/
_updateMessageList = () => {
DeviceEventEmitter.emit(Constant.EVENT_UPDATE_MESSAGE_LIST, !this.state.isEditing); //发监听
};
handleAllRead = () => {
ToastAndroid.show('handleAllRead', ToastAndroid.SHORT)
};
handleDelete = () => {
ToastAndroid.show('handleDelete', ToastAndroid.SHORT)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column'
},
button: {
color: 'white',
},
buttonWrapper: {
flex: 1,
paddingLeft: 10,
paddingRight: 10,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: theme.activeColor,
},
btnContainer: {
flex: 1,
}
});
//https://reactnavigation.org/docs/intro/nesting
MessageWrapperScreen.router = MessageNavigator.router;
扩展:【解决ReactNavigation中Navigator嵌套问题】
MessageScreen.js
import React from 'react';
import {
StyleSheet,
View,
FlatList,
ActivityIndicator,
ToastAndroid,
} from 'react-native';
import LoadingView from "../components/LoadingView";
import MessageItem from "../components/MessageItem";
import {requestMessageList} from "../request/Apis";
export default class MessageScreen extends React.Component {
constructor(props) {
super(props);
this.page = 1;
this.state = {
first: false,//首次加载,不渲染FlatList
refreshing: false,
loadMore: false,
dataList: [],
isEditing: false,
}
}
componentDidMount() {
// this.requestData(1)
}
_onPressItem = (msgId) => {
const {navigate} = this.props.navigation;
console.log(this.props.navigation);
navigate('MessageDetails', {'msgId': msgId})
};
_renderItem = (item, index) => (
<MessageItem
editable = {this.state.isEditing}
data={item}
onPressItem={this._onPressItem}
/>
);
_dividerLine = () => (<View style={{height: 2}}/>);
_emptyComponent = () => (<LoadingView/>);
_onRefresh = () => {
this.setState({
refreshing: true
});
this.requestData(1)
};
_onEndReached = () => {
if (!this.state.refreshing && !this.state.loadMore) {
this.setState({
loadMore: true
});
this.requestData(++this.page);
}
};
_onFooterComponent = () => {
return this.state.loadMore ? (
<View style={styles.footerStyle}>
<ActivityIndicator
style={styles.indicatorStyle}
size="large"
/>
</View>
) : null
};
requestData = () => {
requestMessageList().then((result) => {
this.setState({
first: false,
loadMore: false,
refreshing: false,
dataList: this.page === 1 ? result.data : this.state.dataList.concat(result.data),
})
}, (error) => {
this.setState({
first: false,
loadMore: false,
refreshing: false,
});
ToastAndroid.show("error", ToastAndroid.SHORT);
});
};
render() {
let content;
if (this.state.first) {
content = (<LoadingView/>);
} else {
content = (<FlatList
initialNumToRender={10}
data={this.state.dataList}
keyExtractor={(item, index) => item.relationId}
renderItem={this._renderItem}
onRefresh={this._onRefresh}
refreshing={this.state.refreshing}
ItemSeparatorComponent={this._dividerLine}
onEndReachedThreshold={0.1}
onEndReached={this._onEndReached}
ListFooterComponent={this._onFooterComponent}
/>);
}
return (
<View style={{flex: 1}}>
{content}
</View>
);
}
// render(){
// return(<View/>)
// }
}
const styles = StyleSheet.create({
footerStyle: {
height: 50,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#DDD'
},
indicatorStyle: {
height: 40,
alignItems: 'center',
justifyContent: 'center'
}
});
MessageItem.js
import React from 'react';
import {StyleSheet, View, Text, TouchableNativeFeedback,
Image, Dimensions,DeviceEventEmitter, ToastAndroid} from 'react-native';
import moment from 'moment';
import CheckBox from './CheckBox';
import Constant from "../Constant";
export default class MessageItem extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: false,
isEditing: false,
}
}
_onPress = () => {
const item = this.props.data.item;
this.props.onPressItem(item.relationId);
};
componentWillMount() {
this.listener = DeviceEventEmitter.addListener(Constant.EVENT_UPDATE_MESSAGE_LIST, (e) => {
this.setState({
isEditing: e
});
});
}
onChange = (checked) => {
this.setState({
checked: !this.state.checked,
});
};
componentWillUnmount() {
this.listener.remove();
}
render() {
const item = this.props.data.item;
const time = moment(item.gmtCreate).format('YYYY-MM-DD');
return (
<TouchableNativeFeedback
onPress={this._onPress}
background={TouchableNativeFeedback.Ripple('#CCC', false)}>
<View style={styles.itemContainer}>
{this.state.isEditing ?
(<View style={{marginRight: 5}}>
<CheckBox
label=''
checked={this.state.checked}
onChange={this.onChange}
/>
</View>)
: null}
<View style={{flex: 1}}>
<View style={styles.itemTitleContainer}>
<View style={{flexDirection: 'row', alignItems: 'center', flexShrink: 1,}}>
{item.isRead == 0 ? <View style={styles.dot}/> : null}
<Text style={styles.titleStyle} numberOfLines={1}>{item.title}</Text>
</View>
<Text style={[styles.contentStyle, {flexShrink: 0}]}>{time}</Text>
</View>
<Text style={[styles.contentStyle, {marginTop: 6}]} numberOfLines={2}>{item.summarize}</Text>
</View>
</View>
</TouchableNativeFeedback>
);
}
}
const styles = StyleSheet.create({
itemContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FFF',
padding: 8,
},
itemTitleContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
},
titleStyle: {
fontSize: 15,
color: '#222',
},
contentStyle: {
fontSize: 13,
color: '#BBB',
},
dot: {
width: 6,
height: 6,
borderRadius: 3,
backgroundColor: '#FF0000',
marginRight: 3
},
});
核心代码
在MessageItem中添加时间监听,并在组件卸载后移除监听
componentWillMount() {
this.listener = DeviceEventEmitter.addListener(
Constant.EVENT_UPDATE_MESSAGE_LIST, (e) => {
this.setState({
isEditing: e
});
});
}
componentWillUnmount() {
this.listener.remove();
}
点击编辑按钮,触发事件
DeviceEventEmitter.emit(
Constant.EVENT_UPDATE_MESSAGE_LIST, !this.state.isEditing); //发监听