深入学习React Native之组件入门

  我们在前面两篇RN的文章中介绍了RN的启动配置和样式编写,也简单地涉及了一些RN的组件View、Text和Image,本章我们继续结合官方的案例,深入挖掘RN的组件的用法及配置,以便后续在业务中使用组件。

核心组件

  RN提供了一些核心的组件,我们可以直接import后使用,其中也包含一些iOS或者Android特有的组件,只能针对对应的平台使用,我们来看下具体用法。

基础组件

  首先是RN的五大基础组件,是我们在页面上最常用的组件,我们看下它们的的描述:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

RN组件 对应Android视图 对应iOS视图 对应Html视图
View ViewGroup UIView 非滚动div
Text TextView UITextView span或p
Image ImageView UIImageView img
ScrollView ScrollView UIScrollView 滚动div
TextInput EditText UITextField input

View

  View组件是最基础的组件,类似于div可以进行嵌套使用,在RN样式布局中我们介绍了它结合Flex样式进行页面布局;View在定位布局和div有一些区别,支持absolute绝对定位,不支持fixed和sticky定位。

  直接在View上绑定点击事件,是没有用的,View不支持点击事件,如果我们想要监听它的点击,需要在将它放到TouchableHighlight等元素中:

1
2
3
<TouchableHighlight onPress={this.onClickBox()}>
<View style={styles.box}></View>
</TouchableHighlight>

  在RN页面开发中,如果使用绝对定位布局,某个View有可能会遮住它下方的某个组件;比如我们在一个地图组件上覆盖了一个图片用来展示信息,又不想让其影响下方组件的点击、触摸事件,就可以用到pointerEvents属性,它用于控制当前视图是否可以作为触控事件的目标,有以下几个值:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  • auto:视图可以作为触控事件的目标。(默认)
  • none:视图不能作为触控事件的目标。
  • box-none:视图自身不能作为触控事件的目标,但其子视图可以。
  • box-only:视图自身可以作为触控事件的目标,但其子视图不能。

  我们用样式构建三个重叠的View:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const styles = StyleSheet.create({
box1: {
position: 'absolute',
width: 200,
height: 200,
backgroundColor: 'green',
left: 30,
top: 90,
},
box2: {
position: 'absolute',
width: 150,
height: 150,
backgroundColor: 'red',
left: 30,
top: 50,
},
box3: {
position: 'absolute',
width: 90,
height: 90,
backgroundColor: 'blue',
left: 30,
top: 50,
},
});
class Index extends Component {
clickBox(index) {
console.log('box' + index);
}
render() {
return (
<View style={styles.container}>
<TouchableHighlight onPress={() => this.clickBox('1')}>
<View style={styles.box1}>
<TouchableHighlight onPress={() => this.clickBox('2')}>
<View style={styles.box2}>
<TouchableHighlight onPress={() => this.clickBox('3')}>
<View style={styles.box3}></View>
</TouchableHighlight>
</View>
</TouchableHighlight>
</View>
</TouchableHighlight>
</View>
);
}
}

  正常我们点击每个box都会触发对应的onPress事件;但是如果我们不想触发子元素box2和box3的事件,我们可以添加pointerEvents:none

1
2
<View style={styles.box2} pointerEvents={'none'}>
</View>

pointerEvents none

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  我们看到元素pointerEvents设置为none后,其元素本身及子元素均已不能触发事件了。

Text

  Text组件之前介绍过主要用来显示文字;我们经常需要对Text中超过多少行显示省略号,不能使用css中的text-overflow: ellipsis,而是使用Text的numberOfLines属性来进行文本行数的限制显示:

1
2
3
<Text numberOfLines={2} ellipsizeMode={'tail'}>
很多很多的文本内容
</Text>

  Text组件的样式和web端也有一定的区别,我们通常在html指定整个文档默认的字体、字号及颜色:

1
2
3
4
5
6
/* css代码 */
html{
font-size: 14px;
color: '#333';
font-family: Arial, SimSun, Helvetica, sans-serif;
}

  这样我们对某些div、span中的文字,如果没有指定颜色大小,浏览器就会一路向上查找到根节点html,然后继承它的样式;这样导致的问题就是任何节点都会有font-size属性,RN摒弃了样式继承,推荐使用包含相关样式的组件,然后重复使用这个组件:

