React Native中SearchBar生成的FlatList项高亮实现方案问询
实现FlatList单项高亮选中功能
问题背景
已实现React Native SearchBar,输入时会动态填充FlatList视图,需求是点击FlatList项时仅高亮当前选中项,通过切换item和itemSelected样式实现,且保证同一时间只有一项高亮。
实现方案
核心思路是利用已有的selectedValue状态记录当前选中项的ID,在渲染FlatList每一项时,对比当前项的value与selectedValue,动态应用对应的样式:
- 修改
renderItem函数,根据当前项的value是否等于selectedValue切换样式 - 点击项时通过
handleChangeCancer更新selectedValue,触发FlatList重新渲染,自动取消之前项的高亮
修改后的代码
组件代码
import React, { useState, useEffect } from "react"; import { SearchBar } from 'react-native-elements'; import { StyleSheet, Text, View, ScrollView, ActivityIndicator, FlatList, TouchableOpacity, StatusBar } from "react-native"; import { useNavigation } from '@react-navigation/native'; import constants from './Constants.js'; import { styles } from './CommonStylesWeb.js'; import {SafeAreaView, SafeAreaProvider} from 'react-native-safe-area-context'; const CancerHomepageWeb = ({navigation}) => { const [cancerWiki, setCancerWiki] = useState(""); const [cancerType, setCancerType] = useState(""); const [selectedValue, setSelectedValue] = useState(null); const [selectedLabel, setSelectedLabel] = useState(null); const [options, setOptions] = useState([]); const [cancerWikiContent, setCancerWikiContent] = useState(""); const [loadingWiki, setLoadingWiki] = useState(null); const [loadingCancers, setLoadingCancers] = useState(null); useEffect(() => { const getData = async () => { setLoadingWiki(true); var urlCancerWiki = await constants.getServerURL() + 'cancers_wiki'; let resultCancerWiki = await fetch(urlCancerWiki, { method: "GET", mode: 'cors' }).then(response => response.json()) .then(response => { if (response.status == 200) { setLoadingWiki(false) setCancerWiki(response.content) } else { setLoadingWiki(false) console.warn(response.message) setCancerWiki(response.message) } }); }; getData(); }, []); async function handleChangeCancer(value, label) { setSelectedValue(value); if(label) { setCancerWiki(""); setCancerWikiContent(""); setLoadingWiki(true); const url = await constants.getServerURL() + 'cancer_wiki'; let result = fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: value}), mode: 'cors' }).then(response => response.json()) .then(response => { if (response.status == 200) { setLoadingWiki(false); setCancerWikiContent(response.content) setCancerWiki("") } else { setLoadingWiki(false) console.warn(response.message) setCancerWiki(response.message) } }); } }; const searchCancers = async() => { const url = await constants.getServerURL() + 'cancer_name'; if (cancerType.length >= 3) { setLoadingCancers(true); let result = fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ cancer_type: cancerType}), mode: 'cors' }).then(response => response.json()) .then(response => { setOptions([]); const optionsTemp = []; if (response.status == 200) { setLoadingCancers(false); response.content.map( (cancer) => { optionsTemp.push({label:cancer.name , value: cancer.id}); }); setOptions(optionsTemp); } else { setLoadingCancers(false); console.warn("Error retrieving Cancer"); } }); } } // 核心修改:动态切换选中/未选中样式 const renderItem = ({ item }) => ( <TouchableOpacity onPress={() => handleChangeCancer(item.value,item.label)}> <Text style={item.value === selectedValue ? styles.itemSelected : styles.item}> {item.label} </Text> </TouchableOpacity> ); return ( <View style={styles.container}> <StatusBar style="auto" /> <View> <SearchBar style={styles.searchBar} placeholder="Search by Cancer Type" onChangeText={(cancerType) => setCancerType(cancerType)} value={cancerType} onKeyPress={searchCancers} /> {loadingCancers && <ActivityIndicator size="large" /> } <FlatList data={options} renderItem={renderItem} keyExtractor={item => item.value} style={styles.dropdown} /> </View> <View> <Text style={{ fontSize: 20 }}>Cancers</Text> </View> <ScrollView> <View style={styles.wikiView}> <Text>{cancerWikiContent}</Text> <Text>{cancerWiki}</Text> {loadingWiki && <SafeAreaProvider> <SafeAreaView style={[styles.loadingContainer, styles.loadingHorizontal]}> <ActivityIndicator size="large" /> </SafeAreaView> </SafeAreaProvider> } </View> </ScrollView> </View> ); } export { CancerHomepageWeb };
样式代码(保持原样式即可)
import { StyleSheet } from "react-native"; import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen'; const styles = StyleSheet.create({ searchBar: { width: wp(60), height: 10, paddingLeft: 10, flex: 1, alignItems: "center", justifyContent: "center", }, dropdown: { maxHeight: 150, backgroundColor: 'white', borderWidth: 1, borderColor: 'gray', borderRadius: 5, marginTop: 5, }, item: { padding: 15, fontSize: 16, }, itemSelected: { padding: 15, fontSize: 16, backgroundColor: '#2196F3', color: "white" } }); export { styles };
关键说明
- 利用
selectedValue作为唯一选中标识,每次点击项时更新该状态,FlatList会自动重新渲染所有项,仅为匹配项应用高亮样式 - 搜索新内容时,
options被重置,若selectedValue无匹配项,高亮会自动消失,符合交互逻辑
内容的提问来源于stack exchange,提问作者MattJ
相关产品推荐
相关产品推荐

