本小节我们来学习 RN 内置组件中的容器组件。容器组件大致如下:
- View 组件
- Text 组件
- ScrollView 组件
- Touchable 组件
View 组件#
在 RN 中,View 容器组件支持 Flexbox 布局、样式、触摸事件处理和一些无障碍功能,它可以被放到其他容器组件里面,也可以包含任意多个子组件。
无论是 iOS 还是 Android,View 组件都会直接对应平台的原生视图,其作用等同于 iOS 的 UIView 或者 Android 的 ViewGroup。
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 AppText 组件#
在 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 ViewBoxesWithColorAndTextText 的嵌套主要是为了满足文本某些特定场景的需求。例如在一些信息展示类的场景中,通常需要将同一段落的部分文字的字号,颜色另外设置值,以达到视觉上的区分。
以前在 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)内嵌 Text 的 numberOfLines 属性会失效。
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 中的 html 或 body 标签,浏览器中的页面之所以能上下滚动,就是因为 html 或 body 标签默认有一个 overflow-y: scroll 的属性,如果你把标签的属性设置为 overflow-y: hidden,页面就不能滚动了。
ReactNative 的 ScrollView 组件在 Android 的底层实现用的是 ScrollView 和 HorizontalScrollView,在 iOS 的底层实现用的是 UIScrollView。
使用 ScrollView 组件时,必须要有一个确定的高度才能正常工作。如果不知道容器的准确高度,可以将 ScrollView 组件的样式设置为 {flex: 1},让其自动填充父容器的空余空间。
ScrollView 通常包裹在视图的外面,用于控制视图的滚动,并且很多时候我们并不直接给 ScrollView 设置固定高度或宽度,而是给其父组件设置固定高度或宽度。
后期我们会使用 ScrollView 组件来封装一个轮播图的自定义组件。
API 文档地址:https://reactnative.dev/docs/scrollview
Touchable 组件#
在 RN 应用开发中,点击和触摸都是比较常见的交互行为,不过并不是所有的组件都支持点击事件。为了给这些不具备点击响应的组件绑定点击事件,RN 提供了 Touchable 系列组件。
正如前面所述,Touchable 系列组件并不是单指某一个组件,一共有 4 个,其中跨平台的有 3 个:
-
TouchableHighlight
Touchable 系列组件中比较常用的一个,它是在 TouchableWithoutFeedback 的基础上添加了一些 UI 上的扩展,即当手指按下的时候,该视图的不透明度会降低,同时会看到视图变暗或者变亮,该标签可以添加 style 样式属性。
-
TouchableOpacity
完全和 TouchableHighlight 相同,只是不可以修改颜色,只能修改透明度。
-
TouchableWithoutFeedback
最基本的一个 Touchable 组件,只响应用户的点击事件,不会做任何 UI 上的改变,所以不用添加 style 样式属性,加了也没效果。
另外在 Android 平台上支持一个叫 TouchableNativeFeedback 的组件:
- TouchableNativeFeedback:为了支持 Android 5.0 的触控反馈而新增的组件。该组件在 TouchableWithoutFeedback 所支持的属性的基础上增加了触摸的水波纹效果。可以通过 background 属性来自定义原生触摸操作反馈的背景。(仅限 Android 平台,IOS 平台使用会报错)
示例如下:
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', },})