深入研究React Native以进行Android开发

本文概述

几年前, 我的一个同事告诉我有关React Native的信息。我非常怀疑。我认为这只是另一个跨平台的框架, 在现实生活中永远无法使用-我几乎不知道自己是多么的错误。

多年过去了, React Native技能变得非常需求。既然有一段时间我学到了一些新知识, 所以我想为什么不试一试呢?今天, 我是React Native的拥护者。

缺点:

  • 你不能再使用Android Studio
  • 并非所有应用程序或功能都可以使用React Native
  • React Native是一个新颖的框架, 更新可能会对你当前的代码库产生负面影响
  • JavaScript不是严格类型的语言
  • React Native需要运行JavaScript引擎, 这可能使其性能降低

优点:

  • 简单易学
  • Android和iOS应用程序之间的共享代码库, 仅需细微调整即可匹配平台体验
  • 实时和热加载, 意味着不再需要无限的构建时间
  • 两个平台的本机组件
  • 不断完善
  • 积极发展的社区
  • 大量的图书馆
  • 博览会消除了拥有Mac进行iOS开发的需要
  • 减少人力资源—尽管你可能仍需要进行一些Android / iOS本机开发, 但这种情况很少见。

我可以继续下去, 但让我们在这里停止, 继续本博客文章的主题。在这篇文章中, 我将创建四个基于React Native的Android应用程序:

  1. 基本的计数器, 带有用于递增和递减计数器的按钮
  2. 搜寻r / pics subreddit的应用程式
  3. 通用登录页面
  4. 浏览r / pics subreddit的应用程序

HERE

如上所述, 我们无法将Android Studio用于React Native开发。我们需要替代品。 React Native可以使用任何可用的现代文本编辑器(Atom, VS Code, Sublime Text, Brackets等)进行开发, 但是由于我们具有Android Studio体验, 因此我最喜欢的是由同一家公司构建的WebStorm。尽管WebStorm是付费应用程序(每年129 $), 但是你可以安装它的Early Access版本。 WebStorm的EAP构建是免费的并且相当稳定。如果你喜欢完全免费的编辑器, 请使用VS Code。微软甚至为此开发了惊人的React Native插件, 并且效果很好。

创建一个新项目

先决条件:你的计算机上安装了Android SDK, Node和React Native。

有两种创建新的React Native项目的方法。

  1. 常规方式。使用WebStorm GUI或使用终端命令:react-native init AwesomesrcminiProject
  2. 简便的”创建React Native应用程序”。 create-react-native-app AwesomesrcminiProject

如果你使用create-react-native-app, 则将使用expo自举创建的项目。我不会详细介绍, 但是基本上, 这意味着你无需安装Xcode即可在iOS上运行该应用。通过expo.io的功能和其他一些功能, 使客户端始终保持最新状态也更加容易。但是你不能添加本机代码。因此, 如果你要开发特定功能, 则可能需要从expo中退出应用程序, 而要使用常规的React Native项目。

我将使用第一种方法。

让我们运行项目。首先, 打开仿真器或连接设备。如果使用WebStorm GUI创建了项目, 则只需选择一个配置即可。在WebStorm的右上角, 单击”运行”按钮左侧的下拉菜单, 选择” Android”, 然后单击”运行或调试”。如果使用Terminal创建了项目, 则可以添加新的React Native配置或使用Terminal运行它:

cd AwesomesrcminiProject
react-native run-android

如果一切顺利, 将会看到以下屏幕:

生成的布局

结构和基本设置

项目内值得注意的项目是:

  • android-预先配置有React Native支持的Android Studio项目。
  • ios-Xcode项目已预先配置有React Native支持。
  • node_modules-包含React Native框架和其他Javascript库的文件夹。
  • index.js-我们的应用程序的入口点。
  • App.js-已加载初始组件。

让我们在项目根目录内创建一个文件夹” src”, 然后将App.js移到该文件夹​​中。你必须更新index.js导入以匹配新的App.js位置。

import App from './src/App';

删除App.js中的所有内容并粘贴以下代码:

