react native 实现拖拽排序

2023-11-08

先上效果图,意思意思。其实原理很简单,没有想的那么难。

大家在改造的时候,请注意 this.offset 的值,因为它关系到查找目标box的index(原理:手势释放时,所在的坐标值来推算出目标box的Index),本文代码可读性还需要改造,代码写的有点乱。

借鉴了:
https://blog.csdn.net/nfq6612/article/details/78675515( 原文中有几处bug,运行不出来他说的效果)




'use strict';


import React, { Component } from 'react';

import {
    Image,
    StyleSheet,
    LayoutAnimation,
    Text,
    TouchableHighlight,
    TouchableOpacity,
    TouchableWithoutFeedback,
    Dimensions,
    PanResponder,
    View,
    Alert,
    NativeModules,
    YellowBox
} from 'react-native';
import tools from "../../common/tools";
import {Icon} from 'native-base';


//获取屏幕宽高
let { width, height } = Dimensions.get('window');
let boxWidth = (width - 10) / 4 - 10;
let boxHeight = (width - 10) / 8 - 10;
const { UIManager } = NativeModules;


UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated', 'Module RCTImageLoader']);


export default class CustomTab extends Component {


    static navigationOptions = ({navigation})=>({
        headerTitle: '栏目设置',
        gesturesEnabled:false,
        headerRight: <View />
    });


    constructor(props) {
        super(props);
        let {items,sourceItems} = this.props;
        this._width = width / 4;
        this.topIndex = 0;
        this.leftIndex = 0;
        this.index = 0;
        this.finalTopIndex = 0;
        this.finalLeftIndex = 0;
        this.finalIndex = 0;
        this.prev_left = 0;
        this.prev_top = 0;
        this.left = 0;
        this.top = 0;


        this.barHeight=40;//qzwang
        this._marginTop=10;//qzwang
        this._borderTopWidth=1;//qzwang
        this.offset = this.barHeight + this._marginTop + this._borderTopWidth; //偏移


        //this.boxsPlanHeight =  (this._width / 2) +  boxHeight;
this.items = [{
                id: 0,
                name: '首页',
                ordernum: 0
            },{
                id: 1,
                name: '成交价',
                ordernum: 0
            },{
                id: 2,
                name: '库存',
                ordernum: 0
            },{
                id: 3,
                name: '快讯',
                ordernum: 0
            },{
                id: 4,
                name: '分析',
                ordernum: 0
            },{
                id: 5,
                name: '视频',
                ordernum: 0
            },{
                id: 6,
                name: '钢厂',
                ordernum: 0
            }
        ];
        this.sourceItems=[{
                id: 7,
                name: '期货',
                ordernum: 0
            },{
                id: 8,
                name: '设置',
                ordernum: 0
            },{
                id: 9,
                name: '内容',
                ordernum: 0
            }

        ];

        this.state = {};
    }
    componentWillReceiveProps(nextProps){
    }


    componentWillMount() {
        this._panResponder = PanResponder.create({
            onStartShouldSetPanResponder: (evt, gestureState) => {
                return gestureState.dx !== 0 || gestureState.dy !== 0;
            },
            onMoveShouldSetPanResponder: (evt, gestureState) => true,
            onPanResponderGrant: (evt, gestureState) => {
                const { pageX, pageY } = evt.nativeEvent;
                this.topIndex = Math.floor((pageY - this.offset) / (this._width / 2)) - 1;//qzwang 这里应该减法减1
                this.leftIndex = Math.floor(pageX / this._width);
                this.index = this.topIndex * 4 + this.leftIndex;
                this.prev_left = this._width * this.leftIndex;
                this.prev_top = this._width / 2 * this.topIndex;


            },
            onPanResponderMove: (evt, gestureState) => {
                if (this.index >= 0 && this.index < this.items.length ) {
                    this.left = this.prev_left + gestureState.dx;
                    this.top = this.prev_top + gestureState.dy;
                    let box = this.refs[this.items[this.index].id];
                    box.setNativeProps({
                        style: { top: this.top, left: this.left }
                    });
                }
            },
            onPanResponderTerminationRequest: (evt, gestureState) => true,
            onPanResponderRelease: (evt, gestureState) => {
                if(this.index <0 || this.index >= this.items.length){
                    this.forceUpdate();
                    return;
                }
                    const { pageX, pageY } = evt.nativeEvent;
                    this.finalTopIndex = Math.floor((pageY - this.offset) / (this._width / 2)) -1;
                    this.finalLeftIndex = Math.floor(pageX / this._width);
                    let index = this.finalTopIndex * 4 + this.finalLeftIndex;
                    this.prev_left = this._width * this.finalTopIndex;
                    this.prev_top = this._width / 2 * this.finalTopIndex;


                    if (index >= 0 && index < this.items.length && this.items[index]) {
                        if (index > this.index) {
                            this.moveBack(this.index,index);
                        } else if (index < this.index) {
                            this.moveForward(this.index,index);
                        }else{
                            this.delteBox(index);//删除
                        }
                    } else {
                        //移出范围,则重新回到原始位置
                        let box1 = this.refs[this.items[this.index].id];
                        let top1 = Math.floor(this.index / 4) * (this._width / 2);
                        let left1 = (this.index % 4) * this._width;
                        LayoutAnimation.linear();


                        box1.setNativeProps({
                            style: {
                                top: top1,
                                left: left1,
                                zIndex:9999
                            }
                        });
                        LayoutAnimation.configureNext(LayoutAnimation.Presets.spring); //系统自带
                    }
            },
            onShouldBlockNativeResponder: (event, gestureState) => true
        });
    }


