React Native列表视图FlatList使用优化实践指南
列表视图在app中是非常常见的,目前React Native比较严重的性能问题集中在FlatList大列表等地方,以下通过js层的优化,甚至原生层的优化封装,使性能媲美原生。
FlatList
React Native 0.43版本推出FlatList替代ListView,FlatList实现继承自VirtualizedList,底层的VirtualizedList提供更高的灵活性,但使用便捷性不如FlatList,如无特殊需求无法满足直接使用FlatList。VirtualizedList实现继承自ScrollView,所以FlatList继承了VirtualizedList和ScrollView全部的props,在查阅相关文档时,如在FlatList中找不到相应的prop或者方法可以使用另外两个组件的。React Native的FlatList与android listview、ios uitableview相似,将屏幕外的视图组件回收,达到高性能的目的。
用法
以下实例代码均使用typescript
基本使用
<FlatList<number>// 数据数组data={[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}// keykeyExtractor={(item, index) => index.toString()}// item渲染renderItem={({item: num}) => (<Text>{num}</Text>)}
/>
复制代码
常用props
extraData
有除data以外的数据用在列表中,在此属性中指定,否则界面很可能不会刷新
horizontal
设置为 true 则变为水平布局模式
inverted
翻转滚动方向,多用于聊天列表之类反向展示数据
numColumns
指定一列显示多少个item
常用方法
scrollToEnd
滑动到视图底部
scrollToIndex
滑动到指定位置
scrollToOffset
滑动到指定像素
上拉加载
<FlatList// 上拉回调onEndReached={() => console.log('上拉加载')}// 滑动到最后视图内容比例,设置为0-1,例如0.5则表示滑到最后一个视图一半开始回调onEndReachedThreshold={0.1}
/>
复制代码
下拉刷新
<FlatList// true显示刷新组件refreshing={this.state.refreshing}// 下拉回调onRefresh=(async () => {this.setState({refreshing: true});await 耗时操作this.setState({refreshing: false});});
/>
复制代码
滑动事件
onTouchStart
手指按下开始滑动,调用一次,用于监听交互开始
onTouchMove
手指滑动,调用多次
onTouchEnd
手指松开,调用一次,开始惯性滚动,用于监听交互结束
onMomentumScrollBegin
惯性滚动开始,调用一次,用于监听滑动惯性动画开始
onMomentumScrollEnd
惯性滚动结束,调用一次,用于监听滑动惯性动画结束
onScroll
滑动中,调用多次,用于监听滑动位置
onScrollBeginDrag
开始滑动,调用一次,用于监听滑动开始
onScrollEndDrag
滑动结束,调用一次,用于监听滑动结束
分页
用以开发简单轮播视图,分页滑动查看内容等
// 当前视图索引
private index = 0;
// 必须与this绑定,否则抛出异常
private viewabilityConfig = {viewAreaCoveragePercentThreshold: 100};handleViewableItemsChanged = (info: { viewableItems: Array<ViewToken>; changed: Array<ViewToken>}) => {// index为当前可见视图在view的索引this.index = info.changed[0].index!;
}<FlatList// 每次滑动后一个item停留在整个视图pagingEnabled={true}// 可见视图设置,1-100,50表示一半可见时回调,100表示全部可见时回调viewabilityConfig={this.viewabilityConfig}// 可见视图变更回调onViewableItemsChanged={this.handleViewableItemsChanged}// onViewableItemsChanged会多次回调,监听惯性滑动结束判断分页滑动结束,如需要实时判断视图索引显示,则直接使用onViewableItemsChangedonMomentumScrollEnd={() => console.log('滑动至', this.index)}
/>
复制代码
优化
removeClippedSubviews
移除在屏幕外组件,默认为true,对性能有最大的影响,不要修改为false
windowSize
保持视图个数,即在屏幕外也不移除,默认值为11,在高耗性能组件中,可以适当设置小的值,在会快速滑动的视图中,设置大的值如300,避免快速滑动后当前视图还没有渲染出现空白。
getItemLayout
获取高度,如视图高度固定,设置该属性可以大大改善性能,避免了渲染过程中每一次都需要重新计算视图高度。
getItemLayout={(data, index) => ({length: height, offset: height * index, index})}
key
合理设置key提高react对组件的复用,能很大的优化性能,在组件移出屏幕外,被回收后复用。
原生优化
在要求极高的列表视图中,数据达上千甚至上万,在部分情况FlatList已经无法满足,特别是android设备。以下介绍如何直接使用原生android RecyclerView视图来完成高要求的列表视图。
原生视图代码
public class MyFlatListManager extends SimpleViewManager<MyFlatListManager.MyRecyclerView> {// 自定义RecyclerViewpublic static class MyRecyclerView extends RecyclerView {// 数据列表public List<Data> list = new ArrayList<>();// 适配器public MyAdapter myAdapter;// 布局管理器public LinearLayoutManager mLayoutManager;public MyRecyclerView(Context context) {super(context);myAdapter = new MyAdapter(this, list);// 设置为垂直方向mLayoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false);setLayoutManager(mLayoutManager);// 固定高度避免重新测量,提高性能setHasFixedSize(true);// 禁止数据变更时动画,避免闪烁setItemAnimator(null);setAdapter(myAdapter);}@Overridepublic void requestLayout() {super.requestLayout();// react native android根视图requestLayout为空函数,避免加入新视图无法显示或者高度宽度不正确,手动执行测量post(measureAndLayout);}public final Runnable measureAndLayout = new Runnable() {@Overridepublic void run() {measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));Log.d(TAG, "measureAndLayout");layout(getLeft(), getTop(), getRight(), getBottom());}};}private static class MyViewHolder extends RecyclerView.ViewHolder {public MyViewHolder(View itemView) {super(itemView);}}private static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {private List<MyViewHolder> holders;private List<Data> list;private MyRecyclerView recyclerView;public MyAdapter(MyRecyclerView recyclerView, List<VideoInfo> list) {this.list = list;this.holders = new ArrayList<>();this.recyclerView = recyclerView;}// 视图创建@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.movie_list_row, parent, false);// 手动重新设置高度,match parent itemView.getLayoutParams().height = parent.getHeight();itemView.getLayoutParams().width = parent.getWidth();return new MyViewHolder(itemView);}@Overridepublic void onBindViewHolder(final MyViewHolder holder, int position) {Data data = list.get(position);
// Log.i(TAG, "setTag " + position);holder.itemView.setTag(position);// 绑定视图数据}@Overridepublic int getItemCount() {return list.size();}}private static final String TAG = "MyFlatListViewManager";@Overridepublic String getName() {return "MyFlatListViewManager";}@Overrideprotected MyRecyclerView createViewInstance(final ThemedReactContext reactContext) {return new MyRecyclerView(reactContext);}@Nullable@Overridepublic Map<String, Integer> getCommandsMap() {Map<String, Integer> commandsMap = new HashMap<>();commandsMap.put("addData", 1);return commandsMap;}@Overridepublic void receiveCommand(MyRecyclerView root, int commandId, @Nullable ReadableArray args) {MyAdapter myAdapter = (MyAdapter) root.getAdapter();switch (commandId) {case 1:if (args == null) return;Log.i(TAG, "addData size: " + args.size());Integer position = root.list.size();for (int i = 0; i < args.size(); i++) {// 初始化值,getData为从map中获取data的函数,自行根据结构实现Data data = getData(args.getMap(i));Log.i(TAG, "add data " + data);root.list.add(data);}Log.i(TAG, "addDatas old position " + position + " size " + args.size());// 通知变更myAdapter.notifyItemRangeInserted(position, args.size());break;}}
}复制代码
需要注意的有几个地方
- setHasFixedSize 如果视图高度固定,设置固定高度能提高性能
- setItemAnimator 动画可能会导致在加载图片等的时候闪烁
- requestLayout 必须重新手动触发测量视图,在android中这部分机制被react native屏蔽
- onCreateViewHolder 必须手动设定itemView高度和宽度
react反模式
在原生组件和js层进行props传递,如数据量太大,使用props直接传递已经不合适,数据可能已经达到几m甚至更大。react的props模式已经不再适合这样的场景,在web中也是,大量的数据每一次单个数据的变更都全部重新传递,会导致严重的性能问题。在这种情况下,使用组件ref调用函数来一个一个添加或者一个一个移除相关数组这些大的对象,会很好的提升性能。在android的代码中,不再使用prop传递FlatList的data,而是使用add的方法来添加,然后在js层再进行一层的原生组件封装,让使用与其他组件一致。
React Native列表视图FlatList使用优化实践指南相关推荐
- 基于 React Native 的 58 同城 App 开发实践
作者简介: 彭飞,58 同城 iOS 客户端架构师.专注于新技术的研发,主要负责 App 端组件化架构以及性能优化,并已推广 React Native 在 58 同城 App 中业务场景的应用.在 M ...
- 大道至简——React Native在直播应用中的实践
声明:本文来自「七牛云主办的架构师实践日--亿级移动应用架构最佳实践」的演讲内容整理.PPT.速记和现场演讲视频等参见"七牛架构师实践日"官网. 嘉宾:卜赫,七牛云布道师. 责编: ...
- GitChat · 移动开发 | 征服React Native—列表组件
GitChat 作者:Li Luo 原文:征服React Native-列表组件 关注公众号:GitChat 技术杂谈,一本正经的讲技术 前言 移动应用往往受限于屏幕大小,而数据内容长度的不确定性,在 ...
- React Native在美团外卖客户端的实践
MRN简介 MRN(Meituan React Native) 是基于开源的React Native框架改造并完善而成的一套动态化方案,在开发体验上基本能与原生RN保持一致,同时从业务需求的角度满足从 ...
- React Native组件之FlatList
在过去的一年中React Native经历了从v0.40到v0.52的十几次的版本迭代,可以看到,特别是0.50之后,React Native的组件库在不断地壮大,React Native也正在越来越 ...
- React Native学习五- FlatList
高性能的简单列表组件,支持下面这些常用的功能: 完全跨平台. 支持水平布局模式. 行组件显示或隐藏时可配置回调事件. 支持单独的头部组件. 支持单独的尾部组件. 支持自定义行间分隔线. 支持下拉刷新. ...
- React Native 项目简单整理-组件优化
2019独角兽企业重金招聘Python工程师标准>>> 断断续续敲了一天,记录一下没有优化的分类的代码,App.js 里的代码 /*** Sample React Native Ap ...
- oracle性能优化求生指南_Vue项目性能优化--实践指南,网上最全最详细
点击上方 "程序员小乐"关注, 星标或置顶一起成长 第一时间与你相约 每日英文 Some happened could not forget, even forget also o ...
- 美图Android编译速度优化实践指南
分享嘉宾:张仙华 美图 资深开发工程师 分享嘉宾:张仙华,美图秀秀android团队资深研发工程师,负责编译加速.性能优化.架构设计等公共基础相关工作 导读:本文的主题是美图秀秀的Android编译速 ...
最新文章
- 天池算法赛:数据挖掘经典赛事!DCIC 2020 数字中国创新大赛启动!
- 华为NPU卡ubuntu(无网络连接情况)驱动安装记录
- 社交网络图挖掘4--三角形计数问题
- html button 隐藏_java servlet与html数据交互初体验
- XmlSerializer 对象序列化成XML 自定义编码格式(gb2312)
- python做圆柱绕流_圆柱绕流
- IOS笔记CALayer的position和anchorPoint
- vs在release下调试时局部变量值错位修复
- 在Struts 2中使用JSON Ajax
- 爱奇艺发布新款电视果,AI 语音控制,4G、WiFi 双模式投屏!
- python 实例化过程_python实例化对象的具体方法
- 爬虫python需要安装吗_python爬虫需要安装什么
- html百分比实现边框而不挤出
- UE4.26官方文档网页浏览录屏打包下载版
- python语音识别终极指南_Python语音识别终极指南-帮你完成那个最难的从0到1
- Ubuntu下自制douban.fm播放器
- Python运维(六)--系统监控psutil、数据报scapy、扫描nmap
- CA服务器的搭建和申请证书
- ps打造人物光滑的肤色
- 自动化测试金字塔与反模式