1
2
3
4
5
6
<View>
<MyNavHeaderText>导航标题</MyNavHeaderText>
<MyBodyText>文本内容1</MyBodyText>
<MyBodyText>文本内容2</MyBodyText>
<MyFooterText>页脚内容</MyFooterText>
</View>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  我们在定义MyBodyText组件时,可以把它的内容放到Text中:

1
2
3
4
5
6
7
8
9
class MyBodyText extends Component{
render() {
return (
<Text style={{ fontSize: 20 }}>
{this.props.children}
</Text>
)
}
}

  在Text组件上我们可以绑定点击事件onPress

1
2
3
4
5
6
7
8
9
10
class MyBodyText extends Component{
onClick = ()=>{}
render() {
return (
<Text onPress={this.onClick}>
{this.props.children}
</Text>
)
}
}

Image

  在深入学习RN样式布局我们已经介绍了Image组件通过source属性来加载本地图片或者网络,这里不再赘述了。

  RN也支持对网络图片进行缓存,访问过一次的图片,在一定时间内会缓存到手机中,当需要再次显示的时候,RN会直接从缓存读取;在Android平台,图片会缓存到本地;对于iOS平台,可以通过cache属性实现不同缓存效果:

1
2
3
4
5
6
7
<Image
source={{
uri: 'https://xieyufei.com/img/bg_small.jpg',
cache: 'only-if-cached',
}}
style={{width: 400, height: 400}}
/>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  cache属性可以取以下值:

  • default:使用原生平台默认策略
  • reload:不使用现有的缓存数据
  • force-cache:现有的缓存数据将用于满足请求,忽略其期限或到期日。如果缓存中没有对应请求的数据,则从原始地址加载。
  • only-if-cached:现有的缓存数据将用于满足请求,忽略其期限或到期日。如果缓存中没有对应请求的数据,则不尝试从原始地址加载,并且认为请求是失败的。

  RN还提供了一个统一的方式来管理iOS和Android中的图片,我们首先给两个平台分别创建好两张不同的图片:

1
2
3
4
5
├── button.js
└── img
├── logo.jpg
├── logo.android.jpg
└── logo.ios.jpg

  在我们的代码中,只需要引用logo.jpg图片,RN就会根据平台而选择不同的图片文件:

1
2
3
<Image
style={{width: 360, height: 144}}
source={require('./img/logo.jpg')}></Image>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

Android图片展示

iOS图片展示

  我们看到不同平台展示了对应logo图片;除此之外我们还可以使用@2x@3x这样的文件名后缀,来为不同的屏幕精度提供图片。

  我们经常会遇到文字嵌套背景图片的情况,除了使用Image组件进行绝对布局外,RN还提供了ImageBackground组件,它的参数和Image组件完全相同,只需要把子组件嵌套即可:

1
2
3
4
5
<ImageBackground
style={{width: 360, height: 144}}
source={require('./img/logo.jpg')}>
<Text>我是图片的描述文案</Text>
</ImageBackground>

TextInput

  TextInput组件是一个允许用户在应用中通过键盘输入文本内容的组件;它接收一个value属性作为输入的默认值,当文本框内容变化时回调onChange函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Index extends Component {
constructor() {
super();
this.state = {
value: '',
};
}
onChangeText = (value) => {
this.setState({
value,
});
};
render() {
return (
<TextInput
value={this.state.value}
onChange={val => this.onChangeText(val)}></TextInput>
)
}
}

  和web端的input一样,TextInput同样也支持设置占位符的文字placeholder,同时还能直接设置占位符的色值:

1
2
3
<TextInput
placeholder="请输入"
placeholderTextColor="#999"></TextInput>

  我们在不同的场景下经常能看到弹出不同键盘类型,比如输入密码、数字、邮箱等,我们通过keyboardType属性来设置:

1
2
3
4
<View style={styles.container}>
<TextInput
keyboardType="number-pad"></TextInput>
</View>

number-pad键盘

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  也可以设置为email-address,输入邮箱,我们看下效果:

email-address键盘

  有一些特殊的值仅iOS下可用:

  我们看下url的效果:

url键盘

  在电商应用中我们经常会遇到填写收货地址、说明信息等多行文本,我们可以设置multiline为true,同时安卓上文本默认会垂直居中,我们可以添加textAlignVertical: 'top'的样式:

