React Native 支付宝更多页面的实现

支付宝效果图
  • 实现的效果:
    • 页面向上滑动有吸顶的效果。
    • 便民服务,查询服务 ...所在的 tabBar 与页面下方对应的模块内容一一对应。
    • 页面滑动对应的 tabBartitle 居中偏移。
    • 编辑模式下:数据的 新增删除

  • 效果展示:
还原支付宝效果图

  • 实现吸顶的代码(噗~感觉这个没啥好写 ...... 很尴尬):
    —— 段子手是运用 ScrollView ==> onScroll ==> e.nativeEvent.contentOffset.y 获取页面滑动的当前坐标;
    然后和页面初次加载时 tabBar ==> onLayout ==> e.nativeEvent.layout.y 的值做比较获取状态。

     this.moveHeight = e.nativeEvent.contentOffset.y;
    
      if (this.moveHeight >= typeY) {
          this.setState({
               positionType: true,
          })
      } else {
          this.setState({
               positionType: false
           })
      }
    
  • 实现滑动模块和标题一一对应代码(这个有点篇幅):

    • 先说:点击标题,对应的模块自动置顶

      1. 首先初次加载时,我把 tabBar 下的各个模块的 y 坐标都存下来, push 到数组中,并且对应的模块下标 i 也存起来。
                  //保存 模块的下标和 y 轴
                  let params = {
                      key: i,
                      tmpY: e.nativeEvent.layout.y
                  };
      
                  //数组去重(判断命名的变量不建议用 type)
                  let typePis = tmpArr.some(v => v.key == params.key);
      
                  if (typePis) {
                      tmpArr.filter((v) => {
                          return v.key != params.key
                      })
                  } else {
                      tmpArr.push(params);
                  }
                  break;
      
      • 去重是因为吸顶的原因,会导致页面的 onLayout 重新加载。
      • 这里还有一个小坑:模块返回的坐标顺序并不是按页面展示顺序来的。
      1. 拿到模块的坐标数组 tmpArr 后,根据点击 tabBar 的下标 indextmpArrkey 去匹配,一致时取出 tmpArr 对应的 tmpY 值,根据该值去计算页面的偏移量。
         let y;
         tmpArr.map((v, i) => {
             if (v.key == index) {
                 y = v.tmpY;
             }
         });
      
         // 页面中模块的 y 轴移动 (typeY: tabBar 的坐标,
         // y:模块的坐标,头部    固定位置的搜索: autoHeight(45))
         this.refs.refMoveHeight.scrollTo({y: typeY + y - autoHeight(45)});
      
      1. 在点击 tabBar 最后的标题,模块的内容的高度不够去偏移到置顶的位置的处理(根据已有的内容高度,去自适应填充空白区域)。
        • 保存最后一个模块的 y
          // i:就是点击标题的下标;typeList:是标题数组。
          if (i == typeList.length - 1) {
                this.setState({
                     listCellHeight: e.nativeEvent.layout.y
               })
          }
        
        • 在最后的一个模块后面添加一个高度为 1View ,然后保存它的坐标 footHeight = e.nativeEvent.layout.y,接着再设置一个填充空白页面 View,高度为 屏幕全高 - (footHeight - listCellHeight - typeY + autoHeight(45))
    • 再说下滑动模块和 tabBar 的标题对应

      • 获取手势在屏幕上的滑动方向,把 <= 当前页面滑动高度的模块都塞选出来,然后取出最大的下标,然后和 tabBar 中的标题下标去匹配,一致则标题显示高亮。
        let maxValue = 0;
        if (this.moveHeight > e.nativeEvent.contentOffset.y) {
            tmpArr.map((v, i) => {
                  if (e.nativeEvent.contentOffset.y >= (typeY + v.tmpY -     autoHeight(45))) {
        
                      // console.log(i + '下下下');
                      //因为模块高度的下标不是按在页面中的位置返回的,所以和 tabBar 的下标并不能一一对应,所以要塞选出下标的最大值
                      if (tmpArr[i].key >= maxValue) {
                          maxValue = tmpArr[i].key
                      }
        
                      // console.log(maxValue + 'maxValue下=========');
                  }
              }
          )
        } else {
          tmpArr.map((v, i) => {
                  if (e.nativeEvent.contentOffset.y >= (typeY + v.tmpY - autoHeight(45))) {
                      // console.log(i + '下下下');
                      //因为模块高度的下标不是按在页面中的位置返回的,所以和 tabBar 的下标并不能一一对应,所以要塞选出下标的最大值
                      if (tmpArr[i].key >= maxValue) {
                          maxValue = tmpArr[i].key
                      }
        
                      // console.log(maxValue + 'maxValue上======');
                  }
              }
          )
        }
        
        this.state.currentIndex = maxValue;
        
  • 实现页面滑动对应的 tabBartitle 居中偏移代码

    • 获取屏幕 width 的宽度的一半。

        let widthHalf = 屏幕宽度 / 2;
      
    • 获取到 tabBar 中各个标题位置的 width,同时保存对应的挑剔位置的下标,数组为 tmpArrX

             //保存 tabBar 的下标和 width
              let param = {
                  index: i,
                  tmpX: e.nativeEvent.layout.width
              };
      
              //数组去重
              let tmpType = tmpArrX.some(v => v.index == param.index);
      
              if (tmpType) {
                  tmpArrX.filter((v) => {
                      return v.index != param.index
                  })
              } else {
                  tmpArrX.push(param);
              }
      
    • 拿到点击 tabBar 的下标,获取到这个下标之前的模块 width

       //获取 index 之前模块 width
      let widthX = 0;
      //获取选中的 width
      let indexWidth;
      
      for (let i = 0; i <= index; i++) {
          // console.log('i===' + i);
          tmpArrX.map((item, key) => {
              if (item.index == i) {
                  widthX += item.tmpX
              }
      
              if (index == item.index) {
                  indexWidth = item.tmpX
              }
          })
      }
      
    • 根据判断出来是不是 tabBar 中最后一个标题,然后拿 widthXindexWidth 然后去判断编写逻辑。

      let moveX;
      // index*20 是每个模块的空隙 20
      if (widthX + index * 20 > widthHalf && index != tmpArrX.length - 1) {
          moveX = (widthX - widthHalf + indexWidth) / 2
      } else if (index == tmpArrX.length - 1) {
          //index + 1 :间距比个数多一个; 10 :marGinLeft = 10
          moveX = (index + 1) * 20 + widthX - SCREEN_WIDTH + 10
      }
      
      // 页面中  tabBar 的 x 轴移动
      this.refs.moveX.scrollTo({x: autoWidth(moveX)})
      
  • 实现编辑模式下:数据的 新增删除 的代码(段子手快饿死了,写不动注释了 ......)

      //数组的加减
      _addOrDelete(keyType, data, i) {
          console.log('data===' + JSON.stringify(data) + '===' + i);
          //更改 severListType 的数据
          const {severListType, headList}=this.state;
          let tmpType = severListType.some(item => item.id === data.id);
    
          switch (keyType) {
              case 1:
                  // severListType.splice(i, 1);
                  if (tmpType) {
                      // tmpSeverId :用来存储选中的截取的 id
                      this.state.severListType = severListType.filter((item) => {
                          return item.id != data.id
                      });
                      //编辑模式下,是否为已有模块, true:加号,false:减号
                      data.select = !tmpType;
                  }
                  break;
              case 2:
              case 3:
                  if (!tmpType) {
                      if (severListType.length > 10) {
                          RootToast.show('首页最多添加 11 个应用')
                      } else {
                          severListType.push(data)
                      }
                  } else {
                      //获取输入的值和在另一个模块数组中的下标,然后删除
                      console.log('data1===' + JSON.stringify(data) + '===ss' + i);
                      console.log('severListType===' + JSON.stringify(severListType));
                      this.state.severListType = severListType.filter((item) => {
                          return item.id != data.id
                      });
                      data.select = !tmpType;
                  }
                  break;
          }
          //刷新数据
          this.setState({
              severListType: this.state.severListType
          }, () => {
              // console.log('severListType===' + JSON.stringify(this.state.severListType));
    
          });
      }
    

