最近国内外使用React-Native写APP的公司越来越多了,我们公司也不甘落后,将使用React-Native重写APP这个事提上了日程。然而这个任务落到我头上的时候,我本人作为一个Android菜鸡程序猿当即表示“臣妾做不到啊~”,
然而并没有什么卵用。没办法,只好硬着头皮现学现卖。这东西刚出时间并不算长,网上完整的系统的参考文章较少,多数都是零碎的知识点,只好在博客里做个笔记,省得忘了以后再去满大街找。。
这一系列笔记内容均仅代表我个人的观点,不保证都是对的
React-Native的ListView
写过Android的人都知道ListView是干啥的,在此不赘述。作为一个完整的应用程序最重要的组件之一,RN给我们提供了一个它自己的ListView组件。RN的ListView中包含两个关键参数:数据(dataSource)及绘制每一个Item内容的方法(renderRow)。
dataSource
所谓dataSource,就是你要在ListView中每一个Item里显示的东西所依赖的数据。例如你的ListView要用来显示一个简单的电话本,那么你至少需要一些号码和号码所对应的名称用来显示,而所有这些集合起来,就是dataSource。
RN中使用ListView.DataSource类来管理这些dataSource,使用起来比较简单:
constructor(props) {
super(props);
// 创建DataSource对象
let ds = new ListView.DataSource(params);
// 将DataSource对象设置为state,以便之后更新。
this.state = {
ds,
};
}
其中构造函数中的params参数是一个对象,它可以包含以下四种属性中的一项或多项:
getRowData:function;
getSectionHeaderData:function;
rowHasChanged:function;
sectionHeaderHasChanged:function;
这些属性对应的方法用来将ListView默认的对应方法实现更改为自定义的实现,用来在实现一些相对复杂的页面绘制过程中通过这些方法获得我们需要的内容。
但本文的目的仅为记录ListView的基本使用,因此不一一说明所有属性的使用方法,目前唯一需要用到的只有一个:rowHasChanged,使用起来类似这样:
constructor(props) {
super(props);
// 创建DataSource对象
let ds = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2});
// 将DataSource对象设置为state,以便之后更新。
this.state = {
ds,
};
}
从属性名即可以看出,这个属性对应的方法是用来判断两行数据是否相同的。如果对dataSource进行了更新,ListView将通过这个方法来逐行判断新的行数据和旧的行数据是否相同,如果相同则不进行重新绘制,显然提高了绘制的效率。
那么如何对dataSource进行更新呢?官方文档要(qiang)求(po)我们使用cloneWithRows(newData)方法(或cloneWithRowsAndSections方法,但此处不做展开),用法很简单:
constructor(props) {
super(props);
// 创建DataSource对象
let ds = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2});
// 将DataSource对象设置为state,以便之后更新。
this.state = {
ds,
};
}
_setData = (newData) => {
// 更新数据,同时通过setState更新UI
this.setState({
ds: this.state.ds.cloneWithRows(newData)
});
};
至此,基本的创建及更新dataSource的工作就完成了。接下来就是根据数据绘制每行UI的方法的实现。
renderRow
ListView中使用renderRow参数来为ListView传入绘制每行UI的方法,因此,我们首先需要这个方法的实现:
_renderRow = (rowData) => {
// TODO 绘制UI的逻辑
}
这个方法接收几个参数,目前我们只需要关心第一个参数,即当前行的数据即可。这个方法需要返回一个可渲染的组件,类似< View />、< Image />之类的。比如我们要绘制一个简单的电话本,可以这样写:
_renderRow = (rowData) => {
return <View>
<Text>rowData.name</Text>
<Text>rowData.phoneNumber</Text>
</View>
}
这样我们就完成了绘制每行UI的方法实现。接下来终于可以实现ListView了。
使用ListView
准备好上面两项必要的内容以后,我们就可以把它们添加到ListView中展示出来了,具体用法像这样:
render() {
return (
<ListView
dataSource={this.state.ds}
renderRow={this._renderRow.bind(this)}
/>
);
}
很简单,加上上面两个步骤,再稍微整理一下,整个文件的代码如下:
import {Component} from 'react';
import {View,Text,ListView} from 'react-native';
export default class MyListView extends Component {
_data = [
{name: '张三', phoneNumber: 13800000000},
{name: '李四', phoneNumber: 13900000000},
{name: '王五', phoneNumber: 15800000000},
]
constructor(props) {
super(props);
// 创建DataSource对象
let ds = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2});
// 将DataSource对象设置为state,以便之后更新。
this.state = {
ds,
};
// 绑定this指针
this.setData = this._setData.bind(this);
this.renderRow = this._renderRow.bind(this);
}
_setData = (newData) => {
// 更新数据,同时通过setState更新UI
this.setState({
ds: this.state.ds.cloneWithRows(newData)
});
}
_renderRow = (rowData) => {
// 根据数据绘制单行UI
return <View>
<Text>rowData.name</Text>
<Text>rowData.phoneNumber</Text>
</View>
}
componentWillMount() {
// 添加数据
this.setData(this._data);
}
render() {
return (
<ListView
dataSource={this.state.ds}
renderRow={this.renderRow}
/>
);
}
}
这样,一个简单的ListView示例就完成了。
然而,在实际的项目中不可能存在这么Low的ListView设计,我们要处理大量的数据,我们要画更复杂的布局,我们渴望nai..然而,目前RN自带的ListView并不能完美的满足我们所有的需要,如果一个ListView中包含了大量的数据以及复杂的布局,那么它绘制的效率仍然令人捉鸡,这个时候我们只好通过Native原生控件实现,再使用React-Native调用。这样的做法显然与我们使用React-Native的理由背道而驰,只能希望FB在之后的更新中能够想想法子继续优化一下。。