    moveForward = function(sourceIndex,targetIndex){
        //往前移动
        for (let i = sourceIndex; i > targetIndex; i--) {
            let box2 = this.refs[this.items[i - 1].id];
            let top2 = Math.floor(i / 4) * (this._width / 2);
            let left2 = (i % 4) * this._width;
            //LayoutAnimation.linear();
            LayoutAnimation.configureNext(
                LayoutAnimation.create(
                    200,
                    LayoutAnimation.Types.linear,
                    LayoutAnimation.Properties.scaleXY
                )
            );
            box2.setNativeProps({
                style: {
                    top: top2,
                    left: left2
                }
            });
        }
        let box1 = this.refs[this.items[sourceIndex].id];
        let top1 = Math.floor(targetIndex / 4) * (this._width / 2);
        let left1 = (targetIndex % 4) * this._width;


        box1.setNativeProps({
            style: {
                top: top1,
                left: left1
            }
        });
        let temp = this.items[sourceIndex];
        for (let i = sourceIndex; i > targetIndex; i--) {
            this.items[i] = this.items[i - 1];
        }
        this.items[targetIndex] = temp;
    }
    moveBack = function(sourceIndex,targetIndex){
        for (let i = sourceIndex; i < targetIndex; i++) {
            let box2 = this.refs[this.items[i + 1].id];
            let top2 = Math.floor(i / 4) * (this._width / 2);
            let left2 = (i % 4) * this._width;
            //LayoutAnimation.linear();
            LayoutAnimation.configureNext(
                LayoutAnimation.create(
                    200,
                    LayoutAnimation.Types.linear,
                    LayoutAnimation.Properties.scaleXY
                )
            );
            box2.setNativeProps({
                style: {
                    top: top2,
                    left: left2
                }
            });
        }
        let box1 = this.refs[this.items[sourceIndex].id];
        let top1 = Math.floor(targetIndex / 4) * (this._width / 2);
        let left1 = (targetIndex % 4) * this._width;


        box1.setNativeProps({
            style: {
                top: top1,
                left: left1
            }
        });
        let temp = this.items[sourceIndex];
        for (let i = sourceIndex; i < targetIndex; i++) {
            this.items[i] = this.items[i + 1];
        }
        this.items[targetIndex] = temp;
    }
    delteBox = function(index){
        if(this.items.length==1)
        {
            tools.showToast("必须保留至少一个栏目");
            return;
        }
        if( index <0 || index >= this.items.length ){
            tools.showToast("应用出错");
            return;
        }
        //1、移除
        let item = this.items[index];
        this.items.removeItem(o=>o.id==item.id);
        //2、增加
        this.sourceItems.removeItem(o=>o.id==item.id);
        this.sourceItems.push(item);
        //3、重新刷新
        this.forceUpdate();
    }
    onAddTab=function(item){
        this.items.push(item);
        this.sourceItems.removeItem(o=>o.id==item.id);
        this.forceUpdate();
    }
    renderSource=function(){
        return this.sourceItems.map((item, index) => {
            let top =  Math.floor(index / 4) * (this._width / 2);
            let left = (index % 4) * this._width;


            return (
                <TouchableOpacity
                    onPress={()=>{ this.onAddTab(item) }}
                    key={'' + item.id}
                    style={[componentStyles.touchBox, { top, left }]}
                >
                    <View
                        style={{
                            alignItems: 'center',
                            justifyContent: 'center',
                            width: (width - 10) / 4 - 10,
                            height: (width - 10) / 8 - 10,
                            backgroundColor: '#f3f3f3',
                            borderWidth: 1,
                            borderRadius: 5,
                            borderColor: '#f3f3f3'}}>
                        <Text style={{color: '#5c5c5c'}}>{item.name}</Text>
                    </View>
                </TouchableOpacity>
            );
        });
    }
    renderBoxs=function(){
        return this.items.map((item, index) => {
            let top = Math.floor(index / 4) * (this._width / 2);
            let left = (index % 4) * this._width;
            return (
                <View
                    ref={'' + item.id}
                    {...this._panResponder.panHandlers}
                    key={'' + item.id}
                    style={[componentStyles.touchBox, { top, left }]}
                >
                    <TouchableOpacity onPress={()=>{ this.delteBox(item.id) }} style={componentStyles.closeButton}  underlayColor={'#f9f3f9'}>
                        <Icon name='ios-close' style={{ color:'#ffffff',textAlign:'center', fontSize:15 }} />
                    </TouchableOpacity>
                    <View
                        style={{
                            alignItems: 'center',
                            justifyContent: 'center',
                            width: (width - 10) / 4 - 10,
                            height: (width - 10) / 8 - 10,
                            backgroundColor: '#f3f3f3',
                            borderWidth: 1,
                            borderRadius: 5,
                            borderColor: '#f3f3f3'}}>
                        <Text style={{ color: index > 0 ? '#5c5c5c' : '#ff0000' }}>{item.name}</Text>
                    </View>
                </View>
            );
        });
    }
    render() {
        const boxes = this.renderBoxs();
        const sourceBoxs = this.renderSource();


        let {style,ref,...props} = this.props;
        if(!style){
            style={};
        }
        if(!(style instanceof Array)){
            style=[style];
        }


        return (
            <View style={[componentStyles.container,...style]}>
                <View style={[componentStyles.crossBar,{height: this.barHeight,borderBottomWidth:this._borderTopWidth}]}>
                    <Text>拖拽排序,轻触添加或删除栏目</Text>
                </View>
                <View style={[componentStyles.plan,{marginTop: this._marginTop}]}>
                    {boxes}
                </View>
                <View style={componentStyles.sourcePlan}>
                    <View style={[componentStyles.crossBar,{height: this.barHeight,borderBottomWidth:this._borderTopWidth}]}>
                        <Text>全部栏目</Text>
                    </View>
                    <View style={[componentStyles.sourceBoxs,{marginTop: this._marginTop}]}>
                        {sourceBoxs}
                    </View>
                </View>
            </View>
        );
    }
}


