Skip to content

容器组件

本小节我们来学习 RN 内置组件中的容器组件。容器组件大致如下:

View 组件#

RN 中,View 容器组件支持 Flexbox 布局、样式、触摸事件处理和一些无障碍功能,它可以被放到其他容器组件里面,也可以包含任意多个子组件。

无论是 iOS 还是 AndroidView 组件都会直接对应平台的原生视图,其作用等同于 iOSUIView 或者 AndroidViewGroup

API 文档地址:https://reactnative.dev/docs/view

来看一个简单的示例:

import React from 'react'
import { View, StyleSheet } from 'react-native'
const App = () => {
return (
<View
style={{
flexDirection: 'row',
padding: 20,
flexWrap: 'wrap',
justifyContent: 'space-between',
borderWidth: 1,
}}
>
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item} />
</View>
)
}
const styles = StyleSheet.create({
item: {
width: 50,
height: 50,
borderWidth: 1,
margin: 10,
},
})
export default App

Text 组件#

RN 中,Text 是一个用来显示文本内容的组件,也是使用频率极高的组件,它支持文本和样式的嵌套以及触摸事件的处理。

import React, { useState } from 'react'
import { View, Text, StyleSheet } from 'react-native'
const TextInANest = () => {
const [titleText, setTitleText] = useState("Bird's Nest")
const bodyText = 'This is not really a bird nest.'
const onPressTitle = () => {
setTitleText("Bird's Nest [pressed]")
}
return (
<View style={styles.container}>
<Text style={styles.baseText}>
{/* 除了继承 baseText 样式以外,有自己的样式 */}
<Text style={styles.titleText} onPress={onPressTitle}>
{titleText}
{'\n'}
{'\n'}
</Text>
{/* 继承 baseText 的样式 */}
<Text numberOfLines={5}>{bodyText}</Text>
</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
baseText: {
fontSize: 30,
},
titleText: {
fontSize: 20,
fontWeight: '400',
},
})
export default TextInANest

从布局上讲,Text 组件没有类似于 CSS 行内元素这样的概念,所以单个 Text 组件也是独占一行(因为它相当于网页中的 p 元素),但它属于 Flex 布局范畴,可以使用 flexDirection 属性设置行内并列的效果,例如:

import React from 'react'
import { View, Text } from 'react-native'
const ViewBoxesWithColorAndText = () => {
return (
<View style={{ flex: 1, justifyContent: 'center' }}>
<View>
<Text style={{ fontSize: 40, borderWidth: 1 }}>1</Text>
<Text style={{ fontSize: 40, borderWidth: 1 }}>2</Text>
<Text style={{ fontSize: 40, borderWidth: 1 }}>3</Text>
</View>
<View style={{ flexDirection: 'row' }}>
<Text style={{ fontSize: 40, borderWidth: 1 }}>1</Text>
<Text style={{ fontSize: 40, borderWidth: 1 }}>2</Text>
<Text style={{ fontSize: 40, borderWidth: 1 }}>3</Text>
</View>
</View>
)
}
export default ViewBoxesWithColorAndText

Text 的嵌套主要是为了满足文本某些特定场景的需求。例如在一些信息展示类的场景中,通常需要将同一段落的部分文字的字号,颜色另外设置值,以达到视觉上的区分。

以前在 PC 端书写网页时,我们是通过嵌套 span 标签来处理此需求的,而在 RN 中则是使用 Text 的嵌套来实现。

import React from 'react'
import { Text, StyleSheet, View } from 'react-native'
const BoldAndBeautiful = () => {
return (
<View style={styles.container}>
<Text>
<Text style={{ fontSize: 28, color: '#999' }}>First part</Text>
<Text>and</Text>
<Text style={{ fontSize: 20, color: 'red' }}>second part</Text>
</Text>
<View>
<Text>First part and </Text>
<Text>second part</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
baseText: {
fontWeight: 'bold',
},
innerText: {
color: 'red',
},
})
export default BoldAndBeautiful

不过 RN 中的 Text 嵌套写法也存在以下的问题

(1)被嵌套组件与位置相关的 style 样式几乎都不生效。

import React from "react";
import { Text, View } from "react-native";
const BoldAndBeautiful = () => {
return (
<View style={{ marginTop: 20 }}>
<Text style={{ fontSize: 28 }}>
我是一段普通文字
<Text style={{ paddingLeft: 10, borderWidth: 1 }}>左Padding 10</Text>
<Text style={{ marginLeft: 10, borderWidth: 1 }}>左Margin 10</Text>
</Text>
</View>
);
};
export default BoldAndBeautiful;

(2)内嵌 TextnumberOfLines 属性会失效。

