源码在此


先看一下预览图效果:

pic1.jpg

首先通过构造器初始化state

 constructor(props) {super(props);this.state = {//Global这里是全局变量loadingState: Global.loading,contactData: null,}
}

在render中根据state中的loadingState判断页面属于那种状态。

 render() {switch (this.state.loadingState) {case Global.loading:this.getContacts();return this.renderLoadingView();case Global.loadSuccess:return this.renderSuccessView();case Global.loadError:return this.renderErrorView();default:}
}

状态一:加载中

该状态会获取数据源并显示“正在获取联系人数据 . . . ”,以下数据源是通过fetch指令网络获取的接口拿到的数据,获取的数据也在下面json格式数据中展现。

getContacts() {var url = "http://app.yubo725.top/friends";fetch(url).then((res) => res.json()).then((json) => {// UserInfoUtil.setUserInfo(json);//添加存在本地操作this.setState({loadingState: Global.loadSuccess,contactData: json})})
}

顺便给出网络请求返回的json数据格式如下:

[{"pinyin": "heihei","nick": "这锅我不背","name": "heihei","avatar": "http://rnwechat.oss-cn-beijing.aliyuncs.com/c8c0fd30-32ef-490b-9d8e-553f0c46cdc3.jpg"},......
]

加载过程的页面渲染:

renderLoadingView() {return (<View style={styles.container}><View style={styles.sBar} backgroundColor={Global.titleBackgroundColor}/><TitleBar nav={this.props.navigation}/><View style={styles.content}><CommonLoadingView hintText={"正在获取联系人数据..."}/></View></View>);}

其中用到了两个自定义组件“TitleBar”和“CommonLoadingView”,代码也列出如下:

import React, {Component} from 'react';
import MenuPopWindow from '../common/PopupWindow';
import Global from '../utils/Global';import {StyleSheet,Text,View,Image,Dimensions,TouchableOpacity,Button,Platform
} from 'react-native';const {width, height} = Dimensions.get('window');export default class TitleBar extends Component {constructor(props) {super(props);this.state = {showPop: false,}}renderAndroid() {return (<View style={styles.titleBarContainer}><View style={styles.titleBarTextContainer}><Text style={styles.title}>人脉</Text></View><View style={styles.titleBarButtonContainer}><TouchableOpacity activeOpacity={0.5} onPress={this.handleSearchClick}><Imagesource={require('../img/ic_search.png')}style={styles.titleBarImg}/></TouchableOpacity><TouchableOpacity activeOpacity={0.5} onPress={this.handleAddClick}><Imagesource={require('../img/ic_add.png')}style={styles.titleBarImg}/></TouchableOpacity><View style={{position: 'absolute',top: 0,left: 0,width: width,height: height}}><MenuPopWindowwidth={140}height={200}show={this.state.showPop}closeModal={(show) => {this.setState({showPop: show})}}menuIcons={[require('../img/ic_pop_group_chat.png'), require('../img/ic_pop_add_friends.png'),require('../img/ic_pop_scan.png'), require('../img/ic_pop_help.png')]}menuTexts={['发起群聊', '添加朋友', '扫描名片', '帮助与反馈']}/></View></View></View>);}return this.renderAndroid();}handleSearchClick = () => {// 跳转到SearchScreen界面// this.props.nav.navigate('Search');};handleAddClick = () => {this.setState({showPop: !this.state.showPop});}
}class CustomModal extends Component {constructor(props) {super(props);this.state = {modalVisible: false,}}render() {return (<ModalanimationType={"fade"}transparent={true}visible={this.state.modalVisible}onRequestClose={() => {alert("Modal has been closed.")}}><View style={modalStyle.container}><View style={modalStyle.content}><Text>Hello World! This is a Modal!</Text><Buttonstyle={{marginTop: 20}}title={"Close"}onPress={() => {this.setState({modalVisible: false})}}/></View></View></Modal>);}closeModel = () => {this.setState({modalVisible: false});}openModal() {this.setState({modalVisible: true});}
}const modalStyle = StyleSheet.create({container: {flex: 1,flexDirection: 'column',justifyContent: 'center',alignItems: 'center',backgroundColor: 'rgba(0, 0, 0, 0.5)'},content: {width: width - 40,flexDirection: 'column',justifyContent: 'center',alignItems: 'center',marginLeft: 20,marginRight: 20,backgroundColor: '#FFFFFF',height: 100,borderRadius: 5,paddingTop: 10,paddingBottom: 10,paddingLeft: 10,paddingRight: 10,}
});const styles = StyleSheet.create({titleBarContainer: {flexDirection: 'row',width: width,height: 50,backgroundColor: Global.titleBackgroundColor},titleBarTextContainer: {flex: 1,flexDirection: 'row',alignItems: 'center',paddingLeft: 10,paddingRight: 10,},titleBarButtonContainer: {alignItems: 'center',flexDirection: 'row',paddingLeft: 10,paddingRight: 10,},title: {color: '#FFFFFF',fontSize: 18,fontWeight: 'bold',},titleBarImg: {width: 25,height: 25,marginLeft: 15,marginRight: 15,}
});

TitleBar添加了菜单窗口组件MenuPopWindow,效果图:

TitleBar

import React, {Component} from 'react';import {StyleSheet,Text,View,ActivityIndicator
} from 'react-native';export default class CommonLoadingView extends Component {render() {return (<View style={styles.container}><ActivityIndicator size="large"/><Text style={{marginTop: 15, fontSize: 16}}>{this.props.hintText || "加载中,请稍等..."}</Text></View>);}
}const styles = StyleSheet.create({container: {flex: 1,flexDirection: 'column',justifyContent: 'center',alignItems: 'center'}
});

CommonLoadingView

状态二:加载完成

获取数据之后,需要展示在页面中,怎么样展示是个问题,参考微信通讯录的展示,大概模型如下:

pic3

renderSuccessView:

renderSuccessView() {var listData = [];var headerListData = [];var headerImages = [require('../../images/ic_new_friends.png'), require('../../images/ic_group_chat.png'),require('../../images/ic_tag.png'), require('../../images/ic_common.png')];var headerTitles = ['新的朋友', '群聊', '标签', '公众号'];var index = 0;for (var i = 0; i < headerTitles.length; i++) {headerListData.push({key: index++,title: headerTitles[i],nick: '',icon: headerImages[i],sectionStart: false,});}var contacts = this.state.contactData;for (var i = 0; i < contacts.length; i++) {// var pinyin = PinyinUtil.getFullChars(contacts[i].name);var pinyin = contacts[i].pinyin.toUpperCase();var firstLetter = pinyin.substring(0, 1);if (firstLetter < 'A' || firstLetter > 'Z') {firstLetter = '#';}let icon = require('../../images/avatar.png');if (!Utils.isEmpty(contacts[i].avatar)) {icon = {uri: contacts[i].avatar};}listData.push({key: index++,icon: icon,title: contacts[i].name,nick: contacts[i].nick,pinyin: pinyin,firstLetter: firstLetter,sectionStart: false,})}// 按拼音排序listData.sort(function (a, b) {if (a.pinyin === undefined || b.pinyin === undefined) {return 1;}if (a.pinyin > b.pinyin) {return 1;}if (a.pinyin < b.pinyin) {return -1;}return 0;});listData = headerListData.concat(listData);// 根据首字母分区for (var i = 0; i < listData.length; i++) {var obj = listData[i];if (obj.pinyin === undefined) {continue;}if (i > 0 && i < listData.length) {var preObj = listData[i - 1];if (preObj.pinyin === undefined && obj.pinyin !== undefined) {obj.sectionStart = true;} else if (preObj.pinyin !== undefined && obj.pinyin !== undefined && preObj.firstLetter !== obj.firstLetter) {obj.sectionStart = true;}}}this.listData = listData;return (<View style={styles.container}><TitleBar nav={this.props.navigation}/><View style={styles.divider}/><View style={styles.content}><FlatListref={'list'}data={listData}renderItem={this._renderItem}getItemLayout={this._getItemLayout}/><SideBar onLetterSelectedListener={this.onSideBarSelected.bind(this)}/></View><View style={styles.divider}/></View>);}

其中用到的列表展示器是FlatList,在return之前还有一系列操作,这些操作是针对按字母分组的需求设计的。

Item的样式:
是一个可选的优化,用于避免动态测量内容尺寸的开销,不过前提是你可以提前知道内容的高度。如果你的行高是固定的,那么getItemLayout用起来就既高效又简单,类似下面这样:
getItemLayout={(data, index) => ( {length: 行高, offset: 行高 * index, index} )}
注意:如果你指定了SeparatorComponent,请把分隔线的尺寸也考虑到offset的计算之中。

  _getItemLayout = (data, index) => {let len = data.sectionStart ? (58) : (51);let dividerHeight = 1 / PixelRatio.get();return {length: len,offset: (len + dividerHeight) * index,index};}

Item的内容:
根据上面的拼音分区拿到的sectionStart作为条件判断,渲染每一行的组件。

 _renderItem = (item) => {var section = [];if (item.item.sectionStart) {section.push(<Text key={"section" + item.item.key}style={listItemStyle.sectionView}>{item.item.firstLetter}</Text>);}return (<View>{section}<TouchableHighlight underlayColor={Global.touchableHighlightColor} onPress={() => {this.onListItemClick(item)}}><View style={listItemStyle.container} key={item.item.key}><Image style={listItemStyle.image} source={item.item.icon}/><Text style={listItemStyle.itemText}>{item.item.title}</Text><Textstyle={listItemStyle.subText}>{Utils.isEmpty(item.item.nick) ? "" : "(" + item.item.nick + ")"}</Text></View></TouchableHighlight><View style={{width: width,height: 1 / PixelRatio.get(),backgroundColor: Global.dividerColor}}/></View>);}

侧边的SideBar组件,可以实现滑动索引的效果 > SideBar

import React, {Component} from 'react';
import {Text, TouchableOpacity, View} from 'react-native';export default class SideBar extends Component {render() {var letters = ['☆', '#', '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'];var letterViewArr = [];for (var i = 0; i < letters.length; i++) {letterViewArr.push(<TouchableOpacitykey={i}onPress={this.onLetterSelectedListener.bind(this, letters[i])}><Text style={{color: '#999999', fontSize: 12, paddingLeft: 2, paddingRight: 2}} key={"letter" + i}>{letters[i]}</Text></TouchableOpacity>);}return (<Viewstyle={{flexDirection: 'column', justifyContent: 'center', alignItems: 'center', backgroundColor: '#FFFFFF'}}>{letterViewArr}</View>);}onLetterSelectedListener = (letter) => {// Toast.showShortCenter(letter);this.props.onLetterSelectedListener && this.props.onLetterSelectedListener(letter);}
}

该组件暴露了一个接口与我们实现 > onSideBarSelected(letter):

 onSideBarSelected(letter) {if (this.listData) {for (let i = 0; i < this.listData.length; i++) {let item = this.listData[i];if (item.firstLetter == letter && item.sectionStart) {//根据FlatList的ref属性调用其方法scrollToIndex()可以跳转到对应拼音的联系人Toast.showShortCenter(letter);this.refs.list.scrollToIndex({viewPosition: 0, index: i});break;}}}}

pic3

三、加载错误

    renderErrorView() {return (<View style={{justifyContent: 'center',alignItems: 'center',flex: 1,flexDirection: 'column'}}><Text style={{fontSize: 16, color: '#000000'}}>加载数据出错!</Text></View>);}

作者:淮左明都
链接:https://www.jianshu.com/p/0c46ccd4f5d6
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

RN仿微信通讯录列表相关推荐

  1. 【uniapp前端组件】仿微信通讯录列表组件

    仿微信通讯录列表组件 示例图 前言 仿微信通讯录列表组件,可实现通讯列表以及选择多个联系人功能. 组件介绍 本组件有三个自定义组件构成,都已经集成在bugking7-contact-list中,该组件 ...

  2. 自己现实的仿微信通讯录列表

    自己项目中需要做一个通讯录,单是和微信不一样,微信通讯录头部的几列好像是固定的,但是项目中的头部是群组管理,是动态的,对其联系人还需要做首字母排序,效果倒是很容易做出来,但是这里只能放一个listvi ...

  3. android 字母索引三方,Android ListView字母索引(仿微信通讯录列表)

    布局代码 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_paren ...

  4. 【Android 仿微信通讯录 导航分组列表-上】使用ItemDecoration为RecyclerView打造带悬停头部的分组列表

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 转载请标明出处: http://blog.csdn.net/zxt0601/article/details/52355199 本文 ...

  5. uni-app 写小程序 索引列表,仿微信通讯录

    心里认定了一个女孩 就要好好的珍惜对待她,人生不容辜负,你必须要更加努力 .加油 骚年 uni-app 写小程序 索引列表,仿微信通讯录 去uni-app官网 下载插件 indexlist <m ...

  6. RecyclerView+index索引实现仿微信通讯录

    感觉之前写的有点乱,所以有重新整理了一下这个博客: demo下载地址:http://download.csdn.net/detail/qq_34501274/9799175 最近跟朋友聊天,说道博客相 ...

  7. android 字母搜索栏,android仿微信通讯录搜索示例(匹配拼音,字母,索引位置)

    前言: 仿微信通讯录搜索功能,通过汉字或拼音首字母找到匹配的联系人并显示匹配的位置 一:先看效果图 字母索引 搜索匹配 二:功能分析 1:汉字转拼音 通讯录汉字转拼音(首个字符当考虑姓氏多音字), 现 ...

  8. Android仿微信通讯录

    Android仿微信通讯录 分3部: 1.listview实现显示头像.名字(太简单,这里就不写了) 通讯录页面xml布局代码: <LinearLayout xmlns:android=&quo ...

  9. 仿微信通讯录的Demo----PinnedHeaderListView

    仿微信通讯录的Demo--PinnedHeaderListView 侧边栏首字母匹配 + header分组 本示例代码来自网上 这里只贴出效果图,附件Demo源码,仅供学习和以后参考时用 附件 Dem ...

最新文章

  1. 默认参数,不固定参数 *args,**kwargs
  2. UIView翻译 (参考)
  3. 计算机设计策略,专家经验谈:Excel工作表的设计策略
  4. 天津计算机专业排名2015,2015年南开大学计算机类专业最低分是多少?
  5. Mysql数据库中的 Group by 语句的特殊之处(select 中的项目不必出现在Group by中)---不建议使用!
  6. [转载] R语言read.table函数
  7. 《离散数学及其应用》阅读感想(转载)
  8. 直线端点画垂线lisp_AutoCAD中利用AutoLISP开发小程序,实现快速画直线对称中心线...
  9. jpg图片怎么压缩大小?简单快捷的方法教给你
  10. 1044: 不及格率 Python
  11. 智能教育,是未来教育的趋势吗?
  12. 人才缺口上百万,年薪50万+!
  13. matlab bfs函数,Matlab脚本和函数
  14. matlab 面 颜色,matlab曲面颜色
  15. 触摸控件与显示控件介绍
  16. 东文财 赵栋 罗松 201771010106《面向对象程序设计(java)》实验14
  17. linux打开浏览器密码取消,Deepin下打开谷歌chrome浏览器提示解锁登录密钥环的解决方法...
  18. 3.4 Linux常用的转义字符
  19. VirtualBox 不能为虚拟电脑 打开一个新任务 VERR_NEM_VM_CREATE_FAILED
  20. Electropure EDI 中国区2019年年度总结会议

热门文章

  1. 萌新改代码系列(一)--VINS+GPS
  2. oracle 导出身份证号_ORACLE对身份证号码处理相关的SQL【收藏】
  3. matlab上位机串口通信中如何发送16进制数,而不是当做ASCII字符发送(已实测成功)
  4. 加密IC 在android 机子上的简单应用
  5. 在html网页上在线连接邮箱,怎么在html中创建超级链接和电子邮件链接
  6. ubuntu服务器ftp无法上传文件,ubuntu服务器上传文件ftp
  7. Selenium基于Python的web自动化测试框架(1)-环境搭建
  8. nyoj 1239-引水工程 //并查集
  9. 数码摄影入门:焦距是什么?
  10. macbook视频格式转换_为了找到MacBook这个视频转换软件我哭了!太强大了!