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;
效果图
支持点击和滑动选择当前的字母