react-native仿微信通讯录右侧边栏快速定位功能

代码地址 -> ReactNativeCountrySelect

1. 界面

SectionList把数据渲染出来

右边的A-ZText组件即可,这里为了做滑动定位,没有选择Touchable组件

import * as React from 'react';
import {
  Text,
  View,
  StyleSheet,
  SectionList,
  SafeAreaView,
} from 'react-native';

import countries from './countryCode.json';
const sectionMapArr = [
  ['A', 0],
  ['B', 1],
  ['C', 2],
  ['D', 3],
  ['E', 4],
  ['F', 5],
  ['G', 6],
  ['H', 7],
  ['I', 8],
  ['J', 9],
  ['K', 10],
  ['L', 11],
  ['M', 12],
  ['N', 13],
  ['O', 14],
  ['P', 15],
  ['Q', 16],
  ['R', 17],
  ['S', 18],
  ['T', 19],
  ['U', 20],
  ['V', 21],
  ['W', 22],
  ['X', 23],
  ['Y', 24],
  ['Z', 25],
];
export default class App extends React.Component {
  render() {
    return (
      <SafeAreaView style={styles.container}>
        <SectionList
          containerStyle={{ flex: 1, justifyContent: 'center' }}
          ItemSeparatorComponent={() => (<View style={{ borderBottomColor: '#F8F8F8', borderBottomWidth: 1, }} />)}
          renderItem={({ item, index, section }) => (
            <View style={styles.itemContainer}>
              <Text style={styles.itemText} key={index}>
                {item.countryName}
              </Text>
            </View>
          )}
          renderSectionHeader={({ section: { key } }) => (
            <View style={styles.headerContainer}>
              <Text style={styles.headerText}>{key}</Text>
            </View>
          )}
          sections={countries}
          keyExtractor={(item, index) => item + index}
        />
        <View
          style={{ width: 16, justifyContent: 'center' }}
        >
          {sectionMapArr.map((item, index) => {
            return (
              <Text
                key={index}
              >
                {item[0]}
              </Text>
            );
          })}
        </View>
      </SafeAreaView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#FFFFFF',
    flexDirection: 'row',
  },
  headerContainer: {
    padding: 5,
    backgroundColor: '#F8F8F8',
  },
  headerText: {
    fontWeight: 'bold',
  },
  itemContainer: {
    paddingHorizontal: 5,
    paddingVertical: 10,
  },
  itemText: {
    fontSize: 16,
  },
});

这时候界面已经完成了,然后就是增加触摸滑动定位的功能了。

2. 使用Gesture Responder System监听触摸事件

给右侧的View上启用手势


<View style={{ width: 16, justifyContent: 'center' }} onStartShouldSetResponder={() => true} onMoveShouldSetResponder={() => true} > {sectionMapArr.map((item, index) => { return ( <Text key={index} > {item[0]} </Text> ); })} </View>

这样我们就可以在触摸滑动的时候,获得滑动到的位置

3. 使用onLayout找到每个字母对应的X、Y

首先在constructor里声明一个实例属性,用来记录每个字母的信息:

this.ps = [];

然后在Text组件上利用onLayout获得每个字母的位置,并且存到this.ps里:

<Text
  key={index}
  onLayout={({
    nativeEvent: {
      layout: { x, y, width, height },
    },
  }) => {
    this.ps = this.ps.filter(i => i.key !== item[0]); 
    this.ps.push({
      key: item[0],     // 对应的字母 A-Z
      min: y,           // 字母顶部Y坐标
      max: y + height,  // 字母底部Y坐标
      index: item[1],   // 字母对应SectionList的index
    });
  }}
>
  {item[0]}
</Text>

4. 根据滑动找到滑到哪个字母上

<View
  style={{ width: 16, justifyContent: 'center' }}
  onStartShouldSetResponder={() => true}
  onMoveShouldSetResponder={() => true}
  onResponderMove={({ nativeEvent: { pageY } }) => {
    const offsetY = pageY - this.offsetY;
    const find = this.ps.find(
      i => i.min < offsetY && i.max > offsetY
    );
    if (find) {
      console.log(find) // 滑动到的字母
    }
  }}
>

5. 根据触摸的字母,SectionList跳到对应的位置

  1. 先在constructor里创建ref
this.sectionlist = React.createRef();
  1. 在SectionList上绑定ref
ref={this.sectionlist}
  1. 调用SectionList的scrollToLocation
onResponderMove={({ nativeEvent: { pageY } }) => {
  const offsetY = pageY - this.offsetY;
  const find = this.ps.find(
    i => i.min < offsetY && i.max > offsetY
  );
  if (find) {
    this.sectionlist.current.scrollToLocation({
      sectionIndex: find.index,
      itemIndex: 0,
      animated: false,
    });
  }
}}

完工

react-native仿微信通讯录右侧边栏快速定位功能》有1个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注