设计
Playground:yoga playground
Style
所有的 core components 都接受一个名为 style
的 prop 。style
的 name
和 value
与 web 上使用的 CSS 类似,不同的是 name
使用了驼峰命名法,比如 backgroundColor
,而不是 CSS 上的 background-color
。
style
prop 可以是一个 plain old JavaScript object
,这是我们的示例代码中经常使用的。你也可以传递一个 style
数组,数组中的最后一个样式具有优先权 (precedence) ,因此可以使用它来继承样式。
当组件的样式变得复杂时,通常会使用 StyleSheet.create
在一个地方定义所有的 style
。
示例:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const LotsOfStyles = () => {
return (
<View style={styles.container}>
<Text style={styles.red}>just red</Text>
<Text style={styles.bigBlue}>just bigBlue</Text>
<Text style={[styles.bigBlue, styles.red]}>bigBlue, then red</Text>
<Text style={[styles.red, styles.bigBlue]}>red, then bigBlue</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
marginTop: 50,
},
bigBlue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
});
export default LotsOfStyles;
一种常见的模式是让你的组件接受一个 style
prop ,然后此 style
会作用于这个组件的子组件。通过这种方式来实现类似 CSS 中的样式级联 (cascade) 。
Height and Width
Fixed Dimensions
设置组件尺寸的的一般方式是在 style
中添加固定的 width
和 height
。React Native 中的所有维度都是无单位的,表示与密度无关的像素。
示例:
import React from 'react';
import { View } from 'react-native';
const FixedDimensionsBasics = () => {
return (
<View>
<View style={{
width: 50, height: 50, backgroundColor: 'powderblue'
}} />
<View style={{
width: 100, height: 100, backgroundColor: 'skyblue'
}} />
<View style={{
width: 150, height: 150, backgroundColor: 'steelblue'
}} />
</View>
);
};
export default FixedDimensionsBasics;
Flex Dimensions
在组件的 style
中使用 flex
以根据可用空间的大小来动态地展开和收缩组件。通常你会使用 flex: 1
,它告诉组件填满所有可用空间,并在具有相同父组件的其他组件之间平均共享。给定的 flex
值越大,组件所占的空间比就越高。
import React from 'react';
import { View } from 'react-native';
const FlexDimensionsBasics = () => {
return (
// Try removing the `flex: 1` on the parent View.
// The parent will not have dimensions, so the children can't expand.
// What if you add `height: 300` instead of `flex: 1`?
<View style={{ flex: 1 }}>
<View style={{ flex: 1, backgroundColor: 'powderblue' }} />
<View style={{ flex: 2, backgroundColor: 'skyblue' }} />
<View style={{ flex: 3, backgroundColor: 'steelblue' }} />
</View>
);
};
export default FlexDimensionsBasics;
A component can only expand to fill available space if its parent has dimensions greater than 0. If a parent does not have either a fixed width and height or flex, the parent will have dimensions of 0 and the flex children will not be visible.
在这个例子中,如果把父组件的 style={{ flex: 1 }
去掉,由于父组件没有了任何 dimension ,因此所有的子组件就都无法展开了;如果把父组件的 flex: 1
改成 height: 300
,能正常显示,父组件的高度变成了 300px
,子组件也相应缩小了。
Percentage Dimensions
文档中说:
Similar to flex dimensions, percentage dimensions require parent with a defined size.
但我把父组件的 style={{ height: '100%' }}
去掉后,子组件还是能正常展示,为什么呢?
import React from 'react';
import { View } from 'react-native';
const PercentageDimensionsBasics = () => {
// Try removing the `height: '100%'` on the parent View.
// The parent will not have dimensions, so the children can't expand.
return (
<View style={{ height: '100%' }}>
<View style={{
height: '15%', backgroundColor: 'powderblue'
}} />
<View style={{
width: '66%', height: '35%', backgroundColor: 'skyblue'
}} />
<View style={{
width: '33%', height: '50%', backgroundColor: 'steelblue'
}} />
</View>
);
};
export default PercentageDimensionsBasics;
Layout with Flexbox
Flexbox works the same way in React Native as it does in CSS on the web, with a few exceptions. The defaults are different, with
flexDirection
defaulting tocolumn
instead ofrow
,alignContent
defaulting toflex-start
instead ofstretch
,flexShrink
defaulting to0
instead of1
, theflex
parameter only supporting a single number.
一个组件可以使用 Flexbox algorithm 来指定它的子组件的布局。Flexbox 的设计宗旨是在不同的屏幕尺寸上提供一致的布局。常用的有 flexDirection
, alignItems
, justifyContent
。
Flex
flex 定义了组件的填充方式。
示例:
import React from "react";
import { StyleSheet, Text, View } from "react-native";
const Flex = () => {
return (
<View style={[styles.container, {
// Try setting `flexDirection` to `"row"`.
flexDirection: "column"
}]}>
<View style={{ flex: 1, backgroundColor: "red" }} />
<View style={{ flex: 2, backgroundColor: "darkorange" }} />
<View style={{ flex: 3, backgroundColor: "green" }} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
});
export default Flex;
Flex Direction
flexDirection 用于控制子组件的布局方向。
column(默认值)
row
column-reverse
row-reverse
示例:
import React, { useState } from "react";
import { StyleSheet, Text, TouchableOpacity, View, SafeAreaView } from "react-native";
const FlexDirectionBasics = () => {
const [flexDirection, setflexDirection] = useState("column");
return (
<PreviewLayout
label="flexDirection"
values={["column", "row", "row-reverse", "column-reverse"]}
selectedValue={flexDirection}
setSelectedValue={setflexDirection}
>
<View
style={[styles.box, { backgroundColor: "powderblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "steelblue" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<SafeAreaView style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={[styles.container, { [label]: selectedValue }]}>
{children}
</View>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default FlexDirectionBasics;
Layout Direction
Layout direction 指定层次结构中的子元素和文本的布局方向。
LTR(默认值):从左向右布局。
RtL:从右向左布局。
示例:
import React, { useState } from "react";
import { View, TouchableOpacity, Text, StyleSheet, SafeAreaView } from "react-native";
const DirectionLayout = () => {
const [direction, setDirection] = useState("ltr");
return (
<PreviewLayout
label="direction"
selectedValue={direction}
values={["ltr", "rtl"]}
setSelectedValue={setDirection}>
<View
style={[styles.box, { backgroundColor: "powderblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "steelblue" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<SafeAreaView style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={[styles.container, { [label]: selectedValue }]}>
{children}
</View>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default DirectionLayout;
Justify Content
justifyContent 描述了如何在其容器的主轴内对齐子容器。
flex-start
:默认值;flex-end
center
space-between
space-around
:均匀排列每个元素,每个元素周围分配相同的空间;space-evenly
:均匀排列每个元素,每个元素之间的间隔相等。
可参考 MDN 中的 CSS 的文档 。
import React, { useState } from "react";
import { View, SafeAreaView, TouchableOpacity, Text, StyleSheet } from "react-native";
const JustifyContentBasics = () => {
const [justifyContent, setJustifyContent] = useState("flex-start");
return (
<PreviewLayout
label="justifyContent"
selectedValue={justifyContent}
values={[
"flex-start",
"flex-end",
"center",
"space-between",
"space-around",
"space-evenly",
]}
setSelectedValue={setJustifyContent}
>
<View
style={[styles.box, { backgroundColor: "powderblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "steelblue" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<SafeAreaView style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[styles.button, selectedValue === value && styles.selected]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={[styles.container, { [label]: selectedValue }]}>
{children}
</View>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
// 可以尝试改成 row
// flexDirection: "column",
marginTop: 8,
backgroundColor: "aliceblue",
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default JustifyContentBasics;
Align Items
alignItems 描述了子组件在容器横轴上的排列方式。alignItems
和 justifyContent
非常相似,不同的是 justifyContent
应用于主轴 (main axis) ,alignItems
应用于横轴 (cross axis) 。
stretch
:默认值,拉伸容器中的子组件以匹配容器横轴的height
;flex-start
flex-end
center
baseline
说明:要使 stretch
生效,子组件不能设置 fixed dimension
。
import React, { useState } from "react";
import {
View,
SafeAreaView,
TouchableOpacity,
Text,
StyleSheet,
} from "react-native";
const AlignItemsLayout = () => {
const [alignItems, setAlignItems] = useState("stretch");
return (
<PreviewLayout
label="alignItems"
selectedValue={alignItems}
values={[
"stretch",
"flex-start",
"flex-end",
"center",
"baseline",
]}
setSelectedValue={setAlignItems}
>
<View
style={[styles.box, { backgroundColor: "powderblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[
styles.box,
{
backgroundColor: "steelblue",
width: "auto",
minWidth: 50,
},
]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<SafeAreaView style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={[styles.container, { [label]: selectedValue }, ]}>
{children}
</View>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
minHeight: 200,
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default AlignItemsLayout;
Align Self
alignSelf 控制子组件在横轴上的排列方式,且会覆盖其父组件设置的 alignItems
。
示例:
import React, { useState } from "react";
import { View, SafeAreaView, TouchableOpacity, Text, StyleSheet } from "react-native";
const AlignSelfLayout = () => {
const [alignSelf, setAlignSelf] = useState("stretch");
return (
<PreviewLayout
label="alignSelf"
selectedValue={alignSelf}
values={["stretch", "flex-start", "flex-end", "center", "baseline"]}
setSelectedValue={setAlignSelf}>
<View
style={[styles.box, {
alignSelf,
width: "auto",
minWidth: 50,
backgroundColor: "powderblue",
}]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "steelblue" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<SafeAreaView style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={styles.container}>
{children}
</View>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
minHeight: 200,
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default AlignSelfLayout;
Align Content
alignContent defines the distribution of lines along the cross-axis. This only has effect when items are wrapped to multiple lines using flexWrap
.
import React, { useState } from "react";
import { View, SafeAreaView, TouchableOpacity, Text, StyleSheet } from "react-native";
const AlignContentLayout = () => {
const [alignContent, setAlignContent] = useState("flex-start");
return (
<PreviewLayout
label="alignContent"
selectedValue={alignContent}
values={[
"flex-start",
"flex-end",
"stretch",
"center",
"space-between",
"space-around",
]}
setSelectedValue={setAlignContent}>
<View
style={[styles.box, { backgroundColor: "orangered" }]}
/>
<View
style={[styles.box, { backgroundColor: "orange" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumseagreen" }]}
/>
<View
style={[styles.box, { backgroundColor: "deepskyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumturquoise" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumslateblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "purple" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<SafeAreaView style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={[styles.container, { [label]: selectedValue }, ]}>
{children}
</View>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
flexWrap: "wrap",
marginTop: 8,
backgroundColor: "aliceblue",
maxHeight: 400,
},
box: {
width: 50,
height: 80,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default AlignContentLayout;
Flex Wrap
flexWrap 属性设置在容器上,它控制子组件沿主轴溢出时发生的情况。默认情况下,子元素被强制缩为一行(which can shrink elements)。如果允许 wrap ,则需要沿着主轴将物品 wrap 成多条线。
在使用 wrap 时,可以使用 alignContent
指定如何在容器中放置行。
import React, { useState } from "react";
import { View, SafeAreaView, TouchableOpacity, Text, StyleSheet } from "react-native";
const FlexWrapLayout = () => {
const [flexWrap, setFlexWrap] = useState("wrap");
return (
<PreviewLayout
label="flexWrap"
selectedValue={flexWrap}
values={["wrap", "nowrap"]}
setSelectedValue={setFlexWrap}>
<View
style={[styles.box, { backgroundColor: "orangered" }]}
/>
<View
style={[styles.box, { backgroundColor: "orange" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumseagreen" }]}
/>
<View
style={[styles.box, { backgroundColor: "deepskyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumturquoise" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumslateblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "purple" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<SafeAreaView style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View
style={[
styles.container,
{ [label]: selectedValue },
]}
>
{children}
</View>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
padding: 8,
backgroundColor: "aliceblue",
maxHeight: 300,
},
box: {
width: 50,
height: 80,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default FlexWrapLayout;
Flex Basis, Grow, and Shrink
flexBasis
flexBasis 是使用与轴无关 (axis-independent)
的方式为子组件设置沿主轴方向的默认大小。为子组件设置 flexBasis
与以下的操作效果一样:
如果父组件设置了
flexDirection: row
,设置子组件的flexBasis
相当于为子组件设置了width
;如果父组件设置了
flexDirection: column
,设置子组件的flexBasis
相当于为子组件设置了height
。
flexBasis
设置的大小是子组件的默认大小,是 flexGrow
和 flexShrink
计算完成之前使用的值。
flexGrow
flexGrow 设置了一个 flex 项主尺寸的 flex 增长系数。它指定了 flex 容器中剩余空间的多少应该分配给项目( flex 增长系数)。默认值是 0
。
主尺寸是项的宽度或高度,这取决于 flexDirection
值。
剩余的空间是 flex 容器的大小减去所有 flex 项的大小加起来的大小。如果所有的兄弟项目都有相同的 flexGrow
系数,那么所有的项目将获得相同的剩余空间,否则将根据不同的 flexGrow
系数定义的比例进行分配。
flexGrow 与其他的 flex 属性 flexShrink
和 flexBasis
一起使用,通常使用 flex 速记来定义,以确保所有的值都被设置。默认值是 0
( Web 上默认值是 1
)。
flexShrink
flexShrink
属性指定了 flex 元素的收缩规则。flex 元素仅在默认宽度之和大于容器的时候才会发生收缩,其收缩的大小是依据 flexShrink
的值。
flexBasis、flexGrow、flexShrink 示例
这个示例有一些 Bug 待修复。
import React, { useState } from "react";
import {
View,
SafeAreaView,
Text,
TextInput,
StyleSheet,
} from "react-native";
const App = () => {
const [powderblue, setPowderblue] = useState({
flexGrow: 0,
flexShrink: 1,
flexBasis: "auto",
});
const [skyblue, setSkyblue] = useState({
flexGrow: 1,
flexShrink: 0,
flexBasis: 100,
});
const [steelblue, setSteelblue] = useState({
flexGrow: 0,
flexShrink: 1,
flexBasis: 200,
});
return (
<SafeAreaView style={styles.container}>
<View
style={[
styles.container,
{
flexDirection: "row",
alignContent: "space-between",
},
]}
>
<BoxInfo
color="powderblue"
{...powderblue}
setStyle={setPowderblue}
/>
<BoxInfo
color="skyblue"
{...skyblue}
setStyle={setSkyblue}
/>
<BoxInfo
color="steelblue"
{...steelblue}
setStyle={setSteelblue}
/>
</View>
<View style={styles.previewContainer}>
<View
style={[
styles.box,
{
flexBasis: powderblue.flexBasis,
flexGrow: powderblue.flexGrow,
flexShrink: powderblue.flexShrink,
backgroundColor: "powderblue",
},
]}
/>
<View
style={[
styles.box,
{
flexBasis: skyblue.flexBasis,
flexGrow: skyblue.flexGrow,
flexShrink: skyblue.flexShrink,
backgroundColor: "skyblue",
},
]}
/>
<View
style={[
styles.box,
{
flexBasis: steelblue.flexBasis,
flexGrow: steelblue.flexGrow,
flexShrink: steelblue.flexShrink,
backgroundColor: "steelblue",
},
]}
/>
</View>
</SafeAreaView>
);
};
const BoxInfo = ({
color,
flexBasis,
flexShrink,
setStyle,
flexGrow,
}) => (
<View style={[styles.row, { flexDirection: "column" }]}>
<View
style={[
styles.boxLabel,
{
backgroundColor: color,
},
]}
>
<Text
style={{
color: "#fff",
fontWeight: "500",
textAlign: "center",
}}
>
Box
</Text>
</View>
<Text style={styles.label}>flexBasis</Text>
<TextInput
value={flexBasis}
style={styles.input}
onChangeText={(fB) =>
setStyle((value) => ({
...value,
flexBasis: isNaN(parseInt(fB))
? "auto"
: parseInt(fB),
}))
}
/>
<Text style={styles.label}>flexShrink</Text>
<TextInput
value={flexShrink}
style={styles.input}
onChangeText={(fS) =>
setStyle((value) => ({
...value,
flexShrink: isNaN(parseInt(fS))
? ""
: parseInt(fS),
}))
}
/>
<Text style={styles.label}>flexGrow</Text>
<TextInput
value={flexGrow}
style={styles.input}
onChangeText={(fG) =>
setStyle((value) => ({
...value,
flexGrow: isNaN(parseInt(fG))
? ""
: parseInt(fG),
}))
}
/>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 10,
},
box: {
flex: 1,
height: 50,
width: 50,
},
boxLabel: {
minWidth: 80,
padding: 8,
borderRadius: 4,
marginTop: 8,
},
label: {
marginTop: 6,
fontSize: 16,
fontWeight: "100",
},
previewContainer: {
flex: 1,
flexDirection: "row",
backgroundColor: "aliceblue",
},
row: {
flex: 1,
flexDirection: "row",
flexWrap: "wrap",
alignItems: "center",
marginBottom: 10,
},
input: {
borderBottomWidth: 1,
paddingVertical: 3,
width: 50,
textAlign: "center",
},
});
export default App;
Width and Height
指定元素的宽高。width
和 height
都使用下述的值:
auto
:默认值,React Native 根据元素的内容来计算它的宽高;pixels
:以绝对像素定义宽度/高度。根据组件上设置的其他样式,这可能是节点的最终维度,也可能不是。percentage
:分别以其父元素宽度或高度的百分比定义宽度或高度。
示例:
import React, { useState } from "react";
import {
View,
SafeAreaView,
TouchableOpacity,
Text,
StyleSheet,
} from "react-native";
const WidthHeightBasics = () => {
const [widthType, setWidthType] = useState("auto");
const [heightType, setHeightType] = useState("auto");
return (
<PreviewLayout
widthType={widthType}
heightType={heightType}
widthValues={["auto", 300, "80%"]}
heightValues={["auto", 200, "60%"]}
setWidthType={setWidthType}
setHeightType={setHeightType}
>
<View
style={{
alignSelf: "flex-start",
backgroundColor: "aliceblue",
height: heightType,
width: widthType,
padding: 15,
}}
>
<View
style={[
styles.box,
{ backgroundColor: "powderblue" },
]}
/>
<View
style={[
styles.box,
{ backgroundColor: "skyblue" },
]}
/>
<View
style={[
styles.box,
{ backgroundColor: "steelblue" },
]}
/>
</View>
</PreviewLayout>
);
};
const PreviewLayout = ({
children,
widthType,
heightType,
widthValues,
heightValues,
setWidthType,
setHeightType,
}) => (
<SafeAreaView style={{ flex: 1, padding: 10 }}>
<View style={styles.row}>
<Text style={styles.label}>width </Text>
{widthValues.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setWidthType(value)}
style={[
styles.button,
widthType === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
widthType === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={styles.row}>
<Text style={styles.label}>height </Text>
{heightValues.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setHeightType(value)}
style={[
styles.button,
heightType === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
heightType === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
{children}
</SafeAreaView>
);
const styles = StyleSheet.create({
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
padding: 8,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginRight: 10,
marginBottom: 10,
},
selected: {
backgroundColor: "coral",
shadowOpacity: 0,
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default WidthHeightBasics;
Absolute & Relative Layout
position
定义了元素在父组件中的定位方式。
relative
:默认值,元素的定位是相对的。absolute
:元素的布局是绝对的,它的布局独立于它的兄弟节点。
示例:
import React, { useState } from "react";
import {
View,
SafeAreaView,
TouchableOpacity,
Text,
StyleSheet,
} from "react-native";
const PositionLayout = () => {
const [position, setPosition] = useState("relative");
return (
<PreviewLayout
label="position"
selectedValue={position}
values={["relative", "absolute"]}
setSelectedValue={setPosition}
>
<View
style={[
styles.box,
{
top: 25,
left: 25,
position,
backgroundColor: "powderblue",
},
]}
/>
<View
style={[
styles.box,
{
top: 50,
left: 50,
position,
backgroundColor: "skyblue",
},
]}
/>
<View
style={[
styles.box,
{
top: 75,
left: 75,
position,
backgroundColor: "steelblue",
},
]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<SafeAreaView style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={styles.container}>{children}</View>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
minHeight: 200,
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default PositionLayout;
Images
Static Image Resources
打入 bundle 中的静态图片在使用时可以不指定宽高,bundler 能获取其默认大小。
React Native 提供了一种统一的方式来管理你的 Android 和 iOS 应用中的图像和其他媒体资源。
<Image source={require('./my-icon.png')} />
图像名称的解析方式与 JS 模块的解析方式相同。在上述例子中,bundler
将在与需要它的组件相同的文件夹中寻找 my-icon.png
。
你可以使用 @2x
和 @3x
后缀为不同像素密度的屏幕提供图片。如果你有下面这样的文件结构:
.
├── button.js
└── img
├── check.png
├── check@2x.png
└── check@3x.png
且在 button.js
的代码中包含:
<Image source={require('./img/check.png')} />
check@2x.png
将用于 iPhone 7 ,check@3x.png
将用于 iPhone 7 Plus 。如果没有匹配屏幕密度的图像,将选择最接近的选项。
使用静态图片资源的示例:
// GOOD
<Image source={require('./my-icon.png')} />;
// BAD
var icon = this.props.active
? 'my-icon-active'
: 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />;
// GOOD
var icon = this.props.active
? require('./my-icon-active.png')
: require('./my-icon-inactive.png');
<Image source={icon} />;
Static Non-Image Resources
上面描述的 require
语法也可以用于静态地包含在项目中的音频、视频或文档文件。支持的最常见的文件类型包括 .mp3
、.wav
、.mp4
、.mov
、.html
、.pdf
等。
需要注意的是,视频必须使用绝对定位 (absolute positioning) 而不是 flexGrow
,因为大小信息目前不传递给非图像资产。对于直接链接到 Xcode 或 Android 的 Assets 文件夹中的视频,则不会出现这种限制。
Images From Hybrid App's Resources
如果你在构建一个 hybrid 应用(一些 UI 使用 React Native,另一些 UI 使用 Native 的),你可以在 RN 中使用已经打包到 App 中的图片。
在 Xcode 的 asset 中或 Android 的 drawable 目录中的图片,使用不带拓展名的图片名称:
<Image
source={{ uri: 'app_icon' }}
style={{ width: 40, height: 40 }}
/>
在 Android assets 目录中的图片,需要使用 asset:/
scheme :
<Image
source={{ uri: 'asset:/app_icon.png' }}
style={{ width: 40, height: 40 }}
/>
这些方法不提供安全检查。你要确保这些图像在 App 中可用。你还必须手动指定图像尺寸。
Network Images
和静态资源不同的是,你需要手动指定从网络中下载的图片的宽高:
// GOOD
<Image source={{uri: 'https://reactjs.org/logo-og.png'}}
style={{width: 400, height: 400}} />
// BAD
<Image source={{uri: 'https://reactjs.org/logo-og.png'}} />
如果你想在图片的请求中添加 header 和 body ,你可以在 source
对象中设置它们。
示例:
这个图片资源是否不能这样请求,貌似加了这些参数后就无法正常显示了,只有最开始闪一下就再也不展示了。
<Image
source={{
uri: 'https://reactjs.org/logo-og.png',
method: 'POST',
headers: {
Pragma: 'no-cache'
},
body: 'Your Body goes here'
}}
style={{ width: 400, height: 400 }}
/>
Uri Data Images
有时,你可能会从 REST API 调用获得编码后的图像数据。你可以使用 data:
uri scheme 来使用这些图片。和网络资源一样,你需要手动指定图片的宽高。
示例:
<Image
style={{
width: 51,
height: 51,
resizeMode: 'contain'
}}
source={{
uri:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg=='
}}
/>
Cache Control (iOS Only)
cache
属性使你可以控制网络层如何与 cache 交互。
default
:使用 Native 代码中设置的策略;reload
:(不使用缓存、只从网络加载)URL 的数据将从原始源加载。不应该使用现有的缓存数据来满足 URL 加载请求。force-cache
:(优先使用缓存,若缓存没有则从网络加载)现有的缓存数据将用于满足请求,而不管它的过期日期。如果缓存中没有与请求对应的现有数据,则从原始源加载数据。only-if-cached
:(只从缓存中获取图片,如果缓存没有则加载失败)现有的缓存数据将用于满足请求,而不管它的过期日期。如果缓存中没有与 URL 加载请求对应的数据,则不会尝试从原始源加载数据,然后加载被认为失败。
示例:
<Image
source={{
uri: 'https://reactjs.org/logo-og.png',
cache: 'only-if-cached'
}}
style={{ width: 400, height: 400 }}
/>
Local Filesystem Images
有关使用 Images.xcassets
之外的本地资源的示例,请参阅 react-native-cameraroll 。
Background Image via Nesting
与 Web 上的 background-image
类似,React Native 使用 <ImageBackground>
组件展示背景图片,它的 props 和 <Image>
组件一样。
<ImageBackground>
组件有时候可能不能我们的更定制化的需求,如果要自定义背景图组件,请参看 ImageBackground 组件的文档。
示例:
import React from "react";
import { ImageBackground, StyleSheet, Text, View } from "react-native";
const image = { uri: "https://reactjs.org/logo-og.png" };
const App = () => (
<View style={styles.container}>
<ImageBackground source={image} resizeMode="cover" style={styles.image}>
<Text style={styles.text}>Inside</Text>
</ImageBackground>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
},
image: {
flex: 1,
justifyContent: "center"
},
text: {
color: "white",
fontSize: 42,
lineHeight: 84,
fontWeight: "bold",
textAlign: "center",
backgroundColor: "#000000c0"
}
});
export default App;
iOS Border Radius Styles
Please note that the following corner specific, border radius style properties are currently ignored by iOS's image component:
borderTopLeftRadius
borderTopRightRadius
borderBottomLeftRadius
borderBottomRightRadius
Last updated
Was this helpful?