如何为React Native的RecyclerListView添加搜索过滤功能?
为RecyclerListView添加搜索过滤功能的实现方案
不需要依赖额外的搜索过滤库,直接通过维护搜索状态+过滤原始数据+更新DataProvider就能实现需求。以下是针对你的代码的具体修改步骤:
步骤1:添加搜索状态和处理函数
在组件的state中添加搜索文本状态,同时实现输入变化的处理函数:
constructor(props) { super(props); this.state = { optionModalVisible: false, searchText: '' // 新增搜索文本状态 }; this.currentItem = {}; } // 新增:处理搜索文本变化 handleSearchTextChange = (text) => { this.setState({ searchText: text }); };
步骤2:实现数据过滤逻辑
基于原始音频数据(从Context中获取的audioFiles)和搜索文本,生成过滤后的DataProvider:
// 新增:获取过滤后的DataProvider getFilteredDataProvider = (audioFiles, searchText) => { let filteredData = audioFiles; if (searchText.trim()) { const lowerCaseSearch = searchText.toLowerCase(); filteredData = audioFiles.filter(item => item.filename.toLowerCase().includes(lowerCaseSearch) ); } return this.dataProvider.cloneWithRows(filteredData); };
步骤3:添加搜索栏UI
在render方法的顶部添加搜索输入框:
render() { if(global.Hz === undefined){ global.Hz = 440 } return ( <> {/* 新增搜索栏 */} <View style={styles.searchContainer}> <TextInput style={styles.searchInput} placeholder="搜索音频名称..." value={this.state.searchText} onChangeText={this.handleSearchTextChange} clearButtonMode="always" /> </View> <View style = {styles.viewStyle}> <Text style = {{fontWeight: 'bold',fontSize: 24, }}>{global.Hz} Hz mode</Text> </View> {/* 剩余原有代码... */}
步骤4:更新RecyclerListView的数据源
在AudioContext.Consumer中,用过滤后的DataProvider替代原有的dataProvider:
<AudioContext.Consumer> {({ audioFiles, isPlaying }) => { // 这里要拿到原始的audioFiles const filteredDataProvider = this.getFilteredDataProvider(audioFiles, this.state.searchText); if(!filteredDataProvider.getSize()) return <Text style={styles.emptyText}>未找到匹配的音频</Text>; return ( <View style={{ minHeight: 1, minWidth: 1, flex: 1 }}> <RecyclerListView dataProvider={filteredDataProvider} layoutProvider={this.layoutProvider} rowRenderer={this.rowRenderer} extendedState={{ isPlaying }} /> {/* OptionModal代码不变 */} </View> ); }} </AudioContext.Consumer>
步骤5:添加搜索栏样式
在StyleSheet中补充搜索相关样式:
const styles = StyleSheet.create({ // 原有样式... searchContainer: { padding: 10, backgroundColor: '#f5f5f5', }, searchInput: { height: 40, backgroundColor: 'white', borderRadius: 8, paddingHorizontal: 12, }, emptyText: { textAlign: 'center', marginTop: 20, fontSize: 16, color: '#999', } });
完整修改后的代码
import React, { Component } from "react"; import { View, StyleSheet, Text, TextInput, Dimensions } from "react-native"; import { Profiler } from "react/cjs/react.production.min"; import { AudioContext } from "../context/AudioProvider"; import * as MediaLibrary from "expo-media-library"; import Screen from "../components/Screen"; import { DataProvider, LayoutProvider } from "recyclerlistview"; import { RecyclerListView } from "recyclerlistview"; import AudioListItem from "../components/AudioListItem"; import OptionModal from "../components/OptionModal"; import { Audio } from "expo-av"; import { play, pause, resume, playNext, updatePitch, selectAudio, AddToQueue, } from "../misc/AudioController"; import { goBack } from "./PlayListDetail"; import { storeAudioForNextOpening } from "../misc/helper"; import { hidePlayList } from "./Playlist"; import "./Settings"; export class AudioList extends Component { static contextType = AudioContext; constructor(props) { super(props); this.state = { optionModalVisible: false, searchText: '' }; this.currentItem = {}; } dataProvider = new DataProvider((r1, r2) => { return r1 !== r2; }); layoutProvider = new LayoutProvider( (i) => "audio", (type, dim) => { dim.width = Dimensions.get("window").width; dim.height = 63; } ); handleSearchTextChange = (text) => { this.setState({ searchText: text }); }; getFilteredDataProvider = (audioFiles, searchText) => { let filteredData = audioFiles; if (searchText.trim()) { const lowerCaseSearch = searchText.toLowerCase(); filteredData = audioFiles.filter(item => item.filename.toLowerCase().includes(lowerCaseSearch) ); } return this.dataProvider.cloneWithRows(filteredData); }; handleAudioPress = async (audio) => { await selectAudio(audio,this.context); }; componentDidMount() { this.context.loadPreviousAudio() } rowRenderer = (type, item, index, extendedState) => { return ( <AudioListItem title={item.filename} isPlaying={extendedState.isPlaying} activeListItem={this.context.currentAudioIndex === index} duration={item.duration} onAudioPress={() => this.handleAudioPress(item)} onOptionPress={() => { this.currentItem = item; this.setState({ ...this.state, optionModalVisible: true }); }} /> ); }; navigateToPlaylist = () => { console.log("navigate"); this.context.updateState(this.context, { addToPlayList: this.currentItem, }); this.props.navigation.navigate("Playlist"); } render() { if(global.Hz === undefined){ global.Hz = 440 } return ( <> <View style={styles.searchContainer}> <TextInput style={styles.searchInput} placeholder="搜索音频名称..." value={this.state.searchText} onChangeText={this.handleSearchTextChange} clearButtonMode="always" /> </View> <View style = {styles.viewStyle}> <Text style = {{fontWeight: 'bold',fontSize: 24, }}>{global.Hz} Hz mode</Text> </View> <AudioContext.Consumer> {({ audioFiles, isPlaying }) => { const filteredDataProvider = this.getFilteredDataProvider(audioFiles, this.state.searchText); if(!filteredDataProvider.getSize()) return <Text style={styles.emptyText}>未找到匹配的音频</Text>; return ( <View style={{ minHeight: 1, minWidth: 1, flex: 1 }}> <RecyclerListView dataProvider={filteredDataProvider} layoutProvider={this.layoutProvider} rowRenderer={this.rowRenderer} extendedState={{ isPlaying }} /> <OptionModal options ={[{title: 'Add to playlist',onPress: this.navigateToPlaylist }]} currentItem={this.currentItem} onClose={() => this.setState({ ...this.state, optionModalVisible: false }) } visible={this.state.optionModalVisible} /> </View> ); }} </AudioContext.Consumer> </> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", alignItems: "center", }, viewStyle: { alignItems: 'center', paddingLeft: 20, paddingBottom: 10 }, searchContainer: { padding: 10, backgroundColor: '#f5f5f5', }, searchInput: { height: 40, backgroundColor: 'white', borderRadius: 8, paddingHorizontal: 12, }, emptyText: { textAlign: 'center', marginTop: 20, fontSize: 16, color: '#999', } }); export default AudioList;
核心逻辑说明
- 搜索栏实时响应用户输入,更新组件内部的
searchText状态 - 每次状态变化时,基于Context中的原始音频列表
audioFiles进行过滤,生成新的DataProvider - RecyclerListView会自动识别DataProvider的变化并刷新列表
- 当搜索文本为空时,自动返回全部音频数据,满足初始显示需求
内容的提问来源于stack exchange,提问作者Light Love Peace
相关产品推荐
相关产品推荐

