React Native可拖拽分组列表问题:拖拽时选中整个分组
解决拖拽分组列表时选中整个分组的问题
问题根源
你当前的实现把整个分组(如A、B组)当作了单个可拖拽项,而非分组内的每个子项。DraggableFlatList 接收的 data 是分组数组,renderItem 渲染的是整个分组,导致触发拖拽时会移动整个分组。
修复方案
需要调整数据结构和组件逻辑,让每个子项成为独立的可拖拽单元,同时保留分组展示效果:
- 重构数据状态,将分组数据转为可修改的状态并生成扁平化子项列表
- 给每个子项绑定独立拖拽事件,优化拖拽视觉效果
- 拖拽结束后重新整理分组数据,将子项归属到目标分组
修改后的完整代码
import React, { FC, useState } from "react"; import { Text, View, StyleSheet, TouchableOpacity } from "react-native"; import DraggableFlatList, { RenderItemParams, ScaleDecorator, } from "react-native-draggable-flatlist"; import { GestureHandlerRootView } from "react-native-gesture-handler"; interface GroupItem { header: string; data: string[]; } interface DraggableItem { id: string; header: string; value: string; } const INITIAL_GROUPS: GroupItem[] = [ { header: "A", data: ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10"], }, { header: "B", data: ["B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "B10"], }, ]; export default function App() { const [groups, setGroups] = useState<GroupItem[]>(INITIAL_GROUPS); // 扁平化分组数据,生成可拖拽的子项列表 const flattenedData: DraggableItem[] = groups.flatMap((group) => group.data.map((item) => ({ id: item, header: group.header, value: item, })) ); // 渲染分组头部(仅在每组第一个子项前显示) const renderSectionHeader = (header: string) => ( <View style={styles.header}> <Text style={styles.headerText}>{header}</Text> </View> ); // 渲染单个可拖拽子项 const renderItem = ({ item, drag, isActive, index, }: RenderItemParams<DraggableItem>) => { // 判断是否需要显示分组头部 const showHeader = index === 0 || flattenedData[index - 1].header !== item.header; return ( <View> {showHeader && renderSectionHeader(item.header)} <ScaleDecorator> <TouchableOpacity style={[ styles.draggableContainer, { backgroundColor: isActive ? "blue" : "white" }, ]} onLongPress={drag} > <Text style={styles.text}>{item.value}</Text> </TouchableOpacity> </ScaleDecorator> </View> ); }; // 处理拖拽结束事件,重新整理分组数据 const handleDragEnd = ({ data, to, }: { data: DraggableItem[]; to: number; }) => { // 确定子项拖拽后的目标分组 let targetHeader = data[to].header; if (to > 0) { targetHeader = data[to - 1].header; } else if (data.length > 1) { targetHeader = data[1].header; } // 更新子项的分组归属 const updatedData = data.map((item, idx) => idx === to ? { ...item, header: targetHeader } : item ); // 重新生成分组结构 const uniqueHeaders = groups.map((g) => g.header); const newGroups = uniqueHeaders.map((header) => ({ header, data: updatedData .filter((item) => item.header === header) .map((item) => item.value), })); setGroups(newGroups); }; return ( <GestureHandlerRootView style={{ flex: 1 }}> <View style={styles.container}> <DraggableFlatList data={flattenedData} renderItem={renderItem} keyExtractor={(item) => item.id} onDragEnd={handleDragEnd} /> </View> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ container: { flex: 1, }, header: { backgroundColor: "#f9c2ff", height: 30, justifyContent: "center", paddingLeft: 10, marginVertical: 8, marginHorizontal: 16, }, headerText: { fontSize: 16, fontWeight: "bold", }, draggableContainer: { paddingVertical: 10, marginHorizontal: 16, borderBottomWidth: 1, borderBottomColor: "#eee", }, text: { fontSize: 18, }, });
关键修改点
- 数据扁平化:将分组数据转为包含分组标识的子项列表,让每个子项成为独立的拖拽单元
- 分组头部渲染:通过判断当前子项是否为分组第一个元素,仅在每组开头显示头部
- 拖拽后分组更新:拖拽结束时根据目标位置确定子项的新分组,重新整理并更新分组状态
- 拖拽视觉优化:使用
ScaleDecorator包裹可拖拽项,提供默认的缩放动画效果
内容的提问来源于stack exchange,提问作者DavidTaylor
相关产品推荐
相关产品推荐

