RN仿微信通讯录的滑动导航组件

js 复制代码
import React, {useRef, useState} from 'react';
import {View, Text, SectionList, StyleSheet, PanResponder} from 'react-native';

const ContactList = () => {
  const sectionListRef = useRef(null);
  const alphabetRefs = useRef({});
  const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
  const [currentIndex, setCurrentIndex] = useState('');

  const sections = [
    {
      title: 'A',
      data: ['Alice', 'Amy', 'Alice', 'Amy', 'Alice', 'Amy', 'Alice', 'Amy'],
    },
    {title: 'B', data: ['Bob', 'Bill']},
    {title: 'C', data: ['Charlie', 'Chris']},
    {title: 'D', data: ['David']},
    {
      title: 'E',
      data: [
        'Emma',
        'Ella',
        'Emma',
        'Ella',
        'Emma',
        'Ella',
        'Emma',
        'Ella',
        'Emma',
        'Ella',
        'Emma',
        'Ella',
      ],
    },
    {
      title: 'F',
      data: [
        'Fmma',
        'Flla',
        'Fmma',
        'Flla',
        'Fmma',
        'Flla',
        'Fmma',
        'Flla',
        'Fmma',
        'Flla',
        'Fmma',
        'Flla',
      ],
    },
    {
      title: 'G',
      data: [
        'Gmma',
        'Glla',
        'Gmma',
        'Glla',
        'Gmma',
        'Glla',
        'Emma',
        'Glla',
        'Gmma',
        'Ella',
        'Gmma',
        'Glla',
      ],
    },
    {
      title: 'H',
      data: [
        'Hmma',
        'Hlla',
        'Hmma',
        'Hlla',
        'Hmma',
        'Hlla',
        'Hmma',
        'Hlla',
        'Hmma',
        'Hlla',
        'Hmma',
        'Hlla',
      ],
    },
    {
      title: 'I',
      data: [
        'Imma',
        'Illa',
        'Imma',
        'Illa',
        'Imma',
        'Illa',
        'Imma',
        'Illa',
        'Imma',
        'Illa',
        'Imma',
        'Illa',
      ],
    },
    {
      title: 'J',
      data: [
        'Jmma',
        'Jlla',
        'Jmma',
        'Jlla',
        'Jmma',
        'Jlla',
        'Jmma',
        'Jlla',
        'Jmma',
        'Jlla',
        'Jmma',
        'Jlla',
      ],
    },
  ];

  const renderItem = ({item}) => (
    <View style={styles.contactItem}>
      <Text>{item}</Text>
    </View>
  );

  const renderSectionHeader = ({section: {title}}) => (
    <Text style={styles.sectionHeader}>{title}</Text>
  );

  const handlePanResponderMove = (event, gestureState) => {
    let currentLetter = '';
    for (let letter in alphabetRefs.current) {
      if (
        gestureState.moveY >= alphabetRefs.current[letter].start &&
        gestureState.moveY < alphabetRefs.current[letter].end
      ) {
        currentLetter = letter;
        break;
      }
    }
    setCurrentIndex(currentLetter);
    handleScrollToSection(currentLetter);
  };

  const handleScrollToSection = index => {
    const sectionIndex = sections.findIndex(section => section.title === index);
    if (sectionIndex !== -1 && sectionListRef.current) {
      sectionListRef.current.scrollToLocation({
        sectionIndex,
        itemIndex: 0,
        animated: false,
      });
    }
  };

  const panResponder = PanResponder.create({
    onStartShouldSetPanResponder: () => true,
    onPanResponderMove: handlePanResponderMove,
  });

  return (
    <View style={styles.container}>
      {/* Index Navigation */}
      <View {...panResponder.panHandlers} style={styles.indexNav}>
        {alphabet.map(letter => (
          <View
            key={letter}
            onLayout={event => {
              const layout = event.nativeEvent.layout;
              alphabetRefs.current[letter] = {
                start: layout.y,
                end: layout.y + layout.height,
              };
            }}>
            <Text
              style={[
                styles.indexLetter,
                currentIndex === letter && {fontWeight: 'bold'},
              ]}>
              {letter}
            </Text>
          </View>
        ))}
      </View>

      {/* Contact List */}
      <SectionList
        ref={sectionListRef}
        sections={sections}
        renderItem={renderItem}
        renderSectionHeader={renderSectionHeader}
        keyExtractor={(item, index) => item + index}
        scrollEnabled={false}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
  },
  indexNav: {
    width: 30,
    backgroundColor: '#f0f0f0',
  },
  indexLetter: {
    textAlign: 'center',
    marginVertical: 5,
  },
  contactItem: {
    padding: 20,
    borderBottomWidth: 1,
    borderBottomColor: '#ccc',
  },
  sectionHeader: {
    padding: 10,
    backgroundColor: '#eee',
    fontWeight: 'bold',
  },
});

export default ContactList;

上面代码直接运行的效果图(支持滑动导航,或者自行手动滑动)

相关推荐
Smile_Gently2 小时前
前端:最简单封装nmp插件(组件)过程。
前端·javascript·vue.js·elementui·vue
luckycoke8 小时前
小程序立体轮播
前端·css·小程序
一 乐8 小时前
高校体育场管理系统系统|体育场管理系统小程序设计与实现(源码+数据库+文档)
前端·javascript·数据库·spring boot·高校体育馆系统
懒羊羊我小弟8 小时前
常用Webpack Loader汇总介绍
前端·webpack·node.js
祈澈菇凉9 小时前
ES6模块的异步加载是如何实现的?
前端·javascript·es6
我爱学习_zwj9 小时前
4.从零开始学会Vue--{{组件通信}}
前端·javascript·vue.js·笔记·前端框架
顾比魁9 小时前
XSS盲打:当攻击者“盲狙”管理员
前端·网络安全·xss
黑客老李9 小时前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
java·运维·服务器·前端·xss
晚风予星9 小时前
简记|LogicFlow自定义BPMN元素节点
前端
Json____10 小时前
使用html css js 开发一个 教育机构前端静态网站模板
前端·css·html·js·前端学习·企业站·教育机构网站