拖拽排序组件,支持列表拖拽排序
当前访问频次受限,请登录后继续访问
文档模板:v0.4.2
react-native-drag-sort
本项目基于 react-native-drag-sort 开发。
该第三方库的仓库已迁移至 Gitcode,且支持直接从 npm 下载,新的包名为:@react-native-ohos/react-native-drag-sort,版本所属关系如下:
| 三方库名称 | 三方库版本(npm地址) | 发布信息 | 支持RN版本 | Autolink | 编译API版本 | 社区基线版本 | 源码地址 |
|---|---|---|---|---|---|---|---|
| @react-native-ohos/react-native-drag-sort | ~2.5.0 | Gitcode Releases | 0.72.* / 0.77.* / 0.82.* | 否 | API12+ | 2.4.4 | master |
| @react-native-oh-tpl/react-native-drag-sort | <=2.4.4-0.1.0@deprecated | Github Releases(deprecated) | 0.72 | 否 | API12+ | 2.4.4 | sig |
简介
拖拽排序组件,支持列表拖拽排序
下载安装
进入到工程目录并输入以下命令:
yarn
yarn add @react-native-ohos/react-native-drag-sort
npm
npm install @react-native-ohos/react-native-drag-sort
Tip
使用优先级: DragSortableView > AutoDragSortableView > AnySizeDragSortableView
- 1、如果宽度和高度是固定的,不需要滑动,请使用DragSortableView。
- 2、如果宽度和高度是固定的,需要滑动,请使用AutoDragSortableView。
- 3、如果宽度和高度是任意的,需要滑动,请使用AnySizeDragSortableView。
下面的代码展示了这个库的基本使用场景:
DragSortableView组件使用
import React, { useState } from "react";
import { View, Text, StyleSheet, SafeAreaView } from "react-native";
import { DragSortableView } from "react-native-drag-sort";
//此案例id1、id2不支持拖拽
const Dragsort = () => {
const [data, setData] = useState([
{
id: 1,
title: "固定任务 1",
},
{
id: 2,
title: "固定任务 2",
},
{
id: 3,
title: "任务 3",
},
{
id: 4,
title: "任务 4",
},
{
id: 5,
title: "任务 5",
},
{
id: 6,
title: "任务 6",
},
{
id: 7,
title: "任务 7",
},
{
id: 8,
title: "任务 8",
},
]);
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.header}>
<Text style={styles.header_title}>DragSortableView</Text>
</View>
<DragSortableView
dataSource={data}
parentWidth={400}
childrenWidth={100}
childrenHeight={50}
marginChildrenTop={5}
marginChildrenBottom={5}
marginChildrenLeft={5}
marginChildrenRight={5}
// onDataChange={setData}
keyExtractor={(item, index) => item.id}
onClickItem={(data, item, index) => {
console.log("点击了第", index, "个元素");
}}
onDragStart={() => console.log("Drag started")}
onDragEnd={() => console.log("Drag end")}
onDataChange={() => {
console.log("数据发生变化");
}}
fixedItems={[0, 1]}
delayLongPress={100}
isDragFreely={true}
maxScale={1.2}
minOpacity={0.7}
renderItem={(item, index) => {
return (
<View key={item.id} style={styles.box}>
<Text style={styles.text}>{item.title}</Text>
</View>
);
}}
sortable={true}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
box: {
// position:'relative',
// flexDirection: "column",
justifyContent: "center",
alignContent: "center",
borderRadius: 5,
margin: 20,
backgroundColor: "#4e71f2",
height: 50,
width: 100,
},
text: {
fontSize: 18,
color: "#fff",
textAlign: "center",
},
header: {
height: 48,
justifyContent: "center",
alignItems: "center",
borderBottomColor: "#2ecc71",
borderBottomWidth: 2,
},
header_title: {
color: "#333",
fontSize: 24,
fontWeight: "bold",
},
});
export default Dragsort;
AutoDragSortableView组件使用
//此案例Item1、Item2不支持拖拽
import React, { useState } from "react";
import { View, Text } from "react-native";
import { AutoDragSortableView } from "react-native-drag-sort";
const AutoDragSortDemo = () => {
const [data, setData] = useState([
{ id: "1", text: "Item 1" },
{ id: "2", text: "Item 2" },
{ id: "3", text: "Item 3" },
{ id: "4", text: "Item 4" },
{ id: "5", text: "Item 5" },
{ id: "6", text: "Item 6" },
{ id: "7", text: "Item 7" },
{ id: "8", text: "Item 8" },
{ id: "9", text: "Item 9" },
{ id: "10", text: "Item 10" },
{ id: "11", text: "Item 11" },
{ id: "12", text: "Item 12" },
{ id: "13", text: "Item 13" },
{ id: "14", text: "Item 14" },
{ id: "15", text: "Item 15" },
{ id: "16", text: "Item 16" },
{ id: "17", text: "Item 17" },
{ id: "18", text: "Item 18" },
{ id: "19", text: "Item 19" },
{ id: "20", text: "Item 20" },
{ id: "21", text: "Item 21" },
{ id: "22", text: "Item 22" },
{ id: "23", text: "Item 23" },
{ id: "24", text: "Item 24" },
{ id: "25", text: "Item 25" },
{ id: "26", text: "Item 26" },
{ id: "27", text: "Item 27" },
{ id: "28", text: "Item 28" },
]);
const renderHeaderView = (
<View style={{ height: 50, backgroundColor: "#ff4d4d" }}>
<Text style={{ fontSize: 18, color: "#fff", textAlign: "center" }}>
标题
</Text>
</View>
);
const renderBottomView = (
<View style={{ height: 50, backgroundColor: "#ff4d4d" }}>
<Text style={{ fontSize: 18, color: "#fff", textAlign: "center" }}>
底部
</Text>
</View>
);
return (
<AutoDragSortableView
dataSource={data}
parentWidth={500}
childrenWidth={100}
childrenHeight={100}
marginChildrenTop={20}
marginChildrenBottom={20}
marginChildrenLeft={20}
marginChildrenRight={20}
onDataChange={(data) => {
console.log("数据发生变化");
}}
keyExtractor={(item, index) => item.id}
onClickItem={(data, item, index) => {
console.log("点击了第", index, "个元素");
}}
renderItem={(item, index) => {
return (
<View
key={item.id}
style={{
width: 100,
height: 50,
borderRadius: 5,
margin: 5,
backgroundColor: "#4e71f2",
}}
>
<Text style={{ fontSize: 18, color: "#fff" }}>{item.text}</Text>
</View>
);
}}
scaleDuration={500} //拖拽项缩放效果的持续时间
slideDuration={200} //拖拽项滑动效果的持续时间
autoThrottle={100} //自动滑动到目的地的间隔时间
autoThrottleDuration={500} //自动滑动到目的地的持续时间
sortable={true}
isDragFreely={false}
fixedItems={[0, 1]}
delayLongPress={100}
onDragStart={() => console.log("Drag started")}
onDragEnd={() => console.log("Drag end")}
renderHeaderView={renderHeaderView}
headerViewHeight={50}
scrollIndicatorInsets={{ top: 0, left: 0, bottom: 0, right: 0 }}
renderBottomView={renderBottomView}
bottomViewHeight={50}
onScrollListener={(event) => {
console.log("滚动事件", event);
}}
onScrollRef={(ref) => {
console.log("滚动容器", ref);
}}
/>
);
};
export default AutoDragSortDemo;
AnySizeDragSortableView组件使用
import React, { createRef } from "react";
import {
Text,
TouchableOpacity,
StyleSheet,
View,
Image,
Dimensions,
SafeAreaView,
} from "react-native";
import { AnySizeDragSortableView } from "react-native-drag-sort";
const { width } = Dimensions.get("window");
const headerViewHeight = 160;
const bottomViewHeight = 40;
const getW = (index, isWidth) =>
isWidth ? (index % 3 === 0 ? width - 40 : (width - 60) / 2) : 80;
type State = {
items: { text: string, width: number, height: number }[];
movedKey: any
}
export default class AnySizeDragSortDemo extends React.Component<any, State> {
sortableViewRef: React.RefObject<any>;
constructor(props) {
super(props);
const items:State["items"] = [];
for (let i = 0; i < 26; i++) {
items.push({
text: String.fromCharCode(65 + i),
width: getW(i, true),
height: getW(i, false),
});
}
this.state = {
items,
movedKey: null,
};
this.sortableViewRef = createRef();
}
onDeleteItem = (item, index) => {
const items = [...this.state.items];
items.splice(index, 1);
this.setState({ items });
};
_renderItem = (item, index, isMoved) => {
const { movedKey } = this.state;
return (
<TouchableOpacity
onLongPress={() => {
this.setState({ movedKey: item.text });
this.sortableViewRef.current.startTouch(item, index);
}}
onPressOut={() => this.sortableViewRef.current.onPressOut()}
>
<View
style={[
styles.item_wrap,
{ opacity: movedKey === item.text && !isMoved ? 1 : 0.5 },
]}
>
{
<View style={styles.item_clear_wrap}>
<TouchableOpacity onPress={() => this.onDeleteItem(item, index)}>
<Image
source={require("../SourceLibraryDemo/data/img/clear.png")}
style={styles.item_clear}
/>
</TouchableOpacity>
</View>
}
<View
style={[
styles.item,
{
width: item.width,
height: item.height,
backgroundColor: isMoved ? "red" : "#f39c12",
},
]}
>
{isMoved ? (
<View style={styles.item_icon_swipe}>
<Image
source={{
uri: "https://img0.baidu.com/it/u=4232278629,1470839503&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=746",
}}
style={styles.item_icon}
/>
</View>
) : null}
<View style={styles.item_text_swipe}>
<Text style={styles.item_text}>{item.text}</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
};
render() {
const { items } = this.state;
const renderHeaderView = (
<View style={styles.aheader}>
<Image
source={{
uri: "https://p7.itc.cn/mpbp/pro/20200929/650b94a7b8a542fda242a6575f51d74f.jpeg",
}}
style={styles.aheader_img}
/>
<View style={styles.aheader_context}>
<Text style={styles.aheader_title}>mochixuan</Text>
<Text style={styles.aheader_desc}>
Android, React-Native, Flutter, React, Web。Learn new knowledge and
share new knowledge.
</Text>
</View>
</View>
);
const renderBottomView = (
<View style={styles.abottom}>
<Text style={styles.abottom_desc}>yarn add react-native-drag-sort</Text>
</View>
);
return (
<>
<View style={styles.header}>
<Text style={styles.header_title}>AnySize</Text>
</View>
<AnySizeDragSortableView
ref={this.sortableViewRef}
dataSource={items}
keyExtractor={(item) => item.text}
renderItem={this._renderItem}
onDataChange={(data, callback) => {
this.setState({ items: data }, () => {
callback();
console.log("移动了");
});
}}
renderHeaderView={renderHeaderView}
headerViewHeight={headerViewHeight}
renderBottomView={renderBottomView}
bottomViewHeight={bottomViewHeight}
movedWrapStyle={styles.item_moved}
onDragEnd={() => console.log("Drag end")}
scrollIndicatorInsets={{ top: 1, left: 1, bottom: 1, right: 1 }}
autoThrottle={100}
autoThrottleDuration={500}
areaOverlapRatio={0.5}
childMarginTop={10}
childMarginBottom={10}
childMarginLeft={10}
childMarginRight={10}
/>
</>
);
}
}
const styles = StyleSheet.create({
item_wrap: {
position: "relative",
paddingLeft: 20,
paddingTop: 20,
},
item: {
justifyContent: "space-around",
alignItems: "center",
backgroundColor: "#f39c12",
borderRadius: 4,
},
item_clear_wrap: {
position: "absolute",
left: 10,
top: 10,
width: 20,
height: 20,
zIndex: 999,
},
item_clear: {
width: 20,
height: 20,
},
item_moved: {
opacity: 0.95,
borderRadius: 4,
},
item_icon_swipe: {
width: 50,
height: 50,
backgroundColor: "#fff",
borderRadius: 50 * 0.5,
justifyContent: "center",
alignItems: "center",
},
item_icon: {
width: 30,
height: 30,
resizeMode: "contain",
},
item_text_swipe: {
backgroundColor: "#fff",
width: 56,
height: 30,
borderRadius: 15,
justifyContent: "center",
alignItems: "center",
},
item_text: {
color: "#444",
fontSize: 20,
fontWeight: "bold",
},
header: {
height: 48,
justifyContent: "center",
alignItems: "center",
borderBottomColor: "#2ecc71",
borderBottomWidth: 2,
},
header_title: {
color: "#333",
fontSize: 24,
fontWeight: "bold",
},
aheader: {
height: headerViewHeight,
flexDirection: "row",
borderBottomColor: "#2ecc71",
borderBottomWidth: 2,
zIndex: 100,
backgroundColor: "#fff",
},
aheader_img: {
width: headerViewHeight * 0.6,
height: headerViewHeight * 0.6,
resizeMode: "cover",
borderRadius: headerViewHeight * 0.3,
marginLeft: 16,
marginTop: 10,
},
aheader_context: {
marginLeft: 8,
height: headerViewHeight * 0.4,
marginTop: 10,
},
aheader_title: {
color: "#333",
fontSize: 20,
marginBottom: 10,
fontWeight: "bold",
},
aheader_desc: {
color: "#444",
fontSize: 16,
width: width - headerViewHeight * 0.6 - 32,
},
abottom: {
justifyContent: "center",
alignItems: "center",
height: bottomViewHeight,
backgroundColor: "#fff",
zIndex: 100,
borderTopColor: "#2ecc71",
borderTopWidth: 2,
},
abottom_desc: {
color: "#333",
fontSize: 20,
fontWeight: "bold",
},
});
约束与限制
兼容性
本文档内容基于以下版本验证通过:
- RNOH: 0.72.98; SDK: HarmonyOS-5.0.0(API12); IDE: DevEco Studio 5.0.3.906; ROM: NEXT.0.0.71;
- RNOH:0.77.18; SDK:HarmonyOS 6.0.0.47 (API Version 20); IDE:DevEco Studio 6.0.0.858; ROM:6.0.0.107;
- RNOH: 0.82.1; SDK: HarmonyOS-5.0.0(API12); IDE: DevEco Studio 6.0.1 Release; ROM:6.0.0.120 SP7
接口说明
DragSortableView
"Platform"列表示该属性在原三方库上支持的平台。
"HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。
| Name | Description | Type | Required | Platform | HarmonyOS Support |
|---|---|---|---|---|---|
| dataSource | 列表的数据源数组 | array | Yes | iOS/Android | Yes |
| parentWidth | 父容器宽度 | number | No | iOS/Android | Yes |
| childrenHeight | 每个子项的高度 | number | Yes | iOS/Android | Yes |
| childrenWidth | 每个子项的宽度 | number | Yes | iOS/Android | Yes |
| marginChildrenTop | 若要为子项的最外层视图添加边距,只能使用此方法。 | number | No | iOS/Android | Yes |
| marginChildrenBottom | 子项下边距 | number | No | iOS/Android | Yes |
| marginChildrenLeft | 子项左边距 | number | No | iOS/Android | Yes |
| marginChildrenRight | 子项右边距 | number | No | iOS/Android | Yes |
| sortable | 不允许拖拽 | bool | No | iOS/Android | Yes |
| onClickItem | 点击事件 | function | No | iOS/Android | Yes |
| onDragStart | 拖拽开始后调用 | function | No | iOS/Android | Yes |
| onDragEnd | 拖拽结束后调用 | function | No | iOS/Android | Yes |
| onDataChange | 每次数据变化时都会调用此方法。 | function | No | iOS/Android | Yes |
| renderItem | 渲染子项视图 | function | Yes | iOS/Android | Yes |
| fixedItems | 固定项(不可移除) | array | No | iOS/Android | Yes |
| keyExtractor | (item,index) => key | function | No | iOS/Android | Yes |
| delayLongPress | 长按延迟 | number | No | iOS/Android | Yes |
| isDragFreely | 是否限制拖拽空间 | bool | No | iOS/Android | Yes |
| onDragging | 拖拽时调用 | function | No | iOS/Android | Yes |
| maxScale | 最大缩放比例 | number | No | iOS/Android | Yes |
| minOpacity | 最小不透明度 | number | No | iOS/Android | Yes |
| scaleStatus | 缩放状态 | string | No | iOS/Android | Yes |
| scaleDuration | 缩放持续时间 | number | No | iOS/Android | Yes |
| slideDuration | 滑动持续时间 | number | No | iOS/Android | Yes |
AutoDragSortableView
| Name | Description | Type | Required | Platform | HarmonyOS Support |
|---|---|---|---|---|---|
| dataSource | 列表的数据源数组 | array | Yes | iOS/Android | Yes |
| parentWidth | 父容器宽度 | number | No | iOS/Android | Yes |
| childrenHeight | 每个子项的高度 | number | Yes | iOS/Android | Yes |
| childrenWidth | 每个子项的宽度 | number | Yes | iOS/Android | Yes |
| marginChildrenTop | 若要为子项的最外层视图添加边距,只能使用此方法。 | number | No | iOS/Android | Yes |
| marginChildrenBottom | 子项下边距 | number | No | iOS/Android | Yes |
| marginChildrenLeft | 子项左边距 | number | No | iOS/Android | Yes |
| marginChildrenRight | 子项右边距 | number | No | iOS/Android | Yes |
| sortable | 不允许拖拽 | bool | No | iOS/Android | Yes |
| onClickItem | 点击事件 | function | No | iOS/Android | Yes |
| onDragStart | 拖拽开始后调用 | function | No | iOS/Android | Yes |
| onDragEnd | 拖拽结束后调用 | function | No | iOS/Android | Yes |
| onDataChange | 每次数据变化时都会调用此方法。 | function | No | iOS/Android | Yes |
| renderItem | 渲染子项视图 | function | Yes | iOS/Android | Yes |
| fixedItems | 固定项(不可移除) | array | No | iOS/Android | Yes |
| keyExtractor | (item,index) => key | function | No | iOS/Android | Yes |
| delayLongPress | 长按延迟 | number | No | iOS/Android | Yes |
| isDragFreely | 是否限制拖拽空间 | bool | No | iOS/Android | Yes |
| onDragging | 拖拽时调用 | function | No | iOS/Android | Yes |
| maxScale | 最大缩放比例 | number | No | iOS/Android | Yes |
| minOpacity | 最小不透明度 | number | No | iOS/Android | Yes |
| scaleStatus | 缩放状态 | string | No | iOS/Android | Yes |
| scaleDuration | 缩放持续时间 | number | No | iOS/Android | Yes |
| slideDuration | 滑动持续时间 | number | No | iOS/Android | Yes |
| autoThrottle | 自动节流 | number | No | iOS/Android | Yes |
| autoThrottleDuration | 自动节流持续时间 | number | No | iOS/Android | Yes |
| renderHeaderView | 渲染头部视图 | element | No | iOS/Android | Yes |
| headerViewHeight | 头部视图高度 | number | No | iOS/Android | Yes |
| scrollIndicatorInsets | 滚动条内边距 | ({top:number, left:number, bottom:number, right:number}) | No | iOS | NO |
| renderBottomView | 渲染底部视图 | element | No | iOS/Android | Yes |
| bottomViewHeight | 底部视图高度 | number | No | iOS/Android | Yes |
| onScrollListener | 滚动期间的监听回调 | (event: NativeSyntheticEvent) => void | No | iOS/Android | Yes |
| onScrollRef | 滚动引用回调 | (ref: any) => void | No | iOS/Android | Yes |
AnySizeDragSortableView
| Name | Description | Type | Required | Platform | HarmonyOS Support |
|---|---|---|---|---|---|
| renderItem | 渲染子项视图 | function | Yes | iOS/Android | Yes |
| onDataChange | 每次数据变化时都会调用此方法 | function | Yes | iOS/Android | Yes |
| renderHeaderView | 渲染头部视图 | element | No | iOS/Android | Yes |
| headerViewHeight | 头部视图高度 | number | No | iOS/Android | Yes |
| renderBottomView | 渲染底部视图 | element | No | iOS/Android | Yes |
| bottomViewHeight | 底部视图高度 | number | No | iOS/Android | Yes |
| autoThrottle | 自动节流 | number | No | iOS/Android | Yes |
| autoThrottleDuration | 自动节流持续时间 | number | No | iOS/Android | Yes |
| scrollIndicatorInsets | 滚动条内边距 | ({top:number, left:number, bottom:number, right:number}) | No | iOS/Android | No |
| onScrollListener | 滚动期间的监听回调 | (event: NativeSyntheticEvent) => void | No | iOS/Android | Yes |
| onScrollRef | 滚动引用回调 | (ref: any) => void | No | iOS/Android | Yes |
| areaOverlapRatio | 必须大于 0.5 | number | No | iOS/Android | Yes |
| movedWrapStyle | 样式 | StyleProp | Yes | iOS/Android | Yes |
| childMarginTop | 子项上边距 | number | No | iOS/Android | Yes |
| childMarginBottom | 子项下边距 | number | No | iOS/Android | Yes |
| childMarginLeft | 子项左边距 | number | No | iOS/Android | Yes |
| childMarginRight | 子项右边距 | number | No | iOS/Android | Yes |
| onDragEnd | 拖拽结束后调用 | function | Yes | iOS/Android | Yes |
| dataSource | 列表的数据源数组 | array | Yes | iOS/Android | Yes |
| keyExtractor | 项目的唯一键 | (item,index) => key | Yes | iOS/Android | Yes |
遗留问题
其他
无
目录结构
/react-native-drag-sort # 项目根目录
├── lib # 库源码目录(纯 JS 实现,无原生代码)
│ ├── index.js # 入口文件,统一导出三个拖拽排序组件
│ ├── DragSortableView.js # 基础拖拽排序组件(等尺寸网格布局)
│ ├── AutoDragSortableView.js # 内置 ScrollView 的拖拽排序组件(支持拖动到边缘自动滚动)
│ ├── AnySizeDragSortableView.js # 支持任意尺寸 item 的拖拽排序组件
│ ├── index.d.ts # TypeScript 类型声明
│ └── harmony # 鸿蒙适配目录(纯 JS 库无需原生适配,当前为占位)
├── example # 示例工程
│ ├── src/index.tsx # RN 侧示例代码(DragSortableView 用法演示)
│ └── harmony # OpenHarmony 示例工程(基于 RNOH 框架)
│ └── entry/src/main
│ ├── cpp # C++ 桥接代码(RNOH 工程模板)
│ └── ets # ArkTS 页面与 Ability(加载 RN 示例页)
├── img # README 演示图片
├── README.md # 中文说明文档
├── README_en.md # 英文说明文档
├── CHANGELOG.md # 版本更新记录
├── buildEnv.sh # tgz / har 打包脚本
└── LICENSE # 开源协议
贡献代码
使用过程中发现任何问题都可以提交 Issue,当然,也非常欢迎提交 PR 。
开源协议
本项目基于 The Apache License (Apache) ,请自由地享受和参与开源。