1
2
3
4
5
6
<TextInput
style={{
width: 300,
height: 120,
textAlignVertical: 'top'}}
multiline={true}></TextInput>

多行文本

  在登录页面中,我们需要将输入后的密码进行遮挡,确保我们录入信息的安全,可以使用secureTextEntry属性:

1
2
<TextInput
secureTextEntry={true}></TextInput>

  该属性在multiline={true}时不可使用,我们看下密码遮挡的效果:

密码输入

  我们在iOS上经常会遇到输入框首字符大写的情况,通过autoCapitalize属性可以自动将特定字符切换为大写:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
<TextInput
autoCapitalize="sentences"></TextInput>

  它有下面几个枚举值:

  • sentences:每句话的第一个字符(默认)。
  • characters:所有的字符。
  • words:每个单词的第一个字符。
  • none:不切换。

  我们看下不同枚举值的效果:

首字符大写切换

ScrollView

  ScrollView组件是用来当屏幕宽度或者高度不足以展示所有内容时进行滚动展示的组件,它的用法和View类似,但是必须有一个确定的高度,或者使用flex:1来让它填充整个屏幕:

1
2
3
4
5
6
7
8
9
10
11
<View>
<ScrollView style={{flex: 1}}>
{this.state.list.map((item, index) => {
return (
<View key={index} style={{height: 50}}>
<Text>scroll view {item}</Text>
</View>
);
})}
</ScrollView>
</View>

  ScrollView所有的子元素就可以在垂直方向上滚动,我们看下效果:

ScrollView

  通过horizontal属性,我们可以设置子元素在水平方向上排成一行进行滚动:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
4
5
<View>
<ScrollView style={{flex: 1}} horizontal={true}>
// ...
</ScrollView>
</View>

ScrollView水平滚动

交互组件

Button

  Button组件在很多UI库中也都有封装,像Element的el-button,AntDesign的a-button,在一些表单提交或者需要触发事件时使用。RN中Button有两个重要的props,title展示按钮的文字和onPress触发点击事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Index extends Component {
onClick = () => {
console.log('click');
};
render() {
return (
<View >
<Button
title="我是一个按钮"
onPress={() => this.onClick()}></Button>
</View>
);
}
}

  我们发现Button组件在Android和iOS上的表现形式有些区别:

Button组件

  Button的color属性可以改变按钮的颜色,在Android下改变背景色,iOS改变文本颜色:

1
2
3
4
<Button
color="red"
title="我是一个按钮"
onPress={() => this.onClick()}></Button>

带color的Button组件

  由于Button在不同平台的表现形式不一样,因此我们经常会使用View和Text封装自己的Button组件,或者使用社区组件,比如react-native-button或者react-native-elements的Button。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Button from 'react-native-button';

class Index extends Component {
onClick = () => {
console.log('click');
};
render() {
return (
<View >
<Button
style={{
fontSize: 20,
color: 'green',
width: 200,
height: 300,
backgroundColor: 'red',
borderRadius: 4,
}}
onPress={() => this.onClick()}>
Press Me!
</Button>
</View>
);
}
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

Switch

  Switch组件是一个跨平台通用的“开关”组件,在应用中很多时候会使用一个开关组件来控制某些功能是否开启;它提供的属性不多,value属性用来设置组件当前的值,onValueChange接收一个函数,当组件的值改变时调用此回调函数,回调函数的参数为组件新的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Index extends Component {
constructor() {
super();
this.state = {
isEnabled: true,
};
}
onClickSwitch = (val) => {
this.setState({
isEnabled: val,
});
};
render() {
return (
<View>
<Switch
value={this.state.isEnabled}
onValueChange={(val) => this.onClickSwitch(val)}></Switch>
<Text>{JSON.stringify(this.state.isEnabled)}</Text>
</View>
);
}
}

  我们看下switch组件的效果:

Switch组件

  switch组件还可以改变颜色,支持以下三种属性:

    谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  • thumbColor:开关上圆形按钮的背景颜色。在 iOS 上设置此颜色会丢失按钮的投影效果。
  • trackColor:关闭状态时的边框颜色(iOS)或背景颜色(Android)。
  • ios_backgroundColor:iOS下,自定义的背景颜色。

