前言
今天想跟大家分享一个用RN实现的组件 - ExpandableList。恩,没什么特殊的原因,只是因为最近有一个需求要用到这东西,而且RN没有提供现成的组件,所以很(不)开(得)心(已)地做了一个。下面两张图是用这个组件实现的两个demo,github地址在这儿,有兴趣的可以戳https://github.com/SmallStoneSK/react-native-expandable-list瞅一眼,喜欢的还可以star一个~
如果有哪说的不对的,欢迎指出哦~
![](http://oo819l870.bkt.clouddn.com/simple-list-demo.gif)
![](http://oo819l870.bkt.clouddn.com/qq-friend-list.gif)
讨论与分析
好了,废话不多说,直接进入正题。首先,我们先确定下要解决的问题:
- 组件结构怎么表示?
- 展开/收起动画怎么过渡?
- API设计成怎样让组件的实用性更强?
1. 第一个问题
我们先将ExpandableList这个组件拆解一下,看看都有哪些部分。看下面的这张图,我们可以把一个ExpandableList看成是由一个个Group组成的,而每个Group又了包含GroupHeader和GroupBody,而其实GroupBody本身又是一个List。
分析完结构之后,思路瞬间就有了,这个结构用两个循环就可以表示出来了,就像下面这样:
<View>
{data.map((groupItem, groupIndex) => {
return (
<View key={`group-${groupIndex}`}>
{renderGroupHeader.bind(this, groupItem. groupHeaderData, groupIndex)}
{groupItem.groupListData.map((listItemData, listItemIndex) => {
return (
<View key={`group-${groupIndex}-list-item-${listItemIndex}`}>
{renderListItem.bind(this, listItemData, groupIndex, listItemIndex)}
</View>
);
})}
</View>
);
})}
</View>
2. 第二个问题
没错,结构是很轻易地表示出来了。但是问题来了,展开收起的这个动画过程应该怎么现实呢?我们都知道在RN中如果要实现动画,那Animated绝对是把好手。借助Animated,我们可以很精准地控制动画的实现,当然也包括这里的展开/收起动画。但是在这里,就不劳烦这尊大佛啦~因为借助LayoutAnimation,我们可以实现地更优雅(其实就是偷懒)。
在讲LayoutAnimation之前,不妨先回顾下web中的transition。为啥捏?因为个人觉得这两者就是很像,只要给定了初始状态和终止状态,那这中间的动画切换过程就不需要我们关心了。再来看这个展开/收起的动画,是不是很符合这个条件。每个group都有两种状态,即open和closed。因此,当closed时,我们设置groupBody的height为0就可以了。
3. 第三个问题
为什么要考虑API的设计呢?因为这个组件实在太简单,感觉都编不下去了,不找个主题怎么凑字数。。。当然,这是玩笑话。实际上,在封装这个组件的时候,还是遇到了一些调用上的问题,就比如:
- 如何关联起TouchableXXX和展开/收起动画: 毫无疑问,展开/收起动画是这个组件本身就应该包掉的逻辑。但是,不同需求的groupHeader样式都是各式各样的,就比如最一开始的两个demo图。很明显,两个点击区域都不同,但是点击之后都要有展开/收起的功能,动画的同时还有不同的点击功能。或许你会想到传一个回调函数给ExpandableList,在点击GroupHeader的时候调用这个回调就好了。But,再仔细想想,别忘了TouchableXXX这一部分可是在自定义样式中的,所以ExpandableList组件中是不会包掉touch操作的,那传进来的回调到哪里去调用。。。
- 如何提高组件的性能: 上面虽然用了一个很粗浅的方法大概模拟了下组件的组成,但是