Demo地址
先上圖

項目需要用到分組列表和字母定位相應(yīng)功能,嘗試下使用RN官方組件SectionList效果可以實現(xiàn),但發(fā)現(xiàn)數(shù)據(jù)量大的時候SectionList渲染數(shù)據(jù)效率很低,當滑動過快的時候很容易出現(xiàn)白屏狀態(tài)。加載也相對較慢。
于是使用react-native-largelist自己稍作封裝,效率確實很強大。
首先感謝一下react-native-largelist的作者 GitHub地址
demo有展示下拉加載的Loading,可以使用RN官方組件ActivityIndicator來作為loading效果。
demo使用的是react-native-spinkit簡單美觀。
- 主UI class
import React, { Component } from 'react'
import {
View, Text, Dimensions, FlatList,
TouchableOpacity, ViewPropTypes,
} from 'react-native'
import styles from './Styles'
import PropTypes from 'prop-types'
let screenH = Dimensions.get('window').height;
import UpPullLoading from './UpPullLoading'
import { LargeList } from "react-native-largelist-v3";
export default class List extends Component {
static propTypes = {
UpPullRefresh: PropTypes.func, //是否展示下拉刷新,下拉刷新的回調(diào)
showHeader: PropTypes.bool, //是否展示頭部組件
renderHeader: PropTypes.func, //頭部組件
renderSection: PropTypes.func, //分組頭組建
renderItem: PropTypes.func, //分組每一項組件
ItemBoxStyle: ViewPropTypes.style, //導(dǎo)航容器樣式
showHeaderBoxStyle: ViewPropTypes.style, //導(dǎo)航第一個容器額外樣式
showHeaderStyle: PropTypes.object, //導(dǎo)航第一個Text的額外樣式
flatBoxStyle: ViewPropTypes.style, //導(dǎo)航List的樣式
letterStyle: PropTypes.object, //導(dǎo)航每一個Text的樣式
indexArray: PropTypes.array, //導(dǎo)航數(shù)組,有則展示右側(cè)導(dǎo)航
dataArray: PropTypes.array.isRequired, //數(shù)據(jù)源數(shù)組
HeaderHeight: PropTypes.number, //頭部高度
Section_Height: PropTypes.number, //分組組頭的高度
Index_Height: PropTypes.number.isRequired, //分組每一項的高度
}
componentWillUnmount() {
this.timer && clearTimeout(this.timer)
}
static defaultProps = {
Index_Height: 50,
Section_Height: 0,
showHeader: false, //默認不展示頭部
UpPullRefresh: () => null,
renderSection: () => null,
};
constructor(props) {
super(props)
}
//計算偏移量
getOfset = (key) => {
const { dataArray, Index_Height, Section_Height, HeaderHeight, showHeader } = this.props
let [hKey, itemkey, sectionKey, hot_height] = [key, 0, 0, 0]
//如果展示頭部則加上頭部高度
if (showHeader) {
if (key > 0) hKey = key - 1
hot_height = key ? HeaderHeight : 0
}
for (i = 0; i < hKey; i++) {
for (index = 0, len = dataArray[i].items.length; index < len; index++) {
itemkey++
}
sectionKey++
}
return (itemkey * Index_Height + sectionKey * Section_Height) + hot_height
}
_onSectionselect = (value, key) => {
const ofset = this.getOfset(key)
if (this._LargeList) {
this._LargeList.scrollTo({
x: 0, y: ofset
});
}
};
_renderFooter = () => {
return (
<View style={{ height: 10 }} />
)
}
_FlatItem = ({ item, index }) => {
const { ItemBoxStyle, letterStyle, showHeaderBoxStyle, showHeaderStyle } = this.props
const hot = index == 0
return (
<TouchableOpacity style={[styles.TextBox, ItemBoxStyle, hot && showHeaderBoxStyle]}
onPressIn={({ nativeEvent: e }) => this._onSectionselect(e, index)}>
<Text style={[styles.indexText, letterStyle, hot && showHeaderStyle,]}>
{item}
</Text>
</TouchableOpacity>
)
}
endUpPullRefresh = _ => {
this.timer = setTimeout(() => {
if (this._LargeList)
this._LargeList.endRefresh();
}, 1000);
}
render() {
const { indexArray, dataArray, Section_Height, Index_Height, showHeader, renderItem,
UpPullRefresh, renderSection, renderHeader } = this.props
const top_offset = indexArray ? (screenH - indexArray.length * 15) / 3 : 0
return (
<View style={styles.Box}>
<LargeList
onRefresh={UpPullRefresh}
renderSection={renderSection}
renderIndexPath={renderItem}
refreshHeader={UpPullLoading}
renderFooter={this._renderFooter}
data={dataArray ? dataArray : []}
ref={ref => (this._LargeList = ref)}
heightForSection={() => Section_Height}
heightForIndexPath={() => Index_Height}
renderHeader={showHeader ? renderHeader : () => null}
/>
{
indexArray &&
<View style={[styles.flatBox, {
top: top_offset
}, this.props.flatBoxStyle]}>
<FlatList
data={indexArray}
renderItem={this._FlatItem}
keyExtractor={(item, index) => index.toString()} //不重復(fù)的key
initialNumToRender={indexArray ? indexArray.length : 10}
/>
</View>
}
</View >
)
}
}
- Loading class
這里的加載loading使用的是react-native-spinkit如果不想裝庫可以使用ActivityIndicator
import React from "react";
import {
Animated, View, StyleSheet, Text
} from "react-native";
import arrow from './arrow.png'
import { Colors } from "../../../Themes";
import Spinner from "react-native-spinkit";
import { RefreshHeader } from "react-native-spring-scrollview/RefreshHeader";
export default class UpPullLoading extends RefreshHeader {
static height = 80;
static style = "stickyContent";
render() {
return (
<View style={styles.container}>
{this._renderIcon()}
{this._renderText()}
</View>
);
}
_renderText = _ => {
const s = this.state.status;
if (s === 'refreshing') {
return (
<View />
)
} else {
return (
<View style={styles.rContainer}>
<Text style={styles.text}>
{this.getTitle()}
</Text>
</View>
)
}
}
_renderIcon = _ => {
const s = this.state.status;
if (s === "refreshing") {
return <Spinner size={36} type="9CubeGrid" color={Colors.Subject} style={{ alignSelf: 'center' }} />
}
const { maxHeight, offset } = this.props;
return (
<Animated.Image
source={arrow}
style={{
tintColor: Colors.Subject,
transform: [
{
rotate: offset.interpolate({
inputRange: [-maxHeight - 1 - 10, -maxHeight - 10, -50, -49],
outputRange: ["180deg", "180deg", "0deg", "0deg"]
})
}
]
}}
/>
);
}
getTitle() {
const s = this.state.status;
switch (s) {
case "pulling":
return "下拉刷新"
case "waiting":
return "下拉刷新"
case "pullingEnough":
return "松開刷新"
case "refreshing":
return "請稍等..."
case "pullingCancel":
return "放棄刷新"
case "rebound":
return "刷新完成"
default:
break;
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
flexDirection: "row"
},
rContainer: {
marginLeft: 10
},
text: {
marginVertical: 5,
fontSize: 15,
color: Colors.Subject,
}
});
- 使用的時候只需要導(dǎo)入List class使用方法如下
dataArray 為主數(shù)據(jù)數(shù)組如要使用分組列表 。數(shù)組結(jié)構(gòu)應(yīng)為[{xx:xx,items:[]}]。
當然可以只用作不需要分組的List使用數(shù)結(jié)構(gòu)為只需要dataArray=[{items:你的數(shù)組}]
import React, { Component, Fragment } from 'react'
import {
View, Text, Image,
FlatList, TouchableOpacity,
} from 'react-native'
import { Colors, Styles, Px } from '../../../Themes'
import List from '../../../Component/List'
import styles from './Styles'
import cityIndex from './Config/cityIndex'
import hotCities from './Config/hotCities'
import alphabeticalIndex from './Config/alphabeticalIndex'
import location from '../../../Images/Home/location.png'
import close from '../../../Images/Component/guanbi.png'
import Header from '../../../Component/Header';
import PropTypes from 'prop-types'
const Section_Height = Px(87)
const Index_Height = Px(80)
const HotHeight = Px(402)
export default class CityList extends Component {
static propTypes = {
ChoosingCity: PropTypes.func,
closeModal: PropTypes.func,
}
constructor(props) {
super(props)
}
_renderSection = (index) => {
const contact = cityIndex[index];
return (
<View style={styles.SectionBox}>
<Text style={styles.SectionText}>{contact.sortLetters}</Text>
</View>
)
}
_renderItem = ({ section: section, row: row }) => {
const item = cityIndex[section].items[row];
return (
<TouchableOpacity style={styles.ItemBox}
onPress={() => this.props.ChoosingCity(item.name)}
>
<Text style={styles.ItemTetx}>{item.name}</Text>
<View style={styles.border} />
</TouchableOpacity>
)
}
_flatItem = ({ item, index }) => {
return (
<TouchableOpacity style={styles.flatItemBox}
onPress={() => this.props.ChoosingCity(item.name)}
>
<Text style={styles.ItemTetx} >{item.name}</Text>
</TouchableOpacity>
)
}
LocatingCity = _ => {
return (
<View>
<View style={styles.SectionBox}>
<Text style={styles.SectionText}>當前定位城市</Text>
</View>
<View style={[styles.flatItemBox, styles.LocatingBox]}>
<Image style={styles.ImageStyles} source={location} />
<Text style={[styles.ItemTetx, { color: Colors.white }]} >北京市</Text>
</View>
</View>
)
}
_renderHeader = _ => {
return (
<View>
{this.LocatingCity()}
<View style={styles.SectionBox}>
<Text style={styles.SectionText}>{hotCities.sortLetters}</Text>
</View>
<View style={styles.FlatBox}>
<FlatList
numColumns={3}
data={hotCities.items}
renderItem={this._flatItem}
keyExtractor={(item, index) => `item${index}`}
/>
</View>
</View>
)
}
_LeftComponent = _ => {
return (
<TouchableOpacity onPress={this.props.closeModal}>
<Image source={close} style={Styles.closeStyle} />
</TouchableOpacity>
)
}
_UpPullRefresh = _ => {
//結(jié)束刷新狀態(tài)
this._list.endUpPullRefresh()
}
render() {
return (
<Fragment>
<Header
title={'選擇城市'}
showStatusBar={false}
headerBgColor={Colors.Subject}
titleColor={Colors.white}
LeftComponent={this._LeftComponent} >
</Header>
<View style={styles.Box}>
<List
showHeader={true}
dataArray={cityIndex}
HeaderHeight={HotHeight}
Index_Height={Index_Height}
renderItem={this._renderItem}
indexArray={alphabeticalIndex}
ref={ref => (this._list = ref)}
Section_Height={Section_Height}
renderHeader={this._renderHeader}
UpPullRefresh={this._UpPullRefresh} //下拉刷新
renderSection={this._renderSection}
showHeaderStyle={styles.showHeaderStyle}
showHeaderBoxStyle={styles.showHeaderBoxStyle}
/>
</View>
</Fragment>
)
}
}
demo只展示了城市列表,手機聯(lián)系人列表也是一個道理,只需要更改數(shù)據(jù)源就可以。完整demo地址