  我们给三个不同属性设置不同颜色,不要问我为什么选红和蓝,问就是自古红蓝出CP:

1
2
3
4
5
6
<Switch
thumbColor={'blue'}
trackColor={'red'}
ios_backgroundColor={'yellow'}
value={this.state.isEnabled}
onValueChange={val => this.onClickSwitch(val)}></Switch>

  我们看到thumbColor和ios_backgroundColor在iOS平台能够支持,但是trackColor表现的不是很明显:

iOS下Switch组件

  在安卓平台就只能看到thumbColor的效果:

安卓下Switch组件

Slider

  官方的Slider组件已经废弃,推荐安装使用社区的Slider:

1
2
3
yarn add @react-native-community/slider
# or
npm install @react-native-community/slider --save

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  如果在iOS,还需要在ios目录下运行pod install;Slider组件也是value属性设置进度,onValueChange值的回调函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Slider from '@react-native-community/slider';
class Index extends Component {
constructor() {
super();
this.state = {
slider: 0,
};
}
onChangeSlider1 = val => {
this.setState({
slider: val,
});
};
render(){
return (
<Slider
value={this.state.slider}
minimumValue={0}
maximumValue={1}
minimumTrackTintColor="#f0f0f0"
maximumTrackTintColor="#000000"
onValueChange={val => this.onChangeSlider(val)}
/>
)
}
}

  如果我们需要固定的几个值,可以使用step进行步进设置,step的值必须是在minimumValuemaximumValue范围之内的:

1
2
3
4
5
6
7
<Slider
value={this.state.slider}
step={2}
minimumValue={0}
maximumValue={10}
onValueChange={val => this.onChangeSlider2(val)}
/>

  Slider还支持tapToSeek属性,允许点击滑块轨迹来设置值,这个值默认为false,在安卓上没有影响:

1
2
3
4
5
6
<Slider
tapToSeek={true}
step={2}
minimumValue={0}
maximumValue={10}
/>

  我们看下Slider几个属性的效果:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

Slider

列表组件

FlatList

  FlatList组件是一个高性能的列表组件;我们前面说到,ScrollView也是一个滚动展示的组件,那么两者有什么区别呢?我们通过ScrollView的用法也能看出来,它会简单粗暴地把所有子元素一次性全部渲染出来;如果列表数据量小进行展示没有问题,但是如果一次性展示好几屏的数据,那么创建和渲染都会造成性能的浪费。

  而FlatList会惰性的渲染子元素,只在它们将要出现在屏幕中时开始渲染。除了高性能,FlatList还支持以下功能:

  • 完全跨平台。
  • 支持水平布局模式。
  • 行组件显示或隐藏时可配置回调事件。
  • 支持单独的头部组件。
  • 支持单独的尾部组件。
  • 支持自定义行间分隔线。
  • 支持下拉刷新。
  • 支持上拉加载。
  • 支持跳转到指定行(ScrollToIndex)。
  • 支持多列布局。

  FlatList组件必须的两个属性是datarenderItem,data是列表的数据源,接收一个数组数据;renderItem则从数据源中逐个解析数据,然后回调一个渲染组件格式给FlatList进行渲染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Index extends Component {
render() {
return (
<View>
<FlatList
data={[
{key: 'Devin'},
{key: 'Jackson'},
{key: 'James'}]}
keyExtractor={item => item.key}
renderItem={({item}) => (
<View style={{height: 50}}>
<Text>{item.key}</Text>
</View>
)}></FlatList>
</View>
);
}
}

  为了渲染的唯一性,我们还需要设置keyExtractor属性,指定item的key作为列表每一项的唯一标识;然后我们看下它的下拉刷新功能,如果设置了onRefresh事件,会在列表头部添加一个标准的RefreshControl控件,我们下拉时就会触发onRefresh回调:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Index extends Component {
constructor() {
super();
this.state = {
isLoading: false,
};
}
// 下拉刷新
loadData = () => {
this.setState({
isLoading: true,
});
// 模拟请求数据
setTimeout(() => {
this.setState({
isLoading: false,
});
}, 1500);
};
render() {
return (
<View>
<FlatList
data={[...]}
refreshing={this.state.isLoading}
onRefresh={() => {
//下拉刷新加载数据
this.loadData();
}}></FlatList>
</View>
);
}
}

