You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 04:25:39