在本教程中,我们将使用React Native创建一个新闻阅读器应用程序。 在这个由两部分组成的系列文章中,我将假定这不是您的第一个React Native应用程序,并且在设置机器和在设备上运行该应用程序时,我不会赘述。 也就是说,我将详细解释实际的开发过程。
即使我们将部署到Android,本教程中使用的代码也应在iOS上运行。 这是最终结果。
先决条件
如果您不熟悉React Native并且尚未设置机器,请务必查看React Native文档的入门指南 ,或阅读Envato Tuts +上Ashraff 的入门教程 。 如果要部署到Android或安装Xcode和SDK for iOS, 请不要忘记安装Android SDK 。
完成后,使用npm 安装NodeJS和React Native命令行工具。
npm install -g react-native-cli
1.项目设置
我们现在准备构建项目。 在开始之前,我想简要介绍一下该项目的组合方式。 我们创建两个自定义组件:
- 呈现新闻项目的
NewsItems
-
WebPage
,当用户点击新闻项时呈现网页
然后将它们导入到Android( index.android.js )和iOS( index.ios.js )的主入口点文件中。 这就是您现在需要知道的。
步骤1:建立新应用程式
首先导航到您的工作目录。 在该目录中打开一个新的终端窗口,然后执行以下命令:
react-native init HnReader
这将创建一个名为HnReader的新文件夹,其中包含构建应用程序所需的文件。
React Native已经带有一些默认组件,但也有其他开发人员构建的自定义组件。 您可以在react.parts网站上找到它们。 不过,并非所有组件都可以在Android和iOS上运行。 甚至某些默认组件也不是跨平台的。 这就是为什么在选择组件时必须小心,因为它们在每个平台上可能有所不同,或者在每个平台上可能无法正常工作。
最好进入要使用的组件的GitHub存储库的问题页面,并搜索android支持或ios支持以快速检查该组件是否在两种平台上均有效。
步骤2:安装依赖项
我们将要构建的应用程序依赖于一些第三方库和React组件。 您可以通过在工作目录的根目录中打开package.json来安装它们。 将以下内容添加到package.json中 :
{
"name": "HnReader",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "react-native start"
},
"dependencies": {
"lodash": "^4.0.1",
"moment": "^2.11.1",
"react-native": "^0.18.1",
"react-native-button": "^1.3.1",
"react-native-gifted-spinner": "0.0.3"
}
}
接下来,在工作目录中打开一个终端窗口,然后执行npm install
来安装package.json中指定的依赖项。 这是每个库在项目中的功能的简要说明:
- lodash用于截断字符串。 这可能有点过大,但是您只需编写更少的代码行就意味着更少的责任。
- 瞬间用于确定本地存储中的新闻项目是否已经存在一天。
- react-native是React Native框架。 默认情况下,在您较早执行
react-native init
时会安装该程序。 - react-native-button是用于创建按钮的react native组件。
- 发出网络请求时, react-native-gifted-spinner用作活动指示器。
2.主要组成部分
如前所述,所有React Native项目的入口都是index.android.js和index.ios.js 。 这是本节的重点。 将这些文件的内容替换为以下内容:
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Navigator
} = React;
var NewsItems = require('./components/news-items');
var WebPage = require('./components/webpage');
var ROUTES = {
news_items: NewsItems,
web_page: WebPage
};
var HnReader = React.createClass({
renderScene: function(route, navigator) {
var Component = ROUTES[route.name];
return (
<Component route={route} navigator={navigator} url={route.url} />
);
},
render: function() {
return (
<Navigator
style={styles.container}
initialRoute={{name: 'news_items', url: ''}}
renderScene={this.renderScene}
configureScene={() => { return Navigator.SceneConfigs.FloatFromRight; }} />
);
},
});
var styles = StyleSheet.create({
container: {
flex: 1
}
});
AppRegistry.registerComponent('HnReader', () => HnReader);
让我分解一下。 首先,我们使用use script
指令启用严格模式。 这使解析器可以对您的代码进行更多检查。 例如,如果您初始化变量而未添加var
关键字,它将抱怨。
'use strict';
接下来,我们导入React Native框架。 这使我们可以创建自定义组件并向应用程序添加样式。
var React = require('react-native');
然后,我们从React
对象中提取所需的所有功能。
var {
AppRegistry,
StyleSheet,
Navigator
} = React;
如果您不熟悉ES6(ECMAScript 6),则以上代码段与以下代码相同:
var AppRegistry = React.AppRegistry;
var StyleSheet = React.StyleSheet;
var Navigator = React.Navigator;
ES6中引入了语法糖,以简化将对象属性分配给变量的过程。 这称为解构分配 。
这是我们提取的每个属性的简要说明:
-
AppRegistry
用于注册应用程序的主要组件。 -
StyleSheet
用于声明组件要使用的样式。 -
Navigator
用于在应用程序的不同页面之间切换。
接下来,我们导入应用程序使用的自定义组件。 我们将在以后创建这些。
var NewsItems = require('./components/news-items');
var WebPage = require('./components/webpage');
创建一个ROUTES
变量,并使用上述两个组件作为其属性的值来分配一个对象。 这使我们可以通过引用我们定义的每个键来显示组件。
var ROUTES = {
news_items: NewsItems,
web_page: WebPage
};
通过从React
对象调用createClass
方法来创建应用程序的主要组件。 createClass
方法接受一个对象作为其参数。
var HnReader = React.createClass({
...
});
对象内部是renderScene
方法,每当路线更改时都会调用该方法。 route
和navigator
器作为该方法的参数传递。 route
包含有关当前路线的信息(例如,路线名称)。
navigator
包含可用于在不同路线之间navigator
方法。 在renderScene
方法内部,我们通过将当前路由的名称传递给ROUTES
对象来获取要渲染的组件。 接下来,我们渲染组件,并将route
, navigator
和url
作为属性传递。 稍后,您将看到如何在每个组件中使用它们。 现在,请记住,当您要将数据从主组件传递到子组件时,您要做的就是添加一个新属性,并将要传递的数据用作值。
renderScene: function(route, navigator) {
var Component = ROUTES[route.name]; //get the component for this specific route
//render the component and pass along the route, navigator and the url
return (
<Component route={route} navigator={navigator} url={route.url} />
);
},
render
方法是创建组件时必需的方法,因为它负责渲染组件的用户界面。 在此方法中,我们呈现了Navigator
组件并传递了一些属性。
render: function() {
return (
<Navigator
style={styles.container}
initialRoute={{name: 'news_items', url: ''}}
renderScene={this.renderScene}
configureScene={() => { return Navigator.SceneConfigs.FloatFromRight; }} />
);
},
让我解释一下每个属性的作用:
-
style
用于向组件添加样式。 -
initialRoute
用于指定导航器要使用的初始路由。 如您所见,我们传递了一个包含name
属性的对象,该name
属性的值设置为news_items
。 该对象是传递给我们之前定义的renderScene
方法的route
参数的对象。 这意味着默认情况下,此特定代码将呈现NewsItems
组件。
var Component = ROUTES[route.name];
url
设置为空字符串,因为默认情况下我们没有要呈现的网页。
-
renderScene
负责为特定路线渲染组件。 -
configureScene
负责指定在路线之间导航时要使用的动画和手势。 在这种情况下,我们传递了一个返回FloatFromRight
动画的函数。 这意味着,当导航到具有较高索引的路线时,新页面会从右向左浮动。 当返回时,它从左到右浮动。 这也增加了向左滑动的手势,作为返回上一条路线的一种方式。
() => { return Navigator.SceneConfigs.FloatFromRight; }
样式是在定义主要组件之后定义的。 我们从StyleSheet
对象调用create
方法,并传入一个包含样式的对象。 在这种情况下,我们只有一个,它定义它将占据整个屏幕。
var styles = StyleSheet.create({
container: {
flex: 1
}
});
最后,我们注册组件。
AppRegistry.registerComponent('HnReader', () => HnReader);
3. NewsItem
组件
NewsItem
组件用于呈现新闻项。 定制组件存储在components目录中。 在此目录中,创建news-items.js并向其中添加以下代码:
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
ListView,
View,
ScrollView,
TouchableHighlight,
AsyncStorage
} = React;
var Button = require('react-native-button');
var GiftedSpinner = require('react-native-gifted-spinner');
var api = require('../src/api.js');
var moment = require('moment');
var TOTAL_NEWS_ITEMS = 10;
var NewsItems = React.createClass({
getInitialState: function() {
return {
title: 'HN Reader',
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
news: {},
loaded: false
}
},
render: function() {
return (
<View style={styles.container}>
<View style={styles.header}>
<View style={styles.header_item}>
<Text style={styles.header_text}>{this.state.title}</Text>
</View>
<View style={styles.header_item}>
{ !this.state.loaded &&
<GiftedSpinner />
}
</View>
</View>
<View style={styles.body}>
<ScrollView ref="scrollView">
{
this.state.loaded &&
<ListView initialListSize={1} dataSource={this.state.news} style={styles.news} renderRow={this.renderNews}></ListView>
}
</ScrollView>
</View>
</View>
);
},
componentDidMount: function() {
AsyncStorage.getItem('news_items').then((news_items_str) => {
var news_items = JSON.parse(news_items_str);
if(news_items != null){
AsyncStorage.getItem('time').then((time_str) => {
var time = JSON.parse(time_str);
var last_cache = time.last_cache;
var current_datetime = moment();
var diff_days = current_datetime.diff(last_cache, 'days');
if(diff_days > 0){
this.getNews();
}else{
this.updateNewsItemsUI(news_items);
}
});
}else{
this.getNews();
}
}).done();
},
renderNews: function(news) {
return (
<TouchableHighlight onPress={this.viewPage.bind(this, news.url)} underlayColor={"#E8E8E8"} style={styles.button}>
<View style={styles.news_item}>
<Text style={styles.news_item_text}>{news.title}</Text>
</View>
</TouchableHighlight>
);
},
viewPage: function(url){
this.props.navigator.push({name: 'web_page', url: url});
},
updateNewsItemsUI: function(news_items){
if(news_items.length == TOTAL_NEWS_ITEMS){
var ds = this.state.dataSource.cloneWithRows(news_items);
this.setState({
'news': ds,
'loaded': true
});
}
},
updateNewsItemDB: function(news_items){
if(news_items.length == TOTAL_NEWS_ITEMS){
AsyncStorage.setItem('news_items', JSON.stringify(news_items));
}
},
getNews: function() {
var TOP_STORIES_URL = 'https://hacker-news.firebaseio.com/v0/topstories.json';
var news_items = [];
AsyncStorage.setItem('time', JSON.stringify({'last_cache': moment()}));
api(TOP_STORIES_URL).then(
(top_stories) => {
for(var x = 0; x <= 10; x++){
var story_url = "https://hacker-news.firebaseio.com/v0/item/" + top_stories[x] + ".json";
api(story_url).then(
(story) => {
news_items.push(story);
this.updateNewsItemsUI(news_items);
this.updateNewsItemDB(news_items);
}
);
}
}
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1
},
header: {
backgroundColor: '#FF6600',
padding: 10,
flex: 1,
justifyContent: 'space-between',
flexDirection: 'row'
},
body: {
flex: 9,
backgroundColor: '#F6F6EF'
},
header_item: {
paddingLeft: 10,
paddingRight: 10,
justifyContent: 'center'
},
header_text: {
color: '#FFF',
fontWeight: 'bold',
fontSize: 15
},
button: {
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0'
},
news_item: {
paddingLeft: 10,
paddingRight: 10,
paddingTop: 15,
paddingBottom: 15,
marginBottom: 5
},
news_item_text: {
color: '#575757',
fontSize: 18
}
});
module.exports = NewsItems;
步骤1:导入组件和库
首先,我们导入NewsItem
组件所需的组件和库。 我们还创建了一个全局变量,用于存储要缓存的新闻总数。
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
ListView,
View,
ScrollView,
TouchableHighlight,
AsyncStorage
} = React;
var Button = require('react-native-button');
var GiftedSpinner = require('react-native-gifted-spinner');
var api = require('../src/api.js');
var moment = require('moment');
var TOTAL_NEWS_ITEMS = 10;
我们使用了一些以前没有使用过的组件。
-
Text
用于在React Native中显示文本。 -
View
是创建组件的基本构建块。 可以将其视为网页中的div
。 -
ListView
用于呈现对象数组。 -
ScrollView
用于添加滚动条。 React Native不像网页。 当内容大于视图或屏幕时,滚动条不会自动添加。 这就是为什么我们需要使用此组件。 -
TouchableHighlight
用于使组件响应触摸事件。 -
AsyncStorage
并不是真正的组件。 这是一个用于在React Native中存储本地数据的API。 -
Button
是用于创建按钮的第三方组件。 - 当从网络加载数据时,
GiftedSpinner
用于创建微调器。 -
api
是一个自定义模块,其中包装了fetch
,这是React Native发出网络请求的方式。 要获取网络请求返回的数据,需要大量样板代码,这就是我们将其包装在模块中的原因。 以免在发出网络请求时减少编写代码。 -
moment
是用于与时间相关的任何东西的库。
步骤2:创建NewsItems
组件
接下来,我们创建NewsItems
组件:
var NewsItems = React.createClass({
...
});
此组件中有getInitialState
函数,该函数用于指定此组件的默认状态。 在React Native中,状态用于存储整个组件中可用的数据。 在这里,我们将存储应用程序的标题, ListView
组件的dataSource
,当前news
项和一个布尔值loaded
,该布尔值dataSource
当前是否正在从网络加载新闻项。 loaded
变量用于确定是否显示微调器。 我们将其设置为false
因此默认情况下微调器是可见的。
从本地存储或网络加载新闻项目后,将其设置为true
以隐藏微调框。 dataSource
用于定义要用于ListView
组件的数据源的蓝图。 可以将其视为父类,您将定义的每个数据源都将在其中继承。 这要求包含的对象rowHasChanged
功能,它告诉ListView
到当行已改变重新渲染。
最后, news
对象包含ListView
数据源的初始值。
getInitialState: function() {
return {
title: 'HN Reader',
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
news: {},
loaded: false
}
},
步骤3:实现render
功能
render
功能渲染此组件的用户界面。 首先,我们将所有内容包装在View
。 然后,在内部有标头和正文。 标头包含标题和微调框。 该主体包含ListView
。 体内的所有内容都包裹在ScrollView
以便在内容超出可用空间时自动添加滚动条。
render: function() {
return (
<View style={styles.container}>
<View style={styles.header}>
<View style={styles.header_item}>
<Text style={styles.header_text}>{this.state.title}</Text>
</View>
<View style={styles.header_item}>
{ !this.state.loaded &&
<GiftedSpinner />
}
</View>
</View>
<View style={styles.body}>
<ScrollView ref="scrollView">
{
this.state.loaded &&
<ListView initialListSize={1} dataSource={this.state.news} style={styles.news} renderRow={this.renderNews}></ListView>
}
</ScrollView>
</View>
</View>
);
},
标头内部有两个视图:
- 一个包含标题
- 一个包含微调器
我们这样做是为了代替直接输出文本和微调器,以便我们可以使用flexbox来控制样式。 稍后,您可以在样式部分中查看如何完成此操作。
我们可以使用this.state
来引用存储在状态中的标题,后跟属性名称。 您可能已经注意到,每次我们需要引用一个对象时,都会将其用花括号括起来。 在另一个视图上,我们正在检查状态下的loaded
属性是否设置为false
,如果是,则输出微调器。
<View style={styles.header_item}>
<Text style={styles.header_text}>{this.state.title}</Text>
</View>
<View style={styles.header_item}>
{ !this.state.loaded &&
<GiftedSpinner />
}
</View>
接下来是身体。
<ScrollView ref="scrollView">
{
this.state.loaded &&
<ListView initialListSize={1} dataSource={this.state.news} style={styles.news} renderRow={this.renderNews}></ListView>
}
</ScrollView>
注意,我们已经将ref
属性传递给ScrollView
。 ref
是React Native中的预定义属性,它允许我们为组件分配标识符。 我们可以使用此标识符来引用组件并调用其方法。 这是一个如何工作的示例:
scrollToTop: function(){
this.refs.scrollView.scrollTo(0);
}
然后,您可以有一个按钮,让它在按下时调用该函数。 这将自动将ScrollView
到组件的最上方。
<Button onPress={this.scrollToTop}>scroll to top</Button>
我们不会在应用程序中使用它,但是很高兴知道它的存在。
在ScrollView
内部,我们检查状态下的loaded
属性是否已设置为true
。 如果为true
,则意味着该数据源已经可供ListView
使用,我们可以对其进行渲染。
{
this.state.loaded &&
<ListView initialListSize={1} dataSource={this.state.news} renderRow={this.renderNews}></ListView>
}
我们在ListView
传递了以下属性:
-
initialListSize
用于指定最初安装组件时要呈现的行数。 我们将其设置为1
,这意味着将花费一帧渲染每一行。 我将其设置为1
作为一种性能优化形式,以便用户尽快看到内容。 -
dataSource
是要使用的数据源。 -
renderRow
是用于呈现列表中每一行的函数。
步骤4:实现componentDidMount
函数
接下来,我们有componentDidMount
函数,该函数在安装此组件时被调用:
componentDidMount: function() {
AsyncStorage.getItem('news_items').then((news_items_str) => {
var news_items = JSON.parse(news_items_str);
if(news_items != null){
AsyncStorage.getItem('time').then((time_str) => {
var time = JSON.parse(time_str);
var last_cache = time.last_cache;
var current_datetime = moment();
var diff_days = current_datetime.diff(last_cache, 'days');
if(diff_days > 0){
this.getNews();
}else{
this.updateNewsItemsUI(news_items);
}
});
}else{
this.getNews();
}
}).done();
},
在函数内部,我们尝试获取当前存储在本地存储中的新闻项。 我们使用AsyncStorage
API中的getItem
方法。 它返回一个promise,因此我们可以通过调用then
方法并传递一个函数来访问返回的数据:
AsyncStorage.getItem('news_items').then((news_items_str) => {
...
}).done();
AsyncStorage
只能存储字符串数据,因此我们使用JSON.parse
将JSON字符串转换回JavaScript对象。 如果为null
,则调用getNews
方法,该方法从网络中获取数据。
var news_items = JSON.parse(news_items_str);
if(news_items != null){
...
}else{
this.getNews();
}
如果不为空,则使用AsyncStorage
来获取新闻项上次存储在本地存储中的时间。 然后,我们将其与当前时间进行比较。 如果相差至少一天(24小时),我们将从网络上获取新闻。 如果不是,我们使用本地存储中的那些。
AsyncStorage.getItem('time').then((time_str) => {
var time = JSON.parse(time_str);
var last_cache = time.last_cache; //extract the last cache time
var current_datetime = moment(); //get the current time
//get the difference in days
var diff_days = current_datetime.diff(last_cache, 'days');
if(diff_days > 0){
this.getNews(); //fetch from the network
}else{
this.updateNewsItemsUI(news_items); //use the one in the cache
}
});
步骤5:实现renderNews
函数
接下来是渲染列表中每一行的功能。 在ListView
前面,我们定义了renderRow
属性,其值为this.renderNews
。 这就是那个功能。
迭代中的当前项目作为该函数的参数传递。 这使我们可以访问每个新闻项的title
和url
。 一切都包装在TouchableHighlight
组件内,在内部我们输出每个新闻项的标题。
TouchableHighlight
组件接受onPress
属性,该属性指定用户点击项目时要执行的功能。 在这里,我们调用viewPage
函数并将URL绑定到该函数。 underlayColor
指定在点击组件时的背景色。
renderNews: function(news) {
return (
<TouchableHighlight onPress={this.viewPage.bind(this, news.url)} underlayColor={"#E8E8E8"} style={styles.button}>
<View style={styles.news_item}>
<Text style={styles.news_item_text}>{news.title}</Text>
</View>
</TouchableHighlight>
);
},
在viewPage
函数中,我们保留了之前通过props从index.android.js传递的navigator
属性。 在React Native中,道具用于访问从父组件传递的属性。 我们将其称为this.props
,后跟属性名称。
在这里,我们使用this.props.navigator
来引用navigator
对象。 然后,我们调用push
方法将web_page
路由以及要由WebPage
组件打开的WebPage
的URL推web_page
导航器。 这使应用程序过渡到WebPage
组件。
viewPage: function(url){
this.props.navigator.push({name: 'web_page', url: url});
},
步骤6:实施updateNewsItemsUI
函数
updateNewsItemsUI
函数根据作为参数传递的新闻项的数组更新数据源和状态。 仅当news_items
的总数等于我们先前为TOTAL_NEWS_ITEMS
设置的值时,我们才这样做。 在React Native中,更新状态会触发用户界面重新渲染。 这意味着使用新的数据源调用setState
刷新带有新项目的用户界面。
updateNewsItemsUI: function(news_items){
if(news_items.length == TOTAL_NEWS_ITEMS){
var ds = this.state.dataSource.cloneWithRows(news_items); //update the data source
//update the state
this.setState({
'news': ds,
'loaded': true
});
}
},
步骤7:更新本地存储
updateNewsItemDB
函数更新存储在本地存储中的新闻项。 我们使用JSON.stringify
函数将数组转换为JSON字符串,以便我们可以使用AsyncStorage
进行存储。
updateNewsItemDB: function(news_items){
if(news_items.length == TOTAL_NEWS_ITEMS){
AsyncStorage.setItem('news_items', JSON.stringify(news_items));
}
},
步骤8:提取新闻项
getNews
函数更新存储最后一次缓存数据的本地存储项目,从Hacker News API获取新闻项目,根据获取的新项目更新用户界面和本地存储。
getNews: function() {
var TOP_STORIES_URL = 'https://hacker-news.firebaseio.com/v0/topstories.json';
var news_items = [];
AsyncStorage.setItem('time', JSON.stringify({'last_cache': moment()}));
api(TOP_STORIES_URL).then(
(top_stories) => {
for(var x = 0; x <= 10; x++){
var story_url = "https://hacker-news.firebaseio.com/v0/item/" + top_stories[x] + ".json";
api(story_url).then(
(story) => {
news_items.push(story);
this.updateNewsItemsUI(news_items);
this.updateNewsItemDB(news_items);
}
);
}
}
);
}
Hacker News API中的热门新闻资源返回一个数组,如下所示:
[ 10977819, 10977786, 10977295, 10978322, 10976737, 10978069, 10974929, 10975813, 10974552, 10978077, 10978306, 10973956, 10975838, 10974870...
这些是在Hacker News上发布的热门文章的标识符。 这就是为什么我们需要遍历此数组并对每个项目进行网络请求,以获取实际的详细信息,例如标题和URL。
然后,将其推送到news_items
数组,并调用updateNewsItemsUI
和updateNewsItemDB
函数以更新用户界面和本地存储。
for(var x = 0; x <= 10; x++){
var story_url = "https://hacker-news.firebaseio.com/v0/item/" + top_stories[x] + ".json";
api(story_url).then(
(story) => {
news_items.push(story);
this.updateNewsItemsUI(news_items);
this.updateNewsItemDB(news_items);
}
);
}
步骤9:样式
添加以下样式:
var styles = StyleSheet.create({
container: {
flex: 1
},
header: {
backgroundColor: '#FF6600',
padding: 10,
flex: 1,
justifyContent: 'space-between',
flexDirection: 'row'
},
body: {
flex: 9,
backgroundColor: '#F6F6EF'
},
header_item: {
paddingLeft: 10,
paddingRight: 10,
justifyContent: 'center'
},
header_text: {
color: '#FFF',
fontWeight: 'bold',
fontSize: 15
},
button: {
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0'
},
news_item: {
paddingLeft: 10,
paddingRight: 10,
paddingTop: 15,
paddingBottom: 15,
marginBottom: 5
},
news_item_text: {
color: '#575757',
fontSize: 18
}
});
其中大多数是标准CSS,但请注意,我们已将驼峰替换为驼峰式语法。 这不是因为如果使用诸如padding-left
类的语法,就会出现语法错误。 这是因为React Native需要它。 另请注意, 并非所有css属性都可以使用 。
就是说,这里有一些声明可能不是那么直观,特别是如果您以前没有使用过flexbox的话 :
container: {
flex: 1
},
header: {
backgroundColor: '#FF6600',
padding: 10,
flex: 1,
justifyContent: 'space-between',
flexDirection: 'row'
},
body: {
flex: 9,
backgroundColor: '#F6F6EF'
},
这是NewsItems
组件的标记的简化版本,以帮助您可视化它:
<View style={styles.container}>
<View style={styles.header}>
...
</View>
<View style={styles.body}>
...
</View>
</View>
我们将container
设置为flex: 1
,这意味着它占据了整个屏幕。 在container
内,我们有header
和body
,分别将其设置为flex: 1
和flex: 9
。 在这种情况下,因为header
具有同级,所以flex: 1
不会占据整个屏幕。 这两个将共享整个屏幕。 这意味着由于我们拥有flex: 1
和flex: 9
,所以整个屏幕将被分为十个部分。 每个兄弟姐妹的flex
值相加。
header
占据屏幕的10%, body
占据屏幕的90%。 基本思想是选择一个代表整个屏幕的高度或宽度的数字,然后每个兄弟姐妹从该数字中取一个片段。 但是,不要为此过度。 您不想使用1000,除非您想在电影院中部署应用程序。 当找到高度时,我发现十是神奇的数字。
对于header
我们设置了以下样式:
header: {
backgroundColor: '#FF6600',
padding: 10,
flex: 1,
justifyContent: 'space-between',
flexDirection: 'row'
},
为了刷新您的内存,以下是标头内的简化标记:
<View style={styles.header_item}>
...
</View>
<View style={styles.header_item}>
...
</View>
样式添加到其中:
header_item: {
paddingLeft: 10,
paddingRight: 10,
justifyContent: 'center'
},
我们将flexDirection
设置为row
,将justifyContent
设置为其父级( header
space-between
。 这意味着它将均匀分布其子项,第一个子项在行的开头,最后一个子项在行的末尾。
默认情况下, flexDirection
设置为column
,这意味着由于移动是水平的,因此每个子项都占据整行。 使用row
将使流垂直,以便每个孩子并排。 如果您仍然对Flexbox感到困惑,或者想了解更多有关Flexbox的信息,请查看CSS:Flexbox Essentials 。
最后,将组件暴露给外界:
module.exports = NewsItems;
结论
在这一点上,您应该对如何以React Native方式进行操作有个好主意。 具体来说,您已经学习了如何创建新的React Native项目,通过npm安装第三方库,使用各种组件以及向应用程序添加样式。
在下一篇文章中,我们将继续将WebPage
组件添加到News Reader应用程序中。 随时在下面的评论部分中留下任何问题或评论。