  上拉加载的处理相对比较简单了,当列表被滚动到距离内容最底部不足onEndReachedThreshold属性的距离时,调用onEndReached回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Index extends Component {
// 加载更多数据
loadMoreData = () => {
// 请求接口,向data中添加数据
};
render() {
return (
<View>
<FlatList
data={[...]}
onEndReachedThreshold={10}
onEndReached={() => {
// 上拉加载
this.loadMoreData()
}}></FlatList>
</View>
);
}
}

  这样上拉加载后,如果数据请求完成,列表底部会突然一下多出很多数据,就很突然。

很突然

  我们可以用ListFooterComponent属性在列表底部渲染一个加载中的loading进行提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Index extends Component {
constructor() {
super();
this.state = {
// 正在加载更多数据
isMoreData: false,
};
}
renderLoadMoreView = () => {
return (
<View>
{this.state.isMoreData ? (
<Text>正在加载更多...</Text>
) : (<></>)}
</View>
);
};
render() {
return (
<View>
<FlatList
data={[...]}
ListFooterComponent={() => this.renderLoadMoreView()}></FlatList>
</View>
);
}
}

  我们看下整个下拉刷新和上拉加载的效果:

FlatList

  通过ItemSeparatorComponent属性,我们可以在行与行之间渲染分割线,分割线不会出现在第一行之前和最后一行之后。

1
2
3
4
5
6
7
8
<FlatList
ItemSeparatorComponent={() => (
<View
style={{
height: 1,
backgroundColor: 'red',
}}></View>
)}></FlatList>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  我们看下分割线的效果:

分割线