const componentStyles = StyleSheet.create({
    touchBox: {
        width: boxWidth,
        height: boxHeight,
        backgroundColor: '#fff',
        position: 'absolute',
        left: 0,
        top: 0,
        zIndex:888
    },
    container:{
        flex: 1,
        flexDirection: 'column',
        backgroundColor: '#fff'
    },
    crossBar:{
        flexDirection: 'row',
        backgroundColor: '#fff',
        paddingLeft:10,
        alignItems:'center',
        borderBottomColor:'#969696'
    },
    plan:{


        width: width,
        marginLeft: 10,
        marginBottom: 10,
        marginRight: 10
    },
    sourcePlan:{
        flex:1,
        top: 200,
    },
    sourceBoxs:{
        flexDirection: 'column',
        width: width,
        marginLeft: 10,
        marginBottom: 10,
        marginRight: 10,
        flex:1
    },
    closeButton:{
        height:20,
        width:20,
        backgroundColor:'#CDC9C9',
        borderRadius:10,
        position:'absolute',
        top:1,
        right:1,
        zIndex:9999999,
    },
    bottomBar:{
        height:50
    }
});
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

react native 实现拖拽排序 的相关文章

随机推荐

  • 2022年前端面试题整理,持续更新中

    端面试题整理 已同步到掘金 CSDN 掘金地址 https juejin cn post 7075332630417244173 CSDN 地址 https blog csdn net z1832729975 article details
  • 一种3D视频格式转换(H264 MVC至SBS / OU)方案

    本文尚处于草稿状态 提前公开仅供预览 前言 两年前我就想写这个话题的文章 但一直拖延到现在 因为我在等待SkyBox VR Player支持3D MVC 我在想 如果3D播放器已经支持播放3D MVC格式 那么MVC至SBS转换就没有必要
  • spring中配置DButil数据源

    1 pom引入
  • web服务器群集-Nginx

    关于Nginx 一款高性能 轻量级Web服务软件 系统资源能耗低 对HTTP并发连接的处理能力高 单台物理服务器可支持3000 5000个并发请求 1 Nginx编译安装 安装支持软件 yum y install pcre devel zl
  • Maven打包时出现Process terminated错误

    Maven打包时出现Process terminated错误 检查maven的配置文件 多引入了一次控制器 编码错误 切点表达式错误 用maven打包时出现Process terminated样式的错误 报错如下 查看报错信息 检查mave
  • HDS 多路径软件HDLM for AIX安装及配置—精简范例篇

    HDS 多路径软件HDLM for AIX安装及配置 精简范例篇 aix 6 HDS VSP HDLM DLManager mpio rte HDLM Hitachi Dynamic Link Manager 是HDS公司提供的安装在主机端
  • Spring Data JPA学习笔记

    文章目录 Spring Data JPA 环境搭建 基本CRUD 分页 排序 基于规则自定义方法 基于Query注解方法 Spring Data JPA JPA字面意思是JAVA持久层API JPA定义了一系列标准 建立了实体类和数据库中表
  • CentOS7安装使用中文字符集的方法(转)

    通常安装Linux系统本着最简化安装 会默认使用英文字符集 不会安装中文字符集等其他字符 但是在一些必要情况下需要中文的支持 本文将以CentOS7为例演示下如何安装中文字符集 1 首先使用locale命令看看当前系统所使用的字符集 如图可
  • C语言 合并有序数组

    将2个已知有序的数组合并为一个新的有序数组 已有两个数组 arr1 和 arr2 要求将两个数组中元素合并到数组 arr3 中 合并时要去除数组中的重复数据 include
  • NLP学习(十四)-NLP实战之文本分类-中文垃圾邮件分类-Python3

    一 文本分类实现步骤 定义阶段 定义数据以及分类体系 具体分为哪些类别 需要哪些数据 数据预处理 对文档做分词 去停用词等准备工作 数据提取特征 对文档矩阵进行降维 提取训练集中最有用的特征 模型训练阶段 选择具体的分类模型以及算法 训练出
  • requests爬取网易云音乐

    访问网易云音乐 查找搜索接口的信息 发现搜索的接口没有想要的信息 我又去找其他接口 最后发现信息在这里 而且请求的接口是post的请求头 我换了种方式爬取 用selnium的方式 最后的成果是这样的 不打了 手累 不懂得可以问我 impor
  • 设置激光驱动器电流

    激光二极管在驱动电流过大的情况下较容易损坏 所以在调整激光驱动电路时 用测试负载来代替激光二极管 测试负载与激光二极管类似 但不像激光二极管会被过量的电流损坏 当我们将驱动电流设置到合适之后 测试负载便可以用激光二极管代替 测试负载 测试负
  • Unity3D 旋转天空盒的方法

    天空盒是不能旋转的 但我们可以旋转摄像机来达到天空盒的旋转效果 实现方法如下 1 我们创建一个摄像机名为Skybox Camera 2 主摄像机Main Camera的Clear Flags设置为Don t Clear 3 Skybox C
  • 【离线文本转语音文件】java spring boot jacob实现文字转语音文件,离线文本转化语音,中英文生成语音,文字朗读,中文生成声音,文字生成声音文件,文字转语音文件,文字变声音。

    1 实现效果如下 输入文字 支持中英文 点击转换生成 wav文件 点击下载到本地就可 生成后的音频文件播放 时长1分8秒 2 实现代码 这次采用jacob实现 相比百度AI需要联网 本项目定位内网环境实现 所以最终采jacob 1 环境配置
  • LaTeX数学公式的符号表示

    引言 由于CSDN的Markdown编辑器能轻松地支持 LATEX LaTeX的公式表示 因此 今天我们来细数一下 LATEX LaTeX数学公式的符号表示 以便大家以后随时查用 1 强调模式 a a hat a check a a a a
  • Keywords Search 【HDU - 2222】【AC自动机模板】

    题目链接 学习AC自动机的第一道题 可能跟广大学友是一样的 让我知道了什么是AC自动机 具体讲一下吧 它就是用来求多串匹配的 而KMP只是求单串匹配的 相当于是在KMP上做了优化 之后 就是怎么构造AC自动机了 知道它就是在一棵字典树上做文
  • 最大连续子数组和

    最大连续子数组和 动态规划 迭代求出以i结尾的最大连续和 i 1结尾的最大连续和时和前一位的状态有关 列出递推式即可求解 class Solution public int FindGreatestSumOfSubArray vector
  • PHP多个三目运算符的坑

    废话不多说直接上代码 source gt item source self 自建 item source erp 精选 item source free 免费 大牌好货 有一天看到项目里面的代码 这样写的 多个三目运算符一起限定 项目一直在
  • ChatGPT将要颠覆的前十个行业

    ChatGPT将要颠覆的前十个行业 内容创作 ChatGPT可以生成高质量的文章 新闻和其他类型的文本内容 改变传统内容创作行业 在线客服 ChatGPT可以提供智能 高效的客户服务 改善用户体验 降低企业成本 教育领域 ChatGPT可作
  • react native 实现拖拽排序

    先上效果图 意思意思 其实原理很简单 没有想的那么难 大家在改造的时候 请注意 this offset 的值 因为它关系到查找目标box的index 原理 手势释放时 所在的坐标值来推算出目标box的Index 本文代码可读性还需要改造 代