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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 20:25:17