  FlatList提供了多个scroll函数,可以用来滚动到对应的位置,我们来看下scrollToIndex的用法,这个函数用于滚动到对应的index的位置;首先我们需要通过getItemLayout属性告诉Flatlist整个列表的一些信息:

1
2
3
4
5
6
7
<FlatList
ref={this.flatListRef}
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
index,
offset: ITEM_HEIGHT * index,
})}></FlatList>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  getItemLayout属性是一个可选的优化属性,用于避免动态测量内容尺寸的开销,scrollToIndex函数调用时需要设置这个属性;这里的ITEM_HEIGHT表示整个item的行高,如果我们像上面一样使用了ItemSeparatorComponent属性,需要把分割线的高度也算在ITEM_HEIGHT里面,否则滚动的距离会有偏差。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Index extends Component {
constructor() {
super();
this.flatListRef = React.createRef();
}
componentDidMount() {
setTimeout(() => {
this.flatListRef.current.scrollToIndex({
index: 6,
animated: true,
});
}, 1000);
}
render() {
return (
<FlatList
ref={this.flatListRef}></FlatList>
)
}

  我们看下scrollToIndex的效果:

scrollToIndex效果

SectionList

  SectionList组件和FlatList一样,都是列表组件,而且两者都是基于VirtualizedList进行封装的,不同的是SectionList有一个分组(section)的功能,类似于通讯录的功能,它支持下面功能:

  • 完全跨平台。
  • 行组件显示或隐藏时可配置回调事件。
  • 支持单独的头部组件。
  • 支持单独的尾部组件。
  • 支持自定义行间分隔线。
  • 支持分组的头部组件。
  • 支持分组的分隔线。
  • 支持多种数据源结构
  • 支持下拉刷新。
  • 支持上拉加载。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  SectionList的数据sections也是需要分组的,数据在每个分类的data数组中;SectionList还多了一个renderSectionHeader属性可以用来渲染section的头部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const DATA = [
{
title: 'A',
data: ['A1'],
},
{
title: 'B',
data: ['B1', 'B2'],
},
]
<SectionList
sections={DATA}
renderItem={({item}) => (
<View>
<Text>{item}</Text>
</View>
)}
renderSectionHeader={({section: {title}}) => (
<Text>
{title}
</Text>
)}></SectionList>

  在iOS上,每个section的头部会有吸顶的效果,Android上则没有该效果,我们看下SectionList效果:

SectionList

  下拉刷新和上拉加载的功能和FlatList组件相同,这里就不再赘述了。

VirtualizedList

  VirtualizedList组件是FlatListSectionList的底层实现;FlatList和SectionList使用起来更加的方便,如果这两个组件满足不了我们的需求,我们可以考虑VirtualizedList。

  VirtualizedList在屏幕窗口维护了一个固定高度的渲染窗口,在渲染窗口之外的元素用固定高度的空白元素进行渲染,同时让这个窗口监听内部的滚动事件,当元素离渲染窗口距离太远了,优先级较低,当元素距离可视区较近时,就自动获得了一个较高的渲染等级,通过这种方式逐步渲染;极大改善了渲染大批量数据时的内存消耗和使用性能。

虚拟列表

  它有下面几个必传的属性:

  • data:渲染数据
  • renderItem:根据行数据 data 渲染每一行的组件
  • getItem:通用的获取器,用来从任意类型的数据块中获取一个元素。
  • getItemCount:用来决定数据块中一共有多少元素。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  getItem属性是VirtualizedList特有的属性,它接收列表数据和对应的index,我们可以对列表中相应的数据进行处理然后返回出一个元素给到renderItem进行渲染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const DATA = [];
for (let index = 0; index < 50; index++) {
DATA.push({key: '' + index, label: '' + index});
}
const VirtualizedListComponent = () => {
return (
<View style={styles.container}>
<VirtualizedList
data={DATA}
getItemCount={() => DATA.length}
renderItem={({item, index}) => {
return (
<View>
<Text>{item.title}</Text>
</View>
);
}}
getItem={(list, index) => {
const item = list[index];
return {
id: item.key,
title: item.label,
};
}}
keyExtractor={item => item.key}></VirtualizedList>
</View>
);
};

  其他的属性和FlatList、SectionList基本相同,这里不再赘述了。

其他组件

ActivityIndicator

  ActivityIndicator组件用于显示一个圆形的loading提示符号,它有下面几个属性:

  • color:提示符的前景颜色,iOS上默认是#999
  • size:指示器的大小,默认为’small’,还可设为large。
  • animating:是否要显示指示器动画,默认为 true 表示显示,false 则隐藏。
  • hidesWhenStopped(iOS):在animating为false的时候,是否要隐藏指示器
1
2
3
4
5
6
<ActivityIndicator />
<ActivityIndicator size="large" />
<ActivityIndicator color="#0000ff" />
<ActivityIndicator animating={false} />
<ActivityIndicator animating={false}
hidesWhenStopped={false} />

  我们看下再iOS上的效果:

iOS下ActivityIndicator

  我们看到当animating为false时,隐藏ActivityIndicator的动效,整个指示器像是消失了一样;这是因为hidesWhenStopped默认为true,将指示器隐藏了,如果我们将该属性设为false,则会展示一个静止状态的指示器。我们再看下安卓下的指示器效果:

安卓下ActivityIndicator

  hidesWhenStopped属性在安卓下没有起到作用。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

StatusBar

  StatusBar组件用于控制应用顶部的状态栏,包含时间,运营商名称,网络情况,电池情况等;由于它可以在任何视图加载,当有多个StatusBar组件时,后加载的组件会覆盖前面的,因此使用时需要注意;

1
2
3
4
5
6
7
<View>
<StatusBar backgroundColor="blue" barStyle="light-content" />
<View>
<StatusBar hidden={route.statusBarHidden} />
...
</View>
</View>

我们分别来看下它支持的属性。

  animated设置当状态栏的状态发生变化时,是否需要加入动画;动画支持backgroundColor、barStyle和hidden等属性的变化。hidden属性用于设置状态栏是否隐藏:

1
2
3
4
<View>
<StatusBar animated={true} hidden={this.state.hidden}></StatusBar>
<Text>Status Bar hidden</Text>
</View>

  我们看下改变hidden带上animated的效果:

hidden效果

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  如果animated为false则没有动画效果,切换的比较生硬了;backgroundColor属性设置状态栏的背景颜色,仅支持安卓:

1
2
3
<View>
<StatusBar backgroundColor="blue" />
</View>

backgroundColor效果

  barStyle用于设置状态栏文字的颜色,其值是枚举类型enum(‘default’, ‘light-content’, ‘dark-content’),仅针对Android 6.0以上版本:

barStyle安卓效果

  networkActivityIndicatorVisible属性是一个Boolean类型,在iOS平台上指定显示一个网络活动提示符:

networkActivityIndicatorVisible效果

  translucent属性适用于Android平台,用来指定状态栏是否透明。

1
<StatusBar translucent={ true } backgroundColor="transparent" />

TouchableWithoutFeedback

  TouchableWithoutFeedback组件是Touchable系列组件中最基本的一个组件,只响应用户的点击事件不会做任何UI上的改变,这就是它的名字WithoutFeedback,同时它也不能设置style样式;因此除非有特别的原因,否则一般很少用这个组件,它支持以下属性:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  • onPress:当触摸操作结束时调用
  • onLongPress:当用户长时间按压组件(长按效果)的时候调用
  • onPressIn:当用户开始点击按钮时调用
  • onPressOut:当用户点击结束时调用
  • disabled:bool值默认false,如果设为 true,则禁止此组件的一切交互。
1
2
3
4
5
6
7
8
9
<TouchableWithoutFeedback
onPress={this._onPress}
onLongPress={this._onLongPress}
onPressIn={this._onPressIn}
onPressOut={this._onPressOut}>
<View>
<Text>点击按钮</Text>
</View>
</TouchableWithoutFeedback>

  需要注意的是,Touchable系列组件都只支持一个子元素(有且仅有一个);如果我们想要放置多个元素,可以用一个View包裹它们。

TouchableOpacity

  TouchableHighlight组件是Touchable系列组件中比较常用的,和它的名字Opacity一样,它是通过在按下去改变视图的不透明度来表示按钮被点击的,相比TouchableHighlight少了一个额外的颜色变化,它支持设置style样式。

  它最重要的一个属性就是activeOpacity,表示按钮被触摸操作激活时以多少不透明度显示(0 到 1 之间),默认值为 0.2,数值越小透明效果效果越明显,我们可以设置一个大一点的数值:

1
2
3
4
5
<TouchableOpacity activeOpacity={0.6} style={{marginTop: 40}}>
<View>
<Text>点击TouchableOpacity按钮</Text>
</View>
</TouchableOpacity>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  我们看下TouchableOpacity点出触发的效果:

TouchableOpacity效果

TouchableHighlight

  TouchableHighlight组件也是比较常用的点击按钮,它在TouchableWithoutFeedback的基础上添加了一些UI上的扩展,当按钮点击触发时,该视图的不透明度会降低,同时会看到相应的颜色;它支持以下属性:

  • activeOpacity:被触摸操作激活时以多少不透明度显示
  • underlayColor:按钮点击触发时显示出来的底层的颜色
  • onHideUnderlay:底层的颜色被隐藏的时候调用。
  • 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  • onShowUnderlay:当底层的颜色被显示的时候调用。
1
2
3
4
5
6
7
8
9
10
11
<TouchableHighlight
underlayColor={'orange'}
activeOpacity={0.5}
onPress={this._onPress}
onHideUnderlay={this._hideUnderlay}
onShowUnderlay={this._showUnderlay}
style={{backgroundColor: '#4E6EF2'}}>
<View>
<Text>点击TouchableHighlight按钮</Text>
</View>
</TouchableHighlight>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  我们看下TouchableHighlight的效果:

TouchableHighlight效果

  Modal组件是一种简单的覆盖在屏幕最上层显示内容的组件,且用户无法对下层的UI进行操作,因此我们可以随意在Modal上进行UI操作,它有以下属性:

  • visible:bool值,决定modal是否显示
  • transparent:bool值,指背景是否透明,默认为false,背景为白色
  • animationType:指定了 modal 的动画类型
  • onShow:onShow回调函数会在modal显示时调用。
  • onDismiss:onDismiss回调会在modal被关闭时调用

  Modal组件通过visible属性来控制是否出现;transparent设置背景的透明度,如果为false,则整个背景都会变成不透明的白色覆盖;因此一般设置为true,再在根组件设置背景颜色为rbga(0,0,0,0.5)增加黑色的蒙层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Index extends Component {
constructor() {
super();
this.state = {
visible: false,
};
}
onClick = () => {
this.setState({
visible: !this.state.visible,
});
};
render() {
return (
<View>
<Button
title="显示Model"
onPress={this.onClick}></Button>
<Modal
visible={this.state.visible}
animationType="none"
transparent={true}>
<View
style={{
backgroundColor: 'rgba(0,0,0,0.3)',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<View style={{width: 200, height: 200, backgroundColor: '#fff'}}>
<Text>标题</Text>
<View>
<Button title="关闭" onPress={this.onClick}></Button>
</View>
</View>
</View>
</Modal>
</View>
)
}
}

  我们看下三种动画的效果:

Modal效果

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

iOS组件

SafeAreaView

  SafeAreaView组件用于在一个安全的可视区域内渲染内容;这个组件存在的目的就是针对iPhone X这样带有齐刘海的全面屏设备,为了避免页面内容渲染到刘海范围内。本组件目前仅支持iOS设备以及iOS 11或更高版本。

  它的用法也非常简单,只要把我们原有的页面视图通过SafeAreaView组件包裹起来,同时设置flex: 1,一般可以在根组件上设置:

1
2
3
4
5
6
7
const SafeAreaViewComponent = () => {
return (
<SafeAreaView style={{flex: 1}}>
<Text>Page Content</Text>
</SafeAreaView>
);
};

  我们看下SafeAreaView组件不使用和使用的两种效果。

SafeAreaView效果

  为了在样式上适配iPhone X的屏幕,我们可以在utils中封装一个函数,来单独判断iPhone X:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import {Dimensions, Platform} from 'react-native';

export let screenW = Dimensions.get('window').width;
export let screenH = Dimensions.get('window').height;

// iPhoneX
const X_WIDTH = 375;
const X_HEIGHT = 812;

export function isIphoneX() {
return (
Platform.OS === 'ios' &&
((screenH === X_HEIGHT && screenW === X_WIDTH) ||
(screenH === X_WIDTH && screenW === X_HEIGHT))
);
}

export function ifIphoneX(iphoneXStyle, iosStyle, androidStyle) {
if (isIphoneX()) {
return iphoneXStyle;
} else if (Platform.OS === 'ios') {
return iosStyle;
} else {
if (androidStyle) return androidStyle;
return iosStyle;
}
}

  我们在样式中就可以直接使用ifIphoneX函数:

1
2
3
4
5
6
7
8
9
10
11
12
const styles = StyleSheet.create({  
topBar: {
backgroundColor: '#ffffff',
...ifIphoneX({
// iPhone X样式
paddingTop: 44
}, {
// iOS样式
paddingTop: 20
})
},
})

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

安卓组件

TouchableNativeFeedback

  TouchableNativeFeedback组件是Touchable系列组件中最后一个组件,可以在Android 5.0以后触摸实现水波纹的效果,因此也只能在Android平台使用。

  我们可以通过background属性来自定义原生触摸操作反馈的背景,它接受一个有着type属性和一些基于type属性的额外数据的对象,这个对象推荐通过本组件的几个静态方法来创建:

  • SelectableBackground:会创建一个对象,表示安卓主题默认的对于被选中对象的背景
  • SelectableBackgroundBorderless:会创建一个对象,表示安卓主题默认的对于被选中的无边框对象的背景
  • Ripple:会创建一个对象,当按钮被按下时产生一个涟漪状的背景,你可以通过 color 参数来指定颜色

  上面的效果太绕口,我看下SelectableBackground的用法:

1
2
3
4
5
6
<TouchableNativeFeedback
background={TouchableNativeFeedback.SelectableBackground()}>
<View style={{width: 300, height: 100, backgroundColor: 'red'}}>
<Text>TouchableNativeFeedback</Text>
</View>
</TouchableNativeFeedback>

  我们看到在View按钮点击时,有水波纹的效果:

SelectableBackground效果

  SelectableBackgroundBorderless的效果则会超过整个按钮的边框限制:

1
2
3
4
<TouchableNativeFeedback
background={TouchableNativeFeedback.SelectableBackgroundBorderless()}>
// ...
</TouchableNativeFeedback>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

SelectableBackgroundBorderless效果

  我们上面创建的波纹都是默认的黑色,Ripple函数支持传入一个color参数来指定颜色,它的第二个参数borderless也能设置涟漪是否扩散到按钮之外,达到和SelectableBackgroundBorderless一样的效果:

1
static Ripple(color: string, borderless: boolean, rippleRadius: ?number)
1
2
3
4
<TouchableNativeFeedback
background={TouchableNativeFeedback.Ripple('blue', false)}>
// ...
</TouchableNativeFeedback>

  效果如下:

Ripple效果


谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

本网所有内容文字和图片,版权均属谢小飞所有,任何媒体、网站或个人未经本网协议授权不得转载、链接、转贴或以其他方式复制发布/发表。如需转载请关注公众号【前端壹读】后回复【转载】。