前面我们看了React Native提供给我们的一些组件,另外在电影列表的示例中我们已经介绍了React Native中的ListView组件及其简单的使用,本节内容我们将详细介绍一下ScrollView组件的使用以及ListView组件的其他属性。
ScrollView组件一般是应用在要显示的东西不多的情况下,如果请要显示的内容比较多的话,建议使用ListView组件。
RN中的ScrollView组件通过horizontal 属性可以指定滚动方向,默认属性值是false,也就是默认是垂直滚动。
contentContainerStyle属性
contentContainerStyle: 用于指定所有子元素共有的父style
使用方式:
...
<ScrollView contentContainerStyle = {} styles.contentContainer>
</ScrollView>
...
var styles = StyleSheet.create({
contentContainer:{
paddingVertical:20
}
});
horizontal属性
horizontal: 用于指定子视图的排列方向。默认为false,子视图为垂直布局。
keyboardDismissMode属性
keyboardDismissMode: 用户滚动视图的时候是否要隐藏软键盘,属性值有三个枚举类型。
none(默认值): 滚动时不隐藏软键盘
on-drag: 滚动的时候隐藏软键盘
interactive: 软键盘伴随拖拽操作同步的消失,并且如果往上滑动会回复软键盘。Android设备上不支持这个属性值,效果会和none属性值一样
keyboardShouldPersistTaps属性:默认值false
keyboardShouldPersistTaps: 当此属性为false的时候, 在软键盘激活之后, 点击焦点文本输入框以外的地方, 键盘就会隐藏; 反之,ScrollView不会响应点击操作,并且键盘不会自动消失。
onContentSizeChange属性,属性值是一个function
onContentSizeChange: 此函数会在ScrollView内部的子视图发生变化的时候调用。函数的参数是子视图的宽和高,此方法是通过绑定在子视图的onLayout来实现。
onScroll属性,属性值是一个function
onScroll: 在滚动的过程中,每帧最多调用一次回调函数。调用的频率可以用scrollEventThrottle属性来控制。
refreshControl属性,属性值是element
refreshControl: 指定RefreshControl组件,用于为ScrollView提供下拉刷新功能。
控制是否显示滚动条
showsHorizontalScrollIndicator: 是否显示水平方向的滚动条。
showsVerticalScrollIndicator: 是否显示垂直方向上的滚动条。
endFillColor属性—-Android特有的属性
endFillColor: 有时候滚动视图会占用比实际内容更多的空间。这种情况下可以使用这个属性去指定一个颜色值来填充多余的空间,以避免设置背景和创建不必要的绘制开销。一般情况下并不需要这种高级优化技巧。
pagingEnabled属性
pagingEnabled: 默认值是false。不使用默认值的情况下,滚动条会停在滚动视图的整数倍位置上,这个属性一般会在水平分页的时候用到。
scrollEnabled属性
scrollEnabled: 当值为false的时候,内容不能滚动。
scrollTo方法
scrollTo(y: number | {x?: number, y?: number, animated?: boolean }, x: number, animated: boolean)
滚动到指定的x,y偏移处。第三个参数为是否使用平滑滚动动画。
使用示例
scrollTo({x:0, y:0, animated: true});
下面咱们来看例子:
...
exports.examples = [
{
render: function(){
var _scrollView: ScrollView;
return (
<View>
<ScrollView
ref={(scrollView) => {_scrollView = scrollView;}}
automaticallyAdjustContentInsets={false}
onScroll={() => { console.log('onScroll!');}}
scrollEventThrottle={200}
style={styles.scrollView}>
{THUMBS.map(createThumbRow)}
</ScrollView>
<TouchableOpacity
style={styles.button}
onPress={() => {_scrollView.scrollTo({y:0});}}>
<Text>Scroll to top</Text>
</TouchableOpacity>
</View>
);
}
},{
render: function(){
var _scrollView: ScrollView;
return(
<View>
<ScrollView
ref={(scrollView) => {_scrollView = scrollView; }}
automaticallyAdjustContentInsets={false}
horizontal={true}
style={[styles.scrollView, style.horizontalScrollView]}>
{THUMBS.map(createThumbRow)}
</ScrollView>
<TouchableOpacity
style={styles.button}
onPress={() => {_scrollView.scrollTo({x:0});}}>
<Text>Scroll to start</Text>
</TouchableOpacity>
</View>
);
}
}];
...
上面的代码中我们简单写了一下ScrollView的属性和方法的使用。里面的TouchableOpacity组件就相当于一个添加了状态选择器的button,在点击的时候会有一个深颜色的背景色。
Flexbox布局样式
前面我们已经总结了Flexbox的具体使用和注意事项,大家如果有不熟悉或者有不明白的大家可以回头看一下,学习笔记(五)关于style的学习和总结,另外有一篇blog写的挺好,里面写的还是比较全的。
阴影相关style
下面的这几个属性在是iOS相关的属性,有兴趣的可以具体看一下。
shadowColor: 设置阴影色
shadowOffset: 设置阴影偏移 {width: number, height: number}
shadowOpacity: 设置阴影的不透明度(乘以颜色的alpha分量)
shadowRadius: 设置阴影模糊半径
Transforms…属性列表
transform[
{perspective: number}, // 透视
{rotate:string}, // 旋转
{rotateX: string}, // X轴 旋转
{rotateY: string}, // Y轴 旋转
{rotateZ: string}, // Z轴 旋转
{scale: number}, // 缩放
{scaleX: number}, // 缩放 X轴
{scaleY: number}, // 缩放 Y轴
{translateX: number}, // 水平方向平移
{translateY: number}, // 竖直方向平移
{skewX: string}, // X轴倾斜角
{skewY: string} // Y轴倾斜角
]
transformMatrix 属性值TransformMatrixPropType类型
backfaceVisibility enum(‘visible’, ‘hidden’)
backfaceVisibility=‘visible’ 背景是否可见
backgroundColor string
backgroundColor=’#dddddd’ 背景颜色
borderColor string
borderColor=’#aaaaaa’ 边框颜色
另外有四个相关style:
borderTopColor string
borderRightColor string
borderBottomColor string
borderLeftColor string
borderRadius number
borderRadius=5 边框半径
另外有四个style:
borderTopLeftRadius number
borderTopRightRadius number
borderBottomLeftRadius number
borderBottomRightRadius number
border宽度相关的属性
borderStyle enum(‘solid’, ‘dotted’, ‘dashed’) 加粗、点线、虚线
borderWidth number 边框高度
borderTopWidth number
borderRightWidth number
borderBottomWidth number
borderLeftWidth number
opacity number
opacity 透明度
overflow enum(‘visible’, ‘hidden’)
overflow 滚动条是否显示
一、组件的简单使用和注意事项
英文官网中ListView中对其有比较详细的。
中文官网中也有关于ListView的相关介绍。
下面我们详细介绍一下ListView组件。
就像官网中说的一样,这是一个核心组件,用于高效的显示一个可以垂直滚动的变化的数据列表。最基本的使用方式就是创建一个ListView.DataSource数据源,然后给它传递一个普通的数据数组,在使用数据源来实例化一个ListView组件,并定义一个renderRow回调函数,这个函数会接受数据源中的每一个数据作为参数,返回一个可渲染的组件(其实在Android中renderRow返回的东西就相当于itemView布局),作为ListView的每一行。
下面看一个简单的例子:
import React, { Component } form 'react';
import { AppRegistry, ListView, Text, View } from 'react-native';
class ListViewDemo extends Component {
constructor(props){
super(props);
var ds = new ListView.DataSource({rowHasChanged: (row_1, row_2) => row_1 !== row_2});
this.state = {
dataSource: ds.cloneWithRows(['row 1', 'row 2']),
};
}
render(){
return (
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>{rowData}</Text>}
/>
);
}
}
上面我们展示了ListView的简单实用。除此之外ListView组件支持像:
1.为每一个item添加一个有粘性的头部或尾部
2.整体增加头部和尾部
对于ListView组件,我们要知道以下两点需要注意的地方:
1.为了使我们的用户体验更好,在数据发生改变的时候,我们应该只更新变化的行(item),只更新变化的行,通过提供给数据源的rowHasChange函数可以告诉ListView组件是否需要重绘一行数据。具体操作大家可以看ListView.DataSource
2.限制频繁的进行行渲染,在默认情况下,每次消息循环只有一行会被渲染(可以使用pageSize属性进行配置)。这把较大的工作分散成小的碎片,以降低以为渲染而导致帧丢失的可能性。
二、组件的常用属性
注:ListView是支持ScrollView全部属性的,除了ScrollView的属性之外,还有以下几个自己的特有属性。
dataSource属性
正如上面的示例中写的一样,此属性用于向ListView组件指定数据源。属性值一般是一个数组
initialListSize 属性
此属性用于指定组件刚挂载的时候显示多少行数据。用这个属性来确保首屏显示多少数据,一般用于优化操作。属性值是number
onChangeVisibleRows属性
此属性的属性值是一个函数,函数的参数是(visibleRows, changedRows),当可见的行的集合变化的时候调用此回调函数。visibleRows以{sectionID:{rowID: true}}的格式包含了所有可见行,而changeRows以{sectionID:{rowID: true | false }}的格式包含了所有刚刚改变了可见性的行,其中值为true标识一个行变的可见,而为false的标识行刚刚变为不可见状态。
onEndReachedThreshold属性
用于指定当所有元素都被渲染过后,当滑动到距离最底端什么位置的时候调用onEndReached属性指定的回调函数。属性值是number
onEndReached属性
当所有的数据都已经渲染过,并且列表被滚动到距离最底端不足onEndReachedThreshold属性指定的像素距离的时候调用。属性值是function
当第一次渲染,如果数据不足一屏的时候这个属性的回调函数也会被触发。
pageSize属性
一页需要渲染的行数。属性值是number
removeClippedSubviews属性
此属性默认开启,用于提升列表的滚动性能。需要添加样式:overflow:’hidden’(Android已默认添加此样式)。属性值是bool类型
renderHeader & renderFooter属性
header和footer的添加,如果使用这两个属性,建议把头布局和脚布局包装在一个StaticContainer或者其他恰当的结构中。页脚会永远在底部,页头会永远在顶部。属性值是function
() => renderable
renderRow属性
(rowData, sectionID, rowID, highlightRow) => renderable
从数据源(Data source)中接受一条数据,以及它和它所在section的ID。返回一个可渲染的组件来为这行数据进行渲染。默认情况下参数中的数据就是放进数据源中的数据本身,不过也可以提供一些转换器。
如果一行正在被高亮(通过调用highlightRow函数),ListView会得到相应的通知。当一行被高亮时,其两侧的分割线会被隐藏。行的高亮状态可以通过调用highlightRow(null)来重置。
renderScrollComponent属性
(props) => renderable
指定一个函数,在其中返回一个可以滚动的组件。ListView将会在该组件内部进行绘制。默认情况下回返回一个包含指定属性的ScrollView
renderSectionHeader属性
(sectionData, sectionID) => renderable
如果提供了此函数,会为每一个Section渲染一个粘性标题。
粘性是指当它刚出现时,会处在对应小节的内容顶部;继续下滑当它到达屏幕顶端的时候,它会停留在屏幕顶端,一直到对应的位置被下一小节的标题占据为止。在Android中就类似于联系人的那种格式,按照#、A、B、C、D…分割联系人
renderSeparator属性
(sectionID, rowID, adjacentRowHighlighted) => renderable
如果提供了此属性,一个可渲染的组件会被渲染在每一行的下面,除了小节标题的前面的最后一行。在其上方的小节ID和行ID,以及邻近的行是否被高亮会作为参数传递进来。
scrollRenderAheadDistance属性
当一个行接近屏幕范围多少像素之内的时候,就开始渲染这一行。
属性值是number
三、组件的方法
getMetrics()
导出一些用于性能分析的数据。
scrollTo(…args)
滚动到指定的x, y偏移处,第三个参数指定是否加上过度动画
看下面的例子:
var React = require('react');
var ReactNative = require('react-native');
var {
Image,
ListView,
TouchableHighlight,
StyleSheet,
RecyclerViewBackedScrollView,
Text,
View,
} = ReactNative;
var UIExplorerPage = require('./UIExplorerPage');
var ListViewSimpleExample = React.createClass({
statics: {
title: '<ListView>',
description: 'Performant, scrollable list of data.'
},
getInitialState: function() {
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
return {
dataSource: ds.cloneWithRows(this._genRows({})),
};
},
_pressData: ({}: {[key: number]: boolean}),
componentWillMount: function() {
this._pressData = {};
},
render: function() {
return (
<UIExplorerPage
title={this.props.navigator ? null : '<ListView>'}
noSpacer={true}
noScroll={true}>
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderScrollComponent={props => <RecyclerViewBackedScrollView {...props} />}
renderSeparator={this._renderSeperator}
/>
</UIExplorerPage>
);
},
_renderRow: function(rowData: string, sectionID: number, rowID: number, highlightRow: (sectionID: number, rowID: number) => void) {
var rowHash = Math.abs(hashCode(rowData));
var imgSource = THUMB_URLS[rowHash % THUMB_URLS.length];
return (
<TouchableHighlight onPress={() => {
this._pressRow(rowID);
highlightRow(sectionID, rowID);
}}>
<View>
<View style={styles.row}>
<Image style={styles.thumb} source={imgSource} />
<Text style={styles.text}>
{rowData + ' - ' + LOREM_IPSUM.substr(0, rowHash % 301 + 10)}
</Text>
</View>
</View>
</TouchableHighlight>
);
},
_genRows: function(pressData: {[key: number]: boolean}): Array<string> {
var dataBlob = [];
for (var ii = 0; ii < 100; ii++) {
var pressedText = pressData[ii] ? ' (pressed)' : '';
dataBlob.push('Row ' + ii + pressedText);
}
return dataBlob;
},
_pressRow: function(rowID: number) {
this._pressData[rowID] = !this._pressData[rowID];
this.setState({dataSource: this.state.dataSource.cloneWithRows(
this._genRows(this._pressData)
)});
},
_renderSeperator: function(sectionID: number, rowID: number, adjacentRowHighlighted: bool) {
return (
<View
key={`${sectionID}-${rowID}`}
style={{
height: adjacentRowHighlighted ? 4 : 1,
backgroundColor: adjacentRowHighlighted ? '#3B5998' : '#CCCCCC',
}}
/>
);
}
});
var THUMB_URLS = [
require('./Thumbnails/like.png'),
require('./Thumbnails/dislike.png'),
require('./Thumbnails/call.png'),
require('./Thumbnails/fist.png'),
require('./Thumbnails/bandaged.png'),
require('./Thumbnails/flowers.png'),
require('./Thumbnails/heart.png'),
require('./Thumbnails/liking.png'),
require('./Thumbnails/party.png'),
require('./Thumbnails/poke.png'),
require('./Thumbnails/superlike.png'),
require('./Thumbnails/victory.png'),
];
var LOREM_IPSUM = 'Lorem ipsum dolor sit amet, ius ad pertinax oportere accommodare, an vix civibus corrumpit referrentur. Te nam case ludus inciderint, te mea facilisi adipiscing. Sea id integre luptatum. In tota sale consequuntur nec. Erat ocurreret mei ei. Eu paulo sapientem vulputate est, vel an accusam intellegam interesset. Nam eu stet pericula reprimique, ea vim illud modus, putant invidunt reprehendunt ne qui.';
/* eslint no-bitwise: 0 */
var hashCode = function(str) {
var hash = 15;
for (var ii = str.length - 1; ii >= 0; ii--) {
hash = ((hash << 5) - hash) + str.charCodeAt(ii);
}
return hash;
};
var styles = StyleSheet.create({
row: {
flexDirection: 'row',
justifyContent: 'center',
padding: 10,
backgroundColor: '#F6F6F6',
},
thumb: {
width: 64,
height: 64,
},
text: {
flex: 1,
},
});
module.exports = ListViewSimpleExample;