参考资料:
React Native使用SectionList打造城市选择列表,包含分组的跳转:https://blog.csdn.net/sinat_17775997/article/details/71424324?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-5&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-5
功能特性如下:
- 支持首字母和首个汉字分组检索。
- 支持右侧分组字母的跳转控制。
- 支持拼音搜索。
- 支持批量选择功能、重置功能。
效果图:
1. SectionList列表分组组件。
- https://reactnative.cn/docs/sectionlist
2. pinyin。 这是一个JS拼音工具开源库。
- 它的用途是将中文转化为拼音。
- https://github.com/hotoo/pinyin
1. 将联系人列表中的数据项转化为拼音存储,以便于拼音搜索。
2. 遍历联系人列表,获取字母分组,得到一个set集合,并且转化为数组,该数组用于渲染右侧字母导航组件。
3. 构建SectionList组件渲染数据所需要的列表数据结构,其数组元素结构为:{title:'分组名称',data:[]},其中,title表示分组名称,data表示该分组的数据列表。
遍历遍历联系人列表,根据首字母分组汇总,得到SectionList组件渲染数据所需要的列表数据结构。
1. 将联系人列表中的数据项转化为拼音存储,以便于拼音搜索。
componentWillMount = () => {
// 将数据列表转化为拼音存储,以便于拼音搜索
testData.forEach((item, index, arr) => {
// 将Item的名称转为拼音数组
let pinyinArr = pinyin(item.name, {style: pinyin.STYLE_NORMAL});
item.pinyinArr = pinyinArr;
let pinyinArrStr = '';
// 将拼音数组转化为一个字符串,以支持拼音搜索
for (let i = 0; i < pinyinArr.length; i++) {
for (let j = 0; j < pinyinArr[i].length; j++) {
pinyinArrStr = pinyinArrStr + pinyinArr[i][j];
}
}
item.pinyinArrStr = pinyinArrStr;
});
this.transferToSectionsData(testData);
};
2. 搜索方法
search = () => {
// alert('搜索');
const {dataList, searchValue} = this.state;
if (searchValue && searchValue.trim()) {
let searchValueTemp = searchValue.toLocaleLowerCase();
const resultList = [];
dataList.forEach((item, index, arr) => {
if (item.name) {
if (item.name.toLocaleLowerCase().indexOf(searchValueTemp) >= 0
|| this.pinyinSingleLetterIndexSearch(searchValueTemp, item.pinyinArr) >= 0
|| item.pinyinArrStr.toLocaleLowerCase().indexOf(searchValueTemp) >= 0) {
resultList.push(item);
}
}
});
console.log('search.resultList:', resultList);
this.transferToSectionsData(resultList);
} else {
this.transferToSectionsData(dataList);
}
};
/**
* 在拼音数组中搜索单个拼音,如果匹配,则返回等于大于0的值,否则返回-1
* @param keyword
* @param pinyinArr
* @returns {number}
*/
pinyinSingleLetterIndexSearch = (keyword, pinyinArr) => {
let result = -1;
if (keyword && pinyinArr) {
for (let i = 0; i < pinyinArr.length; i++) {
for (let j = 0; j < pinyinArr[i].length; j++) {
let singleLetterIndex = pinyinArr[i][j].toLocaleLowerCase().indexOf(keyword);
if (singleLetterIndex >= 0) {
return singleLetterIndex;
}
}
}
}
return result;
};
注意:图片资源需自行补充。
/**
*
* @author chenlw
* @date 2020/04/18
*/
import React from 'react';
import {
Dimensions,
FlatList,
SectionList,
StyleSheet,
Text,
TouchableOpacity,
View,
SafeAreaView,
Image,
TextInput,
Platform,
StatusBar,
} from 'react-native';
import pinyin from 'pinyin';
let testData = [
{id: '盖伦', name: '盖伦'},
{id: '崔丝塔娜', name: '崔丝塔娜'},
{id: '大发明家', name: '大发明家'},
{id: '武器大师', name: '武器大师'},
{id: '刀妹', name: '刀妹'},
{id: '卡特琳娜', name: '卡特琳娜'},
{id: '盲僧', name: '盲僧'},
{id: '蕾欧娜', name: '蕾欧娜'},
{id: '拉克丝', name: '拉克丝'},
{id: '剑圣', name: '剑圣'},
{id: '赏金', name: '赏金'},
{id: '发条', name: '发条'},
{id: '瑞雯', name: '瑞雯'},
{id: '提莫', name: '提莫'},
{id: '卡牌', name: '卡牌'},
{id: '剑豪', name: '剑豪'},
{id: '琴女', name: '琴女'},
{id: '木木', name: '木木'},
{id: '雪人', name: '雪人'},
{id: '安妮', name: '安妮'},
{id: '薇恩', name: '薇恩'},
{id: '小法师', name: '小法师'},
{id: '艾尼维亚', name: '艾尼维亚'},
{id: '奥瑞利安索尔', name: '奥瑞利安索尔'},
{id: '布兰德', name: '布兰德'},
{id: '凯特琳', name: '凯特琳'},
{id: '虚空', name: '虚空'},
{id: '机器人', name: '机器人'},
{id: '挖掘机', name: '挖掘机'},
{id: 'EZ', name: 'EZ'},
{id: '暴走萝莉', name: '暴走萝莉'},
{id: '艾克', name: '艾克'},
{id: '波比', name: '波比'},
{id: '赵信', name: '赵信'},
{id: '牛头', name: '牛头'},
{id: '九尾', name: '九尾'},
{id: '菲兹', name: '菲兹'},
{id: '寒冰', name: '寒冰'},
{id: '猴子', name: '猴子'},
{id: '深渊', name: '深渊'},
{id: '凯南', name: '凯南'},
{id: '诺克萨斯', name: '诺克萨斯'},
{id: '祖安', name: '祖安'},
{id: '德莱文', name: '德莱文'},
{id: '德玛西亚王子', name: '德玛西亚王子'},
{id: '豹女', name: '豹女'},
{id: '皮城执法官', name: '皮城执法官'},
{id: '泽拉斯', name: '泽拉斯'},
{id: '岩雀', name: '岩雀'},
];
const selectedFieldName = 'id';
const isAndroid = Platform.OS === 'android';
export default class IndexListComponentExample extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
searchValue: null,
dataList: testData,
sections: [], //section数组
// letterArr: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'], //首字母数组
letterArr: [], //首字母数组
activeLetterIndex: 0,
selectedItemSet: new Set(),
// 是否开启批量选择模式
batchSelected: true,
refreshCount: 0,
};
}
componentWillMount = () => {
// 将数据列表转化为拼音存储,以便于拼音搜索
testData.forEach((item, index, arr) => {
// 将Item的名称转为拼音数组
let pinyinArr = pinyin(item.name, {style: pinyin.STYLE_NORMAL});
item.pinyinArr = pinyinArr;
let pinyinArrStr = '';
// 将拼音数组转化为一个字符串,以支持拼音搜索
for (let i = 0; i < pinyinArr.length; i++) {
for (let j = 0; j < pinyinArr[i].length; j++) {
pinyinArrStr = pinyinArrStr + pinyinArr[i][j];
}
}
item.pinyinArrStr = pinyinArrStr;
});
this.transferToSectionsData(testData);
};
/**
* 转化数据列表
*/
transferToSectionsData = (dataList) => {
//获取联系人列表
let sections = [], letterArr = [];
// 右侧字母栏数据处理
dataList.forEach((item, index, arr) => {
let itemTemp = pinyin(item.name.substring(0, 1), {
style: pinyin.STYLE_FIRST_LETTER,
})[0][0].toUpperCase();
letterArr.push(itemTemp);
});
letterArr = [...new Set(letterArr)].sort();
this.setState({letterArr: letterArr});
// 分组数据处理
letterArr.forEach((item, index, arr) => {
sections.push({
title: item,
data: [],
});
});
dataList.forEach((item1, index1, arr1) => {
let listItem = item1;
sections.forEach((item2, index2, arr2) => {
let firstName = listItem.name.substring(0, 1);
let firstLetter = pinyin(firstName, {style: pinyin.STYLE_FIRST_LETTER})[0][0].toUpperCase();
let pinyinStrArr = pinyin(listItem.name, {style: pinyin.STYLE_NORMAL});
console.log('pinyinStr', pinyinStrArr);
if (item2.title === firstLetter) {
item2.data.push({
firstName: firstName,
name: listItem.name,
id: listItem.id,
});
}
});
});
this.setState({sections: sections});
};
openBatchSelectedMode = (callback) => {
this.setState({
batchSelected: true,
selectedItemSet: new Set(),
}, () => {
callback && callback();
});
};
closeBatchSelectedMode = () => {
this.setState({
batchSelected: false,
selectedItemSet: new Set(),
});
};
addOrDeleteSelectedItem = (item) => {
const {batchSelected, selectedItemSet, refreshCount} = this.state;
if (!batchSelected) {
return;
}
if (item && item[selectedFieldName]) {
if (selectedItemSet.has(item[selectedFieldName])) {
selectedItemSet.delete(item[selectedFieldName]);
} else {
selectedItemSet.add(item[selectedFieldName]);
}
console.log('addOrDeleteSelectedItem.selectedItemSet', selectedItemSet);
this.setState({
selectedItemSet: selectedItemSet,
refreshCount: refreshCount + 1,
}, () => {
});
}
};
/**
* 重置选中的成员
*/
clearSelectedItem = () => {
const {batchSelected, selectedItemSet, refreshCount} = this.state;
selectedItemSet.clear();
this.setState({
selectedItemSet: selectedItemSet,
refreshCount: refreshCount + 1,
}, () => {
});
};
// 字母关联分组跳转
_onSectionselect = (key) => {
this.setState({
activeLetterIndex: key,
}, () => {
});
this.refs._sectionList.scrollToLocation({
itemIndex: 0,
sectionIndex: key,
viewOffset: 20,
});
};
// 分组列表的头部
_renderSectionHeader(sectionItem) {
const {section} = sectionItem;
return (
{section.title.toUpperCase()}
);
}
renderItemSelectedIcon = (item) => {
if (!item) {
return;
}
const {batchSelected, selectedItemSet} = this.state;
if (batchSelected) {
let isActive = selectedItemSet.has(item[selectedFieldName]);
return (
);
} else {
return null;
}
};
_renderItem(item, index) {
const {batchSelected} = this.state;
return (
{
if (!batchSelected) {
this.openBatchSelectedMode(() => {
this.addOrDeleteSelectedItem(item);
});
}
}}
onPress={() => {
this.addOrDeleteSelectedItem(item);
}}
>
{
this.renderItemSelectedIcon(item)
}
{item.firstName}
{item.name}
);
}
renderBatchSelectedHeader = () => {
const {batchSelected, selectedItemSet} = this.state;
if (!batchSelected) {
return (
{
this.openBatchSelectedMode();
}}
>
批量选择
);
}
return (
{
this.closeBatchSelectedMode();
}}
>
取消
已选择{selectedItemSet.size}条记录
{
this.closeBatchSelectedMode();
}}
>
确定
);
};
render = () => {
const {letterArr, sections, activeLetterIndex, batchSelected} = this.state;
//偏移量 = (设备高度 - 字母索引高度 - 底部导航栏 - 顶部标题栏 - 24)/ 2
let top_offset = (Dimensions.get('window').height - letterArr.length * 16 - 52 - 44 - 24) / 2;
if (isAndroid) {
top_offset = top_offset + StatusBar.currentHeight + 45;
}
return (
{
this.renderSearchBar()
}
{
this.renderBatchSelectedHeader()
}
this._renderItem(item, index)}
renderSectionHeader={this._renderSectionHeader.bind(this)}
sections={sections}
keyExtractor={(item, index) => item + index}
ItemSeparatorComponent={() => }
/>
{/*右侧字母栏*/}
index.toString()}
renderItem={({item, index}) => {
let isActive = index === activeLetterIndex;
// let textStyle = isActive ? styles.activeIndicatorText : styles.inactiveIndicatorText;
// let containerStyle = isActive ? styles.activeIndicatorContainer : styles.inactiveIndicatorContainer;
let textStyle = styles.inactiveIndicatorText;
let containerStyle = styles.inactiveIndicatorContainer;
return (
{
this._onSectionselect(index);
}}
>
{item.toUpperCase()}
);
}}
/>
);
};
setSearchValue = (searchValue, callback) => {
this.setState({
searchValue: searchValue,
}, () => {
callback && callback();
});
};
search = () => {
// alert('搜索');
const {dataList, searchValue} = this.state;
if (searchValue && searchValue.trim()) {
let searchValueTemp = searchValue.toLocaleLowerCase();
const resultList = [];
dataList.forEach((item, index, arr) => {
if (item.name) {
if (item.name.toLocaleLowerCase().indexOf(searchValueTemp) >= 0
|| this.pinyinSingleLetterIndexSearch(searchValueTemp, item.pinyinArr) >= 0
|| item.pinyinArrStr.toLocaleLowerCase().indexOf(searchValueTemp) >= 0) {
resultList.push(item);
}
}
});
console.log('search.resultList:', resultList);
this.transferToSectionsData(resultList);
} else {
this.transferToSectionsData(dataList);
}
};
/**
* 在拼音数组中搜索单个拼音,如果匹配,则返回等于大于0的值,否则返回-1
* @param keyword
* @param pinyinArr
* @returns {number}
*/
pinyinSingleLetterIndexSearch = (keyword, pinyinArr) => {
let result = -1;
if (keyword && pinyinArr) {
for (let i = 0; i < pinyinArr.length; i++) {
for (let j = 0; j < pinyinArr[i].length; j++) {
let singleLetterIndex = pinyinArr[i][j].toLocaleLowerCase().indexOf(keyword);
if (singleLetterIndex >= 0) {
return singleLetterIndex;
}
}
}
}
return result;
};
renderSearchBar = () => {
const {searchValue} = this.state;
return (
{
}}
>
{
this.setSearchValue(text, () => {
this.search();
});
}}
onSubmitEditing={() => {
}}
/>
{
searchValue
? {
this.setSearchValue(null, () => {
this.search();
});
}}
>
: null
}
{
this.search();
}}
>
搜索
);
};
}
const styles = StyleSheet.create({
taskNodeTitleText: {
color: '#333333',
fontWeight: 'bold',
fontSize: 16,
},
inactiveIndicatorContainer: {},
activeIndicatorContainer: {
backgroundColor: '#2988FF',
},
inactiveIndicatorText: {
color: '#666666',
},
activeIndicatorText: {
color: '#fff',
},
});