import React from 'react';
import {Text} from 'react-native';
export default class App extends React.Component {
   render() {
       return (
           <Text>Hello srcmini</Text>
       );
   }
}

我们粘贴的代码非常简单。我们创建了一个类App(React.Component的子类), 该类重写render()方法并返回Text组件。 React.Component是使用JSX构建UI的基类。 export default修饰符使该类公开。

现在, 我们准备开始设计布局。

使用Flexbox进行布局

Flexbox与LinearLayout相似, 但是Flexbox远远超出了LinearLayout的功能。

此JSX片段:

<View style={{
  flex: 1, flexDirection: 'row'
}}>
<View style={{
  width: 100, height: 100, backgroundColor: '#9575CD'
}}/>
<View style={{
  width: 100, height: 100, backgroundColor: '#7E57C2'
}}/>
<View style={{
  width: 100, height: 100, backgroundColor: '#673AB7'
}}/>
</View>

渲染此布局:

生成的布局

而此XML:

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="horizontal">
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#9575CD" />
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#7E57C2" />
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#673AB7" />
</LinearLayout>

呈现此:

生成的布局

JSX代码看起来很熟悉, 是吗?让我们使用在JSX和Android XML中看起来相似的布局为布局创建一个”字典”(或备忘单)。

请注意, 功能不一定相等。我正在尝试帮助React Native新手掌握React Native中布局系统的思想。请参阅官方指南以获取详细信息。

考虑以下JSX属性:

flex: 1

等效于:

android:layout_width="match_parent"
android:layout_height="match_parent"

此JSX片段:

<View style={{flex: 1, flexDirection: 'row'}}>
<View style={{
  width: 100, height: 100, backgroundColor: '#9575CD'}}/>
<View style={{
  width: 100, height: 100, backgroundColor: '#7E57C2'}}/>
<View style={{
  width: 100, height: 100, backgroundColor: '#673AB7'}}/>
</View>

而这个XML:

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="horizontal">
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#9575CD" />
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#7E57C2" />
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#673AB7" />
</LinearLayout>

两者都生成此输出:

生成的布局

同样, 此JSX:

<View style={{flex: 1, flexDirection: 'column'}}>
<View style={{
  width: 100, height: 100, backgroundColor: '#9575CD'}}/>
<View style={{
  width: 100, height: 100, backgroundColor: '#7E57C2'}}/>
<View style={{
  width: 100, height: 100, backgroundColor: '#673AB7'}}/>
</View>

而这个XML:

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#9575CD" />
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#7E57C2" />
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#673AB7" />
</LinearLayout>

产生这个:

生成的布局

为了在容器内获得正确的位置, 我们通常会结合使用flexDirection, alignItems和justifyContent属性。

此JSX:

<View style={{flex: 1, flexDirection: 'column', alignItems: 'center'}}>
<View style={{
  width: 100, height: 100, backgroundColor: '#9575CD'}}/>
<View style={{
  width: 100, height: 100, backgroundColor: '#7E57C2'}}/>
<View style={{
  width: 100, height: 100, backgroundColor: '#673AB7'}}/>
</View>

而这个XML:

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center_horizontal"
  android:orientation="vertical">
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#9575CD" />
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#7E57C2" />
<View
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="#673AB7" />
</LinearLayout>

将产生以下布局:

生成的布局

此JSX:

<View style={{flex: 1, flexDirection: 'column', justifyContent: 'center'}}>
   <View style={{
      width: 100, height: 100, backgroundColor: '#9575CD'}}/>
   <View style={{
      width: 100, height: 100, backgroundColor: '#7E57C2'}}/>
   <View style={{
      width: 100, height: 100, backgroundColor: '#673AB7'}}/>
</View>

而这个XML

<LinearLayout
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center_vertical"
   android:orientation="vertical">
   <View
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#9575CD" />
   <View
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#7E57C2" />
   <View
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#673AB7" />
</LinearLayout>

将产生以下布局:

生成的布局

此JSX:

<View style={{flex: 1, flexDirection: 'row', justifyContent: 'center'}}>
   <View style={{
      width: 100, height: 100, backgroundColor: '#9575CD'}}/>
   <View style={{
      width: 100, height: 100, backgroundColor: '#7E57C2'}}/>
   <View style={{
      width: 100, height: 100, backgroundColor: '#673AB7'}}/>