import React from "react";
import { Text, View } from "react-native";
const BoldAndBeautiful = () => {
return (
<View style={{ marginTop: 20 }}>
<Text style={{ fontSize: 28, borderWidth: 1 }}>
1.{" "}
<Text numberOfLines={2} ellipsizeMode={"tail"}>
我是一段普通文字我是一段普通文字我是一段普通文字我是一段普通文字我是一段普通文字
</Text>
</Text>
</View>
);
};
export default BoldAndBeautiful;

如果使用不同的 Text 组件设置不同的字号,那么对齐的方式仍然是使用 Flex 布局对齐。

例如垂直居中:

import React from 'react'
import { Text, View } from 'react-native'
const App = () => {
return (
<View style={{ marginTop: 20 }}>
<View
style={{
flexDirection: 'row',
marginTop: 10,
borderWidth: 1,
alignItems: 'center',
}}
>
<Text style={{ fontSize: 20 }}>我是文字</Text>
<Text style={{ fontSize: 30 }}>我是大一点的文字</Text>
</View>
<View
style={{
flexDirection: 'row',
marginTop: 10,
borderWidth: 1,
alignItems: 'flex-start',
}}
>
<Text style={{ fontSize: 20 }}>我是文字</Text>
<Text style={{ fontSize: 30 }}>我是大一点的文字</Text>
</View>
</View>
)
}
export default App

不过需要注意的是,由于字号大小不一,小字号文字的上边距会略小,例如将上例中 alignItems 值修改为 flex-start,但是由于不同的字体大小可以明显的看到上边距是不同的。如果想要不同字体大小的文字边距相同,可以利用 padding 进行微调。

API 文档地址:https://reactnative.dev/docs/text

ScrollView 组件#

ScrollView 是一个支持横向或竖向的滚动组件,几乎所有页面都会用到。

ScrollView 组件类似于 Web 中的 htmlbody 标签,浏览器中的页面之所以能上下滚动,就是因为 htmlbody 标签默认有一个 overflow-y: scroll 的属性,如果你把标签的属性设置为 overflow-y: hidden,页面就不能滚动了。

ReactNativeScrollView 组件在 Android 的底层实现用的是 ScrollViewHorizontalScrollView,在 iOS 的底层实现用的是 UIScrollView

使用 ScrollView 组件时,必须要有一个确定的高度才能正常工作。如果不知道容器的准确高度,可以将 ScrollView 组件的样式设置为 {flex: 1},让其自动填充父容器的空余空间。

ScrollView 通常包裹在视图的外面,用于控制视图的滚动,并且很多时候我们并不直接给 ScrollView 设置固定高度或宽度,而是给其父组件设置固定高度或宽度。

后期我们会使用 ScrollView 组件来封装一个轮播图的自定义组件。

API 文档地址:https://reactnative.dev/docs/scrollview

Touchable 组件#

RN 应用开发中,点击和触摸都是比较常见的交互行为,不过并不是所有的组件都支持点击事件。为了给这些不具备点击响应的组件绑定点击事件,RN 提供了 Touchable 系列组件。

正如前面所述,Touchable 系列组件并不是单指某一个组件,一共有 4 个,其中跨平台的有 3 个:

另外在 Android 平台上支持一个叫 TouchableNativeFeedback 的组件:

示例如下:

import React, { useState } from 'react'
import {
View,
StyleSheet,
TouchableOpacity,
TouchableNativeFeedback,
TouchableHighlight,
TouchableWithoutFeedback,
Text,
} from 'react-native'
export default function App() {
const [count, setCount] = useState(0)
return (
<View style={styles.container}>
<TouchableHighlight
style={styles.touchableStyle}
onPress={() => setCount(count + 1)}
>
<Text style={styles.txtStyle}>点击加1</Text>
</TouchableHighlight>
<TouchableOpacity
style={styles.touchableStyle}
onPress={() => setCount(count + 1)}
>
<Text style={styles.txtStyle}>点击加1</Text>
</TouchableOpacity>
<TouchableWithoutFeedback onPress={() => setCount(count + 1)}>
<View style={styles.touchableStyle}>
<Text style={styles.txtStyle}>点击加1</Text>
</View>
</TouchableWithoutFeedback>
<TouchableNativeFeedback onPress={() => setCount(count + 1)}>
<View style={styles.touchableStyle}>
<Text style={styles.txtStyle}>点击加1</Text>
</View>
</TouchableNativeFeedback>
<Text style={[styles.countText]}>{count !== 0 ? count : null}</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#F5FCFF',
},
touchableStyle: {
width: 300,
height: 38,
borderRadius: 5,
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#06C1AE',
marginTop: 20,
marginBottom: 20,
},
txtStyle: {
color: '#ffffff',
textAlign: 'center',
fontSize: 18,
},
countText: {
marginTop: 10,
alignSelf: 'center',
fontSize: 38,
color: '#06C1AE',
},
})