React Native 好用的时间线 组件
效果如下:
实现方法:
一、组件封装
CustomTimeLine.js
"use strict"; import React, {Component} from "react";
import {
StyleSheet,
ListView,
Image,
View,
Text,
TouchableOpacity
} from "react-native"; const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2
}); const defaultCircleSize = ;
const defaultCircleColor = "#007AFF";
const defaultLineWidth = ;
const defaultLineColor = "#007AFF";
const defaultTimeTextColor = "black";
const defaultDotColor = "white";
const defaultInnerCircle = "none"; export default class CustomTimeLine extends Component {
static defaultProps = {
circleSize: defaultCircleSize,
circleColor: defaultCircleColor,
lineWidth: defaultLineWidth,
lineColor: defaultLineColor,
innerCircle: defaultInnerCircle,
columnFormat: "single-column-left",
separator: false,
showTime: true
}; constructor(props, context) {
super(props, context); this._renderRow = this._renderRow.bind(this);
this.renderTime = (this.props.renderTime
? this.props.renderTime
: this._renderTime
).bind(this);
this.renderDetail = (this.props.renderDetail
? this.props.renderDetail
: this._renderDetail
).bind(this);
this.renderCircle = (this.props.renderCircle
? this.props.renderCircle
: this._renderCircle
).bind(this);
this.renderEvent = this._renderEvent.bind(this); this.state = {
data: this.props.data,
dataSource: ds.cloneWithRows(this.props.data),
x: ,
width:
};
} componentWillReceiveProps(nextProps) {
this.setState({
data: nextProps.data,
dataSource: ds.cloneWithRows(nextProps.data)
});
} render() {
return (
<View style={[styles.container, this.props.style]}>
<ListView
ref="listView"
style={[styles.listview, this.props.listViewStyle]}
dataSource={this.state.dataSource}
renderRow={this._renderRow}
automaticallyAdjustContentInsets={false}
{...this.props.options}
/>
</View>
);
} _renderRow(rowData, sectionID, rowID) {
let content = null; switch (this.props.columnFormat) {
case "single-column-left":
content = (
<View style={[styles.rowContainer, this.props.rowContainerStyle]}>
{this.renderTime(rowData, sectionID, rowID)}
{this.renderEvent(rowData, sectionID, rowID)}
{this.renderCircle(rowData, sectionID, rowID)}
</View>
);
break;
case "single-column-right":
content = (
<View style={[styles.rowContainer, this.props.rowContainerStyle]}>
{this.renderEvent(rowData, sectionID, rowID)}
{this.renderTime(rowData, sectionID, rowID)}
{this.renderCircle(rowData, sectionID, rowID)}
</View>
);
break;
case "two-column":
content =
rowID % === ? (
<View style={[styles.rowContainer, this.props.rowContainerStyle]}>
{this.renderTime(rowData, sectionID, rowID)}
{this.renderEvent(rowData, sectionID, rowID)}
{this.renderCircle(rowData, sectionID, rowID)}
</View>
) : (
<View style={[styles.rowContainer, this.props.rowContainerStyle]}>
{this.renderEvent(rowData, sectionID, rowID)}
{this.renderTime(rowData, sectionID, rowID)}
{this.renderCircle(rowData, sectionID, rowID)}
</View>
);
break;
}
return <View key={rowID}>{content}</View>;
} _renderTime(rowData, sectionID, rowID) {
if (!this.props.showTime) {
return null;
}
var timeWrapper = null;
switch (this.props.columnFormat) {
case "single-column-left":
timeWrapper = {
alignItems: "flex-end"
};
break;
case "single-column-right":
timeWrapper = {
alignItems: "flex-start"
};
break;
case "two-column":
timeWrapper = {
flex: ,
alignItems: rowID % === ? "flex-end" : "flex-start"
};
break;
}
return (
<View style={timeWrapper}>
<View style={[styles.timeContainer, this.props.timeContainerStyle]}>
<Text style={[styles.time, this.props.timeStyle]}>
{rowData.time}
</Text>
</View>
</View>
);
} _renderEvent(rowData, sectionID, rowID) {
const lineWidth = rowData.lineWidth
? rowData.lineWidth
: this.props.lineWidth;
const isLast = this.props.renderFullLine
? !this.props.renderFullLine
: this.state.data.slice(-)[] === rowData;
const lineColor = isLast
? "rgba(0,0,0,0)"
: rowData.lineColor ? rowData.lineColor : this.props.lineColor;
let opStyle = null; switch (this.props.columnFormat) {
case "single-column-left":
opStyle = {
borderColor: lineColor,
borderLeftWidth: lineWidth,
borderRightWidth: ,
marginLeft: ,
paddingLeft:
};
break;
case "single-column-right":
opStyle = {
borderColor: lineColor,
borderLeftWidth: ,
borderRightWidth: lineWidth,
marginRight: ,
paddingRight:
};
break;
case "two-column":
opStyle =
rowID % ==
? {
borderColor: lineColor,
borderLeftWidth: lineWidth,
borderRightWidth: ,
marginLeft: ,
paddingLeft:
}
: {
borderColor: lineColor,
borderLeftWidth: ,
borderRightWidth: lineWidth,
marginRight: ,
paddingRight:
};
break;
} return (
<View
style={[styles.details, opStyle]}
onLayout={evt => {
if (!this.state.x && !this.state.width) {
const {x, width} = evt.nativeEvent.layout;
this.setState({x, width});
}
}}
>
<TouchableOpacity
disabled={this.props.onEventPress == null}
style={[this.props.detailContainerStyle]}
onPress={() =>
this.props.onEventPress ? this.props.onEventPress(rowData) : null
}
>
<View style={styles.detail}>
{this.renderDetail(rowData, sectionID, rowID)}
</View>
{this._renderSeparator()}
</TouchableOpacity>
</View>
);
} _renderDetail(rowData, sectionID, rowID) {
let title = rowData.description ? (
<View>
<Text style={[styles.title, this.props.titleStyle]}>
{rowData.title}
</Text>
<Text style={[styles.description, this.props.descriptionStyle]}>
{rowData.description}
</Text>
</View>
) : (
<Text style={[styles.title, this.props.titleStyle]}>{rowData.title}</Text>
);
return <View style={styles.container}>{title}</View>;
} _renderCircle(rowData, sectionID, rowID) {
var circleSize = rowData.circleSize
? rowData.circleSize
: this.props.circleSize ? this.props.circleSize : defaultCircleSize;
var circleColor = rowData.circleColor
? rowData.circleColor
: this.props.circleColor ? this.props.circleColor : defaultCircleColor;
var lineWidth = rowData.lineWidth
? rowData.lineWidth
: this.props.lineWidth ? this.props.lineWidth : defaultLineWidth; var circleStyle = null; switch (this.props.columnFormat) {
case "single-column-left":
circleStyle = {
width: this.state.x ? circleSize : ,
height: this.state.x ? circleSize : ,
borderRadius: circleSize / ,
backgroundColor: circleColor,
left: this.state.x - circleSize / + (lineWidth - ) /
};
break;
case "single-column-right":
circleStyle = {
width: this.state.width ? circleSize : ,
height: this.state.width ? circleSize : ,
borderRadius: circleSize / ,
backgroundColor: circleColor,
left: this.state.width - circleSize / - (lineWidth - ) /
};
break;
case "two-column":
circleStyle = {
width: this.state.width ? circleSize : ,
height: this.state.width ? circleSize : ,
borderRadius: circleSize / ,
backgroundColor: circleColor,
left: this.state.width - circleSize / - (lineWidth - ) /
};
break;
} var innerCircle = null;
switch (this.props.innerCircle) {
case "icon":
let iconSource = rowData.icon ? rowData.icon : this.props.icon;
let iconStyle = {
height: circleSize,
width: circleSize
};
innerCircle = (
<Image
source={iconSource}
style={[iconStyle, this.props.iconStyle]}
/>
);
break;
case "dot":
let dotStyle = {
height: circleSize / ,
width: circleSize / ,
borderRadius: circleSize / ,
backgroundColor: rowData.dotColor
? rowData.dotColor
: this.props.dotColor ? this.props.dotColor : defaultDotColor
};
innerCircle = <View style={[styles.dot, dotStyle]}/>;
break;
}
return (
<View style={[styles.circle, circleStyle, this.props.circleStyle]}>
{innerCircle}
</View>
);
} _renderSeparator() {
if (!this.props.separator) {
return null;
}
return <View style={[styles.separator, this.props.separatorStyle]}/>;
}
} const styles = StyleSheet.create({
container: {
flex:
},
listview: {
flex:
},
sectionHeader: {
marginBottom: ,
backgroundColor: "#007AFF",
height: ,
justifyContent: "center"
},
sectionHeaderText: {
color: "#FFF",
fontSize: ,
alignSelf: "center"
},
rowContainer: {
flexDirection: "row",
flex: ,
//alignItems: 'stretch',
justifyContent: "center"
},
timeContainer: {
minWidth:
},
time: {
textAlign: "right",
color: defaultTimeTextColor
},
circle: {
width: ,
height: ,
borderRadius: ,
position: "absolute",
left: -,
alignItems: "center",
justifyContent: "center"
},
dot: {
width: ,
height: ,
borderRadius: ,
backgroundColor: defaultDotColor
},
title: {
fontSize: ,
fontWeight: "bold"
},
details: {
borderLeftWidth: defaultLineWidth,
flexDirection: "column",
flex:
},
detail: {paddingTop: , paddingBottom: },
description: {
marginTop:
},
separator: {
height: ,
backgroundColor: "#aaa",
marginTop: ,
marginBottom:
}
});
代码调用:
import React, {Component} from 'react';
import {StyleSheet, View} from 'react-native';
import Timeline from './CustomTimeLine'; export default class TestTimeLine extends Component {
constructor() {
super()
this.data = [
{time: '09:00', title: '商家已接单', description: 'Event 1 Description'},
{time: '10:45', title: '正在沟通', description: 'Event 2 Description'},
{time: '12:00', title: '合同签订', description: 'Event 3 Description'},
{time: '14:00', title: '订单完成', description: 'Event 4 Description'},
{time: '16:30', title: '用户评价', description: 'Event 5 Description'}
]
} render() {
return (
<View style={styles.container}>
<Timeline
style={styles.list}
data={this.data}
showTime={true}
/>
</View>
);
}
} const styles = StyleSheet.create({
container: {
flex: ,
padding: ,
paddingTop: ,
backgroundColor: 'white'
},
list: {
flex: ,
marginTop: ,
},
});
更多使用方法,参考:
https://github.com/thegamenicorus/react-native-timeline-listview
本博客地址: wukong1688
本文原文地址:https://www.cnblogs.com/wukong1688/p/11049014.html
转载请著名出处!谢谢~~