RN仿微信通讯录侧边栏滑动选择效果

import React, {useRef, useEffect} from 'react';
import {Dimensions, StyleSheet, View, Text, SafeAreaView} from 'react-native';
// 获取屏幕宽高
let ScreenWidth = Dimensions.get('window').width;
let ScreenHeight = Dimensions.get('window').height;
// 始终返回true的函数,用于设置触摸响应
const returnTrue = () => true;
const TestScreen = () => {
  const alphabet = [
    'A',
    'B',
    'C',
    'D',
    'E',
    'F',
    'G',
    'H',
    'I',
    'J',
    'K',
    'L',
    'M',
    'N',
    'O',
    'P',
    'Q',
    'R',
    'S',
    'T',
    'U',
    'V',
    'W',
    'X',
    'Y',
    'Z',
  ];
  // 创建ref来存储select_null和measure的值
  const selectNull = useRef(null);
  const measure = useRef({});
  // 创建ref来引用sectionItem的view
  const sectionItemRef = useRef(null);
  // 修复sectionItem的尺寸测量
  const fixSectionItemMeasure = () => {
    const sectionItem = sectionItemRef.current;
    if (!sectionItem) {
      return;
    }
    setTimeout(() => {
      sectionItem.measure((x, y, width, height, pageX, pageY) => {
        // 存储测量的数据
        measure.current = {
          y: pageY,
          width,
          height,
        };
      });
    }, 0);
  };

  // 根据触摸点定位字母
  const getZM = (topHeight, currentHeight) => {
    const navItemHeight = 26; // 字母栏的高度(要在样式里面也设置高度,这里的高度是根据样式动态改变的,字母栏样式的高是多少,这里就写多少)
    // 计算触摸点在导航中的索引
    const indexNav = Math.ceil((currentHeight - topHeight) / navItemHeight) - 1; // ((当前距离屏幕距离-父容器距离屏幕距离) / 元素高度) - 1 = (当前的索引元素距离父容器顶部距离 / 元素高度) - 1 = 第n个字母 - 1 = 当前字母的索引值
    if (alphabet[indexNav]) {
      console.log(alphabet[indexNav]); // 在当前触摸点上面
      return alphabet[indexNav]; // 返回当前字母
    } else {
      console.log('null'); // 不在触摸点打印下null
    }
  };

  // 处理触摸事件,该事件在用户触摸屏幕时调用
  const selectNav = e => {
    const ev = e.nativeEvent.touches[0]; // 获取多个触摸事件的第一个。比如你放了三根手指在屏幕上,他只算第一个放到屏幕上的
    const targetY = ev.pageY; // 动态获取屏幕上触摸点垂直方向的距离
    const localY = ev.locationY; // 第一次触摸到屏幕上距离顶部的距离
    const {y, width, height} = measure.current; // 获取当前的容器距离顶部的距离(这里因为父容器是top:20,所以这里获取的是20)
    console.log('y', y, targetY);
    // 调用getZM函数进行字母定位
    getZM(y, targetY); // 定位字母,并且触发相应的事件
  };

  // 在组件挂载时修复尺寸,该方法在组件第一次渲染后执行
  useEffect(() => {
    fixSectionItemMeasure();
  }, []);

  // 在组件更新时也修复尺寸,该方法在组件更新后执行
  useEffect(() => {
    fixSectionItemMeasure();
  });

  // 渲染函数组件
  return (
    <SafeAreaView>
      <View
        style={{
          width: ScreenWidth,
          height: ScreenHeight,
          position: 'relative',
        }}>
        <View
          ref={sectionItemRef} // 将当前 View 的引用存储到 sectionItemRef 中,以便后续可以通过 ref 访问它
          style={styles.container} // 设置 View 的样式,使用前面定义的 styles.container 样式
          onStartShouldSetResponder={returnTrue} // 定义当用户开始触摸屏幕时是否应该成为响应者的条件
          onMoveShouldSetResponder={returnTrue} // 定义当用户移动手指时是否应该成为响应者的条件
          onResponderGrant={selectNav} // 当响应器被激活时(即用户开始触摸屏幕)调用的函数
          onResponderMove={selectNav} // 当响应器在激活状态下移动时调用的函数
          onResponderRelease={selectNull.current} // 当响应器被释放(即用户停止触摸屏幕)时调用的函数
        >
          {alphabet.map((item, index) => {
            return (
              <View style={styles.oit} key={index}>
                <Text style={{lineHeight: 26, textAlign: 'center'}}>
                  {item}
                </Text>
              </View>
            );
          })}
        </View>
      </View>
    </SafeAreaView>
  );
};

// 样式定义
const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    top: 20,
    right: 0,
    width: 80,
    height: 676,  // 26*26 26个单词,每个单词26高度
    backgroundColor: 'pink',
  },
  oit: {
    flexShrink: 0,
    height: 26, // 高度自己慢慢调整(如果使用了flex布局,一定要看好自己的高度是否被正确设置	)
    width: '100%',
  },
});

export default TestScreen;

效果图

在这里插入图片描述

支持点击和滑动选择当前的字母

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萧寂173

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值