</View>

而这个XML:

<LinearLayout
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center_horizontal"
   android:orientation="horizontal">
   <View
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#9575CD" />
   <View
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#7E57C2" />
   <View
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#673AB7" />
</LinearLayout>

将产生以下布局:

生成的布局

此JSX:

<View style={{flex: 1, flexDirection: 'row', justifyContent: 'center', alignItems: 'center'}}>
   <View style={{
      width: 100, height: 100, backgroundColor: '#9575CD'}}/>
   <View style={{
      width: 100, height: 100, backgroundColor: '#7E57C2'}}/>
   <View style={{
      width: 100, height: 100, backgroundColor: '#673AB7'}}/>
</View>

和这个XML:

<LinearLayout
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center"
   android:orientation="vertical">
   <View
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#9575CD" />
   <View
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#7E57C2" />
   <View
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#673AB7" />
</LinearLayout>

将产生以下布局:

生成的布局

经验教训:如果我们有flexDirection:row’, 则alignItems在Y轴上起作用, justifyContent在X轴上起作用。一切都针对flexDirection:”列”进行了镜像-justifyContent影响Y轴, alignItems影响Y轴。

justifyContent:”灵活启动” 重力=”开始|左”
alignItems:”弹性启动” 重力=”开始|左”
justifyContent:” flex-end” 重力=”结束|右”
alignItems:’flex-end’ 重力=”结束|右”

自己尝试。将justifyContent值设置为”周围”, “之间”和”均匀”。

国家管理

为了更新应用程序状态, 你将使用React的状态变量。每当状态更新时, 都会调用render()。

将以下代码复制到你的应用中:

import React from 'react';
import {Button, Text, View} from 'react-native';
export default class App extends React.Component {
   /*
   Initialize state object
   with variable 'number'
   set to 0 and variable name
   with value of empty string
   */
   state = {number: 0};
   render() {
       return (
           <View style={{
               flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', flex: 1, padding: 20
           }}>
               <Button title='Decrement'
                       color='#e57373'
                       onPress={() => this.decrement()}/>
               <Text>
                   {/*
                    Text will be automatically
                    updated whenever state.number
                    has changed value
                    */}
                   Value = {this.state.number}
               </Text>
               <Button title='Increment'
                       color='#64B5F6'
                       {/*
                        Set listener for click
                        */}
                       onPress={() => this.increment()}/>
           </View>
       );
   }
   //Declaration of decrement function
   decrement() {
       //To update the state we need invoke this.setState
       //with new value for variable 'number'
       this.setState({number: this.state.number - 1});
   }
   increment() {
       this.setState({number: this.state.number + 1});
   }
}
最终减量应用

如果你点击DECREMENT和INCREMENT按钮, 则会看到该文本会自动为你更新。无需显式使用textView.setText(” Value” +数字)。

状态功能之所以派上用场, 有多种原因:

  • 轻松获取值-你始终知道在何处以及如何获取特定变量的值。
  • 数据未绑定到特定的小部件。
  • 具有多个依赖于公共值更改的小部件。

为/ r / pics创建搜索应用

现在, 我们已经掌握了基本知识, 让我们创建一些更复杂的东西:/ r / pics的搜索应用程序。 Reddit提供了一个简单明了的JSON API端点, 因此我们无需再进行其他工作即可获得身份验证以使其正常工作。

React Native提供了一个内置的Fetch API。由于我们大多数人可能都习惯于翻新及其易用性, 因此我们将使用axios。你可以通过终端命令安装axios

使用纱线(我的首选方法):

纱线添加轴

或使用npm:

npm安装axios

进口:

import React from 'react';
import {
   TextInput, View, Text, Image, ActivityIndicator, Platform, StyleSheet
} from 'react-native';
import axios from 'axios';
TextInput = EditText, ActivityIndicator = ProgressBar
Platform - Platform detecting module
StyleSheet - Module for creating stylesheets and moving them away from JSX

