[RN] React Native 好用的时间线 组件
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 = 16;
const defaultCircleColor = "#007AFF";
const defaultLineWidth = 2;
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: 0,
width: 0
};
}
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 % 2 === 0 ? (
<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: 1,
alignItems: rowID % 2 === 0 ? "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(-1)[0] === 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: 0,
marginLeft: 20,
paddingLeft: 20
};
break;
case "single-column-right":
opStyle = {
borderColor: lineColor,
borderLeftWidth: 0,
borderRightWidth: lineWidth,
marginRight: 20,
paddingRight: 20
};
break;
case "two-column":
opStyle =
rowID % 2 == 0
? {
borderColor: lineColor,
borderLeftWidth: lineWidth,
borderRightWidth: 0,
marginLeft: 20,
paddingLeft: 20
}
: {
borderColor: lineColor,
borderLeftWidth: 0,
borderRightWidth: lineWidth,
marginRight: 20,
paddingRight: 20
};
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 : 0,
height: this.state.x ? circleSize : 0,
borderRadius: circleSize / 2,
backgroundColor: circleColor,
left: this.state.x - circleSize / 2 + (lineWidth - 1) / 2
};
break;
case "single-column-right":
circleStyle = {
width: this.state.width ? circleSize : 0,
height: this.state.width ? circleSize : 0,
borderRadius: circleSize / 2,
backgroundColor: circleColor,
left: this.state.width - circleSize / 2 - (lineWidth - 1) / 2
};
break;
case "two-column":
circleStyle = {
width: this.state.width ? circleSize : 0,
height: this.state.width ? circleSize : 0,
borderRadius: circleSize / 2,
backgroundColor: circleColor,
left: this.state.width - circleSize / 2 - (lineWidth - 1) / 2
};
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 / 2,
width: circleSize / 2,
borderRadius: circleSize / 4,
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: 1
},
listview: {
flex: 1
},
sectionHeader: {
marginBottom: 15,
backgroundColor: "#007AFF",
height: 30,
justifyContent: "center"
},
sectionHeaderText: {
color: "#FFF",
fontSize: 18,
alignSelf: "center"
},
rowContainer: {
flexDirection: "row",
flex: 1,
//alignItems: 'stretch',
justifyContent: "center"
},
timeContainer: {
minWidth: 45
},
time: {
textAlign: "right",
color: defaultTimeTextColor
},
circle: {
width: 16,
height: 16,
borderRadius: 10,
position: "absolute",
left: -8,
alignItems: "center",
justifyContent: "center"
},
dot: {
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: defaultDotColor
},
title: {
fontSize: 16,
fontWeight: "bold"
},
details: {
borderLeftWidth: defaultLineWidth,
flexDirection: "column",
flex: 1
},
detail: {paddingTop: 10, paddingBottom: 10},
description: {
marginTop: 10
},
separator: {
height: 1,
backgroundColor: "#aaa",
marginTop: 10,
marginBottom: 10
}
});
代码调用:
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: 1,
padding: 20,
paddingTop: 65,
backgroundColor: 'white'
},
list: {
flex: 1,
marginTop: 20,
},
});
更多使用方法,参考:
https://github.com/thegamenicorus/react-native-timeline-listview
本博客地址: wukong1688
本文原文地址:https://www.cnblogs.com/wukong1688/p/11049014.html
转载请著名出处!谢谢~~
以上是 [RN] React Native 好用的时间线 组件 的全部内容, 来源链接: utcz.com/z/382204.html