TIP:
  • 吸顶效果在 android 低配中会出现卡顿现象。
  • 页面滑动对应的 tabBartitle 居中偏移(我的写法还是有问题的,还有就是模块滑动时 tabBar 对应居中也会有卡顿)。
新增手势
  • TouchableHighlight 属性
名称 属性 注释
accessibilityComponentType View.AccessibilityComponentType 设置可访问的组件类型
accessibilityTraits View.AccessibilityTraits 设置访问特征
accessible bool 设置当前组件是否可以访问
delayLongPress View.AccessibilityTraits 设置当前组件是否可以访问
accessibilityTraits number 设置延迟的时间,单位为毫秒。从 onPressIn 方法开始,到 onLongPress 被调用这一段时间
delayPressIn number 设置延迟的时间,单位为毫秒,从用户触摸控件开始到 onPressIn 被调用这一段时间
delayPressOut number 设置延迟的时间,单位为毫秒,从用户触摸事件释放开始到 onPressOut 被调用这一段时间
onLayout function 当组件加载或者改组件的布局发生变化的时候调用。调用传入的参数为 {nativeEvent:{layout:{x,y,width,height}}}
onLongPress function 当用户长时间按压组件(长按效果)的时候调用该方法
onPress function 当用户点击的时候调用(触摸结束)。 但是如果事件被取消了就不会调用。(例如:当前被滑动事件所替代)
onPressIn function 用户开始触摸组件回调方法
onPressOut function 用户完成触摸组件之后回调方法
pressRetentionOffset {top: ,left: ,bottom: ,right: } 该设置当视图滚动禁用的情况下,可以定义当手指距离组件的距离。当大于该距离该组件会失去响应。当少于该距离的时候,该组件会重新进行响应。确保你传入一个常量来减少内存分配。

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

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,783评论 0 38
  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,340评论 0 2
  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些阅读 2,029评论 0 2
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile丽语阅读 3,830评论 0 6
  • “孩子,不要怕,叔叔带你去玩一会 儿”老男人猥琐的声音传入女孩的耳朵里, 女孩害怕的不知所措。 不一会儿,车停下了...
    盈盈子清阅读 623评论 1 3