创建类:

export default class App extends React.Component {
}

初始化状态。我们需要:

  • loading-用于显示进度条。
  • 错误-显示在发出REST API请求时是否产生一些错误。
  • imgUrl-预览搜索到的图像。
  • 文字-搜索查询。
state = {text: '', loading: false, error: null, imgUrl: null};

添加JSX代码。我们具有TextInput和Image组件的垂直布局。

render() {
   return (
       //Predefined style. See below
       <View style={styles.containerStyle}>
           {/*
            returnKeyType ~ imeOptions
            onSubmitEditing ~ et.OnEditorActionListener
           */}
           <TextInput
               style={styles.textInputStyle}
               placeholder="Enter text to search an image"
               returnKeyType='search'
               autoFocus={true}
               onChangeText={(text) => this.setState({text})}
               onSubmitEditing={() => this.searchPicture()}/>
           {/*
            Render error Image component
            if this.state.imgUrl is
            not equal to null
           */}
           {
               this.state.imgUrl &&
               <Image
                   source={{uri: this.state.imgUrl}}
                   style={{flex: 1}}/>
           }
       </View>
   );
}

新的东西:

onChangeText={(text) => this.setState({text})}
onSubmitEditing={() => this.searchPicture()}
{
   this.state.imgUrl &&
   <Image
       source={{uri: this.state.imgUrl}}
       style={{flex: 1}}/>
}

第一种方法与使用TextWatcher组件的EditText做类似的工作。老实说, React Native更好。

触发searchPicture()后, 在键盘上按下返回键(et.OnEditorActionListener)时, 将调用第二种方法。

当imgUrl不为null或未定义时, 将渲染Image, 因为” &&”运算符不会检查第二个参数(如果第一个参数已为false)。

你可能想知道为什么this.state.imgUrl为假。好吧, 当在JavaScript中使用逻辑运算符时, 除了‘’(一个空字符串), 0, false, null或undefined以外的其他都是真。无需进行特定检查。

searchPicture() {
   //Default state
   this.setState({loading: true, error: null, imgUrl: null});
   axios.get('https://www.reddit.com/r/pics/search.json', {
       params: { //the get param map
           restrict_sr: 'on', //search only /r/pics
           limit: 1, //limit to one search item
           sort: 'new', //sort by creation date
           q: this.state.text //our search query
       }
   }).then(response => { //promise is resolved and 'then' block is triggered
       //set state with new values
       this.setState({
           imgUrl: response.data.data.children[0]
               .data.preview.images[0].source.url, error: null, loading: false
       })
   }).catch(error => {//Some error occurred
       //set error
       this.setState({error: error.message, loading: false, imgUrl: null})
   })
}

开始了。该应用程序现在应该可以正常工作了。输入搜索字符串, 然后按回车键。

最终Reddit搜索应用

由于我们的应用程序也已经准备好呈现ActivityIndi​​cator和错误, 因此我们需要在Image组件之后添加一些代码:

{
   //Separate method
   this.renderProgress()
}
{/*
Render error Text component
if this.state.error is
not equal to null
*/}
{
   this.state.error &&
   <Text style={{margin: 16, color: 'red'}}>
       {this.state.error}
   </Text>
}

你也可以将渲染组件移到主要render()方法之外:

renderProgress() {
   //If this.state.loading is true
   //return View containing a progressbar
   //View takes style array
   if (this.state.loading === true) {
       return (
           <View style={
               [styles.containerStyle, {justifyContent: 'center'}]}>
               <ActivityIndicator color='#e57373'/>
           </View>
       );
   }
}

剩下的就是样式。将它们放在App类之外。

const styles = StyleSheet.create({
   containerStyle: {
       flexDirection: 'column', flex: 1, //Since React Native is cross platform
       //let's handle both platforms.
       //Add top margin to fix status bar overlap
       marginTop: Platform.OS === 'ios' ? 20 : 0, }, textInputStyle: {
       marginLeft: 16, marginRight: 16, height: Platform.OS === 'ios' ? 30 : undefined
   }
});

现在, 我们可以添加一些其他调整, 例如在启动应用程序时自动打开软键盘。

