RN通过PanResponder 实现单元格拖拽

什么是PanResponder

  • PanResponder 是 React Native 实现的一套手势相应方法。PanResponder 类可以将多点触摸操作协调成一个手势。它将单点触控手势能够适应额外的触摸,并可用于识别简单的多点触控手势。
  • 默认情况下,PanResponder 会通过 InteractionManager来阻止长时间运行的JS时间打断当前的手势活动。
  • PanResponder提供一个对触摸响应系统的可预测的包装。
  • 官网介绍地址:PanResponder

实现cell的可拖动

  1. 创建 class DragCell extends Component{},绘制简单的两行四列的单元格用实现cell拖拽功能

    const cells = this.state.cells.map((elem , index)=>{
            let top = Math.floor(index /4)* this._width ; 
            let left = (index % 4) * this._width;
            // 单个 cell 布局 ,手势绑定
            return(
                <View ref={"cell"+index} 
                    {...this._panResponder.panHandlers} 
                    key={elem.key}
                    style={[styles.touchCell,{top,left}]} 
                    underlayColor="#eee">
                    <View style={styles.cellContainer}>
                        <Image style={styles.cellIcon} source={elem.icon}></Image>
                        <Text style={styles.cellTitle}>{elem.title}</Text>
                    </View>
                </View>
            )
        })
    
  2. 在 constructor() 定义需要使用的变量和数据

       this._width = width/4;     //单元格宽度
       this.topIndex = 0;         //开始点击所在行
       this.leftIndex = 0;        //开始点击所在列
       this.index = 0;            //开始所选cell在数组中的下标
       this.finalTopIndex = 0;    //拖拽结束所在的行
       this.finalLeftIndex = 0;   //拖拽之后所在的列
       this.finalIndex = 0;       //拖拽之后cell在数组中的下标
       this.prev_left = 0;        //cell相对父组件距离left距离
       this.prev_top = 0;         //cell相对父组件距离top距离
       this.left = 0;             //拖拽之后 cell 距离 left 的距离
       this.top = 0;              //拖拽之后 cell 距离 top 的距离
       this.state = {
         selected : 7,
         // cell数据
         cells:[{
             key:0,
             title:"A aaaa",
             icon:require('./images/day1.png'),
             size:48,
             bgColor:"#ff856c",
             hideNav:false,
         },{...}]
     }
    
  3. 在componentWillMount()中添加 PanResponder ,并根据 PanResponder 的回调更新布局和数据。

  4. 通过onPanResponderGrant(e, gestureState) => {...} 获取即将被拖拽的cell的坐标,根据坐标获取当前单元格的句柄

         onPanResponderGrant:(evt,gestureState) => {
         const {pageX,pageY} = evt.nativeEvent;
         this.topIndex = Math.floor((pageY - width*0.3) / this._width);  // 根据坐标Y获取当前所在的行
         this.leftIndex = Math.floor((pageX) / this._width);             // 根据坐标X获取当前所在的列    
         this.index = this.topIndex *4 + this.leftIndex;                 // 根据每行显示的cell个数,已经行,列,计算出cell所在数组的下标
         this.prev_left = this._width * this.leftIndex;                  // cell 距离 left 的距离
         this.prev_top = this._width * this.topIndex;                    // cell 距离 top 的距离
         this.setState({
             selected :  this.index     // 修改 state 的值
         });
         let oneCell = this.refs["cell"+this.index];
    

5.通过onPanResponderMove(e, gestureState) => {...} 计算移动的距离,根据距离计算判断是否需要更新数据,调用_endMove()方法 , 移动的距离符合更新条件就对数组更新,通过setState数组刷新单元格列表。

    _endMove(evt,gestureState){
        this.finalTopIndex  = Math.floor(this.top / this._width + 0.5);        // 拖拽之后计算所在 列
        this.finalLeftIndex = Math.floor(this.left /this._width + 0.5);        // 拖拽之后计算所在 行
        //判断是否需要移动
        if((-1 < this.finalTopIndex) && (this.finalTopIndex <5) && (-1 < this.finalLeftIndex) && this.finalLeftIndex <4){
            this.finalIndex = this.finalTopIndex * 4 +this.finalLeftIndex ;
            // 数组数据替换
            let cells = this.state.cells;
            let moveCell =  cells[this.index];
            cells.splice(this.index ,1);
            cells.splice(this.finalIndex , 0 ,moveCell) ; 
            this.setState({
                cells
            })
            if(this.finalIndex != this.index){
                this.index =  this.finalIndex;
                this.setState({
                    selected : this.finalIndex,
                })
            }
            LayoutAnimation.configureNext(this.animations);
        }else{
            LayoutAnimation.configureNext(this.animations);
        }
    }
  1. 使用 Component

    render(){
            return(
                <View style={{flex:1}}>           
                    <DragCell/>
                </View>
            )
        } 
    

运行就可以实现简单的单元格拖拽了 ,效果如下:
dragCellDemo.gif

完整代码下载地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容