请注意, 有一种更简单的方法可以使TextInput自动聚焦(autoFocus = {true}道具), 但是就本示例而言, 我们将不再使用它。

用prop添加对TextInput的引用:

ref = {ref => this.searchInput = ref}

并重写componentDidMount()生命周期方法, 如下所示:

componentDidMount(){
   this.searchInput.focus();
}

重新加载应用程序, 键盘会自动为我们打开。

组件生命周期方法

我们已经创建了一个组件, 但是让我们来回顾一下组件的整个生命周期。

这是React的生命周期流程:

  • constructor()-构造函数总是在应用程序启动时被调用
  • 静态_getDerivedStateFromProps_(属性, 状态)-在渲染之前和更新之后调用。返回用于更新状态的对象。返回null不更新任何内容。
  • render()-每个React Component类都需要渲染。用于渲染视图。
  • componentDidMount()-在呈现组件并将其安装到视图树后调用。
  • shouldComponentUpdate(nextProps, nextState)-状态或道具更改后调用。每次状态更新后, 返回值默认为true。如果返回true, 则调用render()。
  • getSnapshotBeforeUpdate(prevProps, prevState)-在提交呈现输出之前调用。
  • componentDidUpdate(prevProps, prevState, 快照)-呈现新更新后调用。在第一个render()之后不会调用它。
  • componentWillUnmount()-在组件卸载和销毁之前调用。
组件生命周期图

可重复使用的组件

在项目上工作时, 我们经常需要创建可重用的组件。有两种创建组件的方法:

  1. 创建一个扩展React.Component的类。如果需要生命周期方法, 则应使用此方法。
  2. 通过编写返回View的函数以简化语法。

由于我们已经创建了Component类, 因此可以为此实例创建一个函数。

假设我们需要一个<CardView>的类似物。在./src目录下创建一个”公用”文件夹。

创建CardView.js。

import React from "react";
import {View} from "react-native";
export default CardView = (props) => {
   return (
       //Style will be merged from default containerStyle
       //and props.style. props.style attributes will override
       //values if parameters are same.
       <View style={{...styles.containerStyle, ...props.style}}>
           {/*
            props.children contain subviews
            add this line if the component is container
           */}
           {props.children}
       </View>
   );
};
const styles = {
   containerStyle: {
       borderRadius: 4, margin: 5, padding: 5, elevation: 5, shadowColor: 'black', shadowRadius: 5, shadowOpacity: 0.5, shadowOffset: {width: 0, height: 3}, backgroundColor: 'white'
   }
};

使用我们新的CardView布局的LoginForm:

import React from "react";
import {TextInput, Platform, Button, StyleSheet} from "react-native";
import CardView from "../common/components/CardView";
export default class LoginForm extends React._Component _{
   render() {
       return (
    //Override default style
           <CardView style={{
               borderRadius: 4, backgroundColor: '#fff'
           }}>
               <TextInput
                   placeholder="Email"
                   style={styles.textInputStyle}/>
               <TextInput
                   placeholder="Password"
                   style={styles.textInputStyle}
                   secureTextEntry={true}/>
               <Button color="#841584"
                       title="Login"
                       onPress={() => console.log("onLoginPress")}
                       buttonStyle={styles.buttonStyle}/>
           </CardView>
       );
   }
}
const styles = StyleSheet.create({
   buttonStyle: {
       elevation: 5, height: 40
   }, textInputStyle: {
       padding: 10, //Additional params to make
       //iOS inputs prettier
       ...Platform.select({
           ios: {
               borderRadius: 2, marginTop: 5, backgroundColor: '#eeeeee'
           }
       })
   }
});

将LoginForm类导入App类中, 并用View包装

<View style={{flex: 1, justifyContent: 'center'}}>
   <LoginForm/>
</View>

如果你调整样式中的参数, 则可以获得更好的东西。

最终生成的登录表单应用程序

导航

导航到不同的场景是大多数应用程序中必不可少的部分。我们将创建一个Reddit / r / pics浏览器应用。

在React Native中创建导航非常容易。

先决条件

  • 使用yarn或npm安装反应导航
  • 用yarn或npm安装axios

首先创建两个不同的组件。

注意:你应该已经熟悉下面的大多数代码。我将全班粘贴。

PictureList.js:

import React from 'react';
import {
   ActivityIndicator, FlatList, Image, Text, TouchableHighlight, View
} from "react-native";
import axios from "axios";
import CardView from "../common/CardView";
export default class PictureList extends React.Component {
   state = {loading: true, error: null, posts: null};
   componentDidMount() {
       axios.get('https://www.reddit.com/r/pics.json')
           .then(response => {
               this.setState({
                   posts: response.data.data.children, loading: false
               })
           }).catch(error => {
           this.setState({
               error: error.message, loading: false
           })
       })
   }
   render() {
       return (
           <View style={{flex: 1, justifyContent: 'center'}}>

                // FlatList ~ ListView
                // data - DataSource for the List
                // renderItem - function returns View item
                // keyExtractor - Unique id for items

               {this.state.posts &&
               <FlatList data={this.state.posts}
                         renderItem={this.renderItem.bind(this)}
                         keyExtractor={(item) => (item.data.id + '')}/>}
               {this.state.loading &&
               <ActivityIndicator size="large" color="#f4511e"/>}
           </View>
       );
   }
   navigateToPicture(title, url) {
       this.props.navigation.navigate('PicturePreview', {
           'title': title, 'url': url
       })
   }
   renderItem(item) {
       //Destructuring values from item
       //Read more 'ES6 destructuring'
       const {data} = item.item;
       const {title} = data;
       const {url} = data.preview.images[0].source;
       return (
           //Clickable view
           <TouchableHighlight onPress={() =>
               this.navigateToPicture(title, url)}>
               {/Reusing our CardView/}
               <CardView>
                   <Image style={{height: 150}}
                          source={{uri: url}}/>
                   <Text style={{padding: 5}}>{title}</Text>
               </CardView>
           </TouchableHighlight>
       )
   }
}

PicturePreview.js:

import React from 'react';
import {Image} from "react-native";
export default class PicturePreview extends React.Component {
   //Destructure navigation
   //Set title to header
   static _navigationOptions = ({navigation}) => ({
       title: navigation.state.params.title
   });
   render() {
       const {url} = this.props.navigation.state.params;
       return (<Image style={{flex: 1}} source={{uri: url}}/>)
   }
}

navigationOptions将由React-Navigation自动调用。

现在转到App.js

注意:React-Navigation中有许多导航类型。今天, 我们将专注于StackNavigation。请参考官方网站了解详细信息。

import React from 'react';
import {createStackNavigator} from "react-navigation";
import PictureList from "./components/PictureList";
import PicturePreview from "./components/PicturePreview";
export default class App extends React.Component {
   render() {
       return (
           <Router/>
       );
   }
}
//Customize the header_
const NavigationOptions = {
   headerTintColor: '#fff', headerStyle: {
       backgroundColor: '#f4511e', }
};
//Create the router.
const Router = createStackNavigator({
       //Name the screen
       'PictureList': {
           //Link the Component
           screen: PictureList, //Additional navigation options
           navigationOptions: {
               title: '/r/pics Browser', ...NavigationOptions
           }
       }, 'PicturePreview': {
           screen: PicturePreview, navigationOptions: NavigationOptions
       }
   }, {
       //Root
       initialRouterName: 'PictureList'
   }
);

如你所见, 我们需要做的就是创建一个导航路由器, 并使应用程序呈现它。如果一切顺利, 我们将提供功能强大的Reddit / r / pics浏览器应用程序。

Android:

Android中的最终浏览应用

iOS:

iOS中的最终浏览应用

React Native很棒!

自从开始编程以来, 我就拥有纯粹的移动开发经验。但是现在我可以使用React编写几乎所有内容:移动, 桌面和Web。

如果你决定开始使用React Native开发下一个令人惊叹的应用程序, 你会发现它有它的怪癖和一些错误, 但是React Native非常实用, 适合大多数项目。

相关:构建QR扫描仪:React Native Camera教程

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?