项目结构
|-- 项目结构
|-- .gitignore
|-- app.json
|-- App.tsx
|-- babel.config.js
|-- images.d.ts
|-- package-lock.json
|-- package.json
|-- tsconfig.json
|-- yarn.lock
|-- .expo-shared
| |-- assets.json
|-- assets
| |-- icon.png
| |-- splash.png
| |-- images
| |-- cookbook-active.png
| |-- cookbook.png
| |-- location-active.png
| |-- location.png
| |-- menu-active.png
| |-- menu.png
| |-- more-active.png
| |-- more.png
| |-- search.png
| |-- swiper-1.png
| |-- swiper-2.jpeg
| |-- swiper-3.jpeg
|-- context
| |-- navigation.js
|-- mock
| |-- cookbook-category.json
| |-- cookbook-detail.json
| |-- cookbook-hotcate.json
| |-- cookbook-list-json.json
| |-- cookbook-list.json
| |-- mock.js
| |-- route.json
|-- pages
| |-- cate
| | |-- Cate.tsx
| | |-- style_cate.js
| |-- detail
| | |-- Detail.tsx
| | |-- style_detail.js
| |-- home
| | |-- Home.tsx
| | |-- HotCate.tsx
| | |-- style_home.js
| | |-- Swiper.tsx
| | |-- Top10.tsx
| |-- index
| | |-- Index.tsx
| | |-- styled_index.js
| | |-- style_index.js
| |-- map
| | |-- Map.tsx
| |-- more
| |-- More.tsx
|-- store
| |-- index.ts
|-- utils
|-- http.js
环境搭建
本项目是应用
ReactNative
,TypeScript
,Mobx
等技术开发的一个“美食大全”的项目,基本的环境搭建,大家参照本文基础部分。
expo init rn-cookbooks
然后选择 blank (TypeScript):
? Choose a template:
----- Managed workflow -----
blank a minimal app as clean as an empty canvas
❯ blank (TypeScript) same as blank but with TypeScript configuration
tabs several example screens and tabs using react-navigation
----- Bare workflow -----
minimal bare and minimal, just the essentials to get you started
minimal (TypeScript) same as minimal but with TypeScript configuration
启动项目:
cd rn-cookbooks
yarn start
Index组件初始化
在根目录下创建
pages/index
文件夹,在里面创建一个Index.tsx
文件,编辑内容:
// pages/index/Index.tsx
import React, { Component } from 'react'
import {
View,
Text,
StyleSheet
} from 'react-native'
interface Props {
}
interface State {
}
export default class Index extends Component {
constructor(props) {
super(props)
}
state: State = {
}
componentDidMount() {
}
render() {
return (
Index 组件内容
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
})
修改根目录下的 App.tsx
:
import React from 'react'
import Index from './pages/index/Index'
export default function App() {
return (
)
}
引入tabbar导航器
在项目环境命令行里安装 tabbar 导航器,详细内容可参见 react-native-tab-navigator 官网
yarn add react-native-tab-navigator -S
修改 index.tsx, 引入 tab-navigator 代码:
import React, { Component } from 'react'
import TabNavigator from 'react-native-tab-navigator'
import {
View,
Text
} from 'react-native'
import {
Img
} from './styled_index'
import styles from './style_index'
import cookbook from '../../assets/images/cookbook.png'
import cookbookActive from '../../assets/images/cookbook-active.png'
import category from '../../assets/images/menu.png'
import categoryActive from '../../assets/images/menu-active.png'
import map from '../../assets/images/location.png'
import mapActive from '../../assets/images/location-active.png'
import more from '../../assets/images/more.png'
import moreActive from '../../assets/images/more-active.png'
interface Props {
}
interface State {
selectedTab: string
}
class Index extends Component {
constructor(props: Props) {
super(props)
}
state: State = {
selectedTab: 'home'
}
componentDidMount() {
}
render() {
return (
}
renderSelectedIcon={() => }
onPress={() => this.setState({ selectedTab: 'home' })}
>
{美食大全 }
}
renderSelectedIcon={() => }
onPress={() => this.setState({ selectedTab: 'category' })}
>
{分类 }
}
renderSelectedIcon={() => }
onPress={() => this.setState({ selectedTab: 'map' })}
>
{地图 }
}
renderSelectedIcon={() => }
onPress={() => this.setState({ selectedTab: 'more' })}
>
{更多 }
)
}
}
export default Index
问题:
- ts 提示引入的 png 不能识别,飘红了。解决方案是在项目跟目录下创建
images.d.ts
文件,内容如下:
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
在 pages/index
下创建样式文件:
下面使用两种形式书写样式表,到时候自行开发按需选择
- 安装 styled-components 模块
npm i styled-components -D
- 创建 style
d
_index.js, 内容如下:
import styled from 'styled-components'
const Img = styled.Image `
width: 25px;
height: 25px;
`
export {
Img
}
再创建一个样式文件 style_index.js, 内容如下:
import { StyleSheet } from 'react-native'
export default StyleSheet.create({
titleStyle: {
color: '#666'
},
tabBarStyle: {
paddingBottom: 34,
height: 80
},
selectedTitleStyle: {
color: '#000'
}
})
tabbar 的兼容处理
安装 expo-device
npm i expo-device -S
修改 index.ts
, 根据您设备情况引入不同的样式,此处只是测试性代码,只做了iphone XR 和 其他非 “齐刘海” iPhone 手机:
// 载入模块
import * as Device from 'expo-device'
// 在 TabNavigator 上修改 tabBarStyle
搭建antd-mobile-rn环境
在开始之前,推荐先学习 React 和 ES2015。使用了
babel
,试试用 ES2015 的写法来提升编码的愉悦感。确认 Node.js 已经升级到 v4.x 或以上。
1. 创建一个项目
可以是已有项目,或者是使用 create-react-native-app 新创建的空项目,你也可以从 官方示例 脚手架里拷贝并修改
参考更多 官方示例集
或者你可以利用 React 生态圈中的 各种脚手架
完整步骤请查看此处文档: antd-mobile-sample/create-react-native-app
2. 安装
npm install @ant-design/react-native --save
or
yarn add @ant-design/react-native
链接字体图标
react-native link @ant-design/icons-react-native
3. 使用
按需加载
下面两种方式都可以只加载用到的组件,选择其中一种方式即可。
-
使用 babel-plugin-import(推荐)。
// .babelrc or babel-loader option { "plugins": [ ["import", { libraryName: "@ant-design/react-native" }] // 与 Web 平台的区别是不需要设置 style ] }
然后改变从 @ant-design/react-native 引入模块方式即可。
import { Button } from '@ant-design/react-native';
创建Home组件
在项目根目录下创建
pages/home
文件夹,在这个文件夹下创建Home.tsx
文件,内容如下:
import React, { Component } from 'react'
import Swiper from './Swiper'
import HotCate from './HotCate'
interface Props {
}
interface State {
}
class Home extends Component {
render() {
return (
<>
>
)
}
}
export default Home
此时在 Home.tsx 中引入 Swiper 和 HotCate 两个组价。
创建Swiper组件
在根目录下创建 utils 文件夹,在这个文件夹里创建 http.js 文件,内容如下:
// utils/http.js
export const get = (url) => {
return fetch(url, {
method: 'get'
})
.then(response => response.json())
.then(result => {
return result.data
})
}
在 pages/home 文件夹里再创建一个 Swiper.tsx 组件,内容如下:
import React, { Component } from 'react'
import { Carousel } from '@ant-design/react-native'
import { get } from '../../utils/http'
import {
View,
Image
} from 'react-native'
import styles from './style_home'
interface Props {
}
interface State {
list: Array
}
class Swiper extends Component {
state = {
list: []
}
async componentDidMount() {
let list = await get('http://localhost:9000/api/swiper')
this.setState({
list
})
}
render() {
return (
{
this.state.list.slice(0, 5).map((value, index) => {
return (
)
})
}
)
}
}
export default Swiper
在 page/home 文件里创建 style_home.js 文件,编辑样式如下:
import { StyleSheet } from 'react-native'
export default StyleSheet.create({
// swiper
wrapper: {
height: 170
},
containerHorizontal: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
height: 170
},
slideImg: {
height: 170,
width: '100%'
},
})
创建HotCate组件
在
pages/home
文件夹里构建HotCate.tsx
文件,内容为:
import React, { Component } from 'react'
import { Grid } from '@ant-design/react-native'
import { get } from '../../utils/http'
import styles from './style_home'
import {
View,
Text,
Image,
StyleSheet
} from 'react-native'
interface Props {
}
interface State {
hotCate: Array
修改 pages/home/style_home.js
文件,样式如下:
import { StyleSheet } from 'react-native'
export default StyleSheet.create({
// hotcate
container: {
paddingTop: 20,
paddingBottom: 10
},
gridContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
gridText: {
fontSize: 16,
margin: 6
},
gridImg: {
width: 70,
height: 70,
borderRadius: 5
},
})
创建Top10组件
Top10组件渲染的数据和Swiper组件可以使用同一个接口的数据,因此我们决定应用Mobx来管理这个数据。
安装 Mobx 相关模块
npm i mobx mobx-react -S
构建 store
在项目根目录下创建 store 文件夹,在这个文件下创建 index.js 文件:
// store/index.js
import {
observable,
action,
computed
} from 'mobx'
class Store {
// swiper 与 top10 共享的数据
@observable
list = []
// swiper 数据过滤
@computed
get swiper() {
return this.list.slice(0, 5).map((value, index) => {
return {
img: value.img
}
})
}
// top10 数据过滤
@computed
get top10() {
return this.list.slice(0, 10).map((value, index) => {
return {
img: value.img,
all_click: value.all_click,
favorites: value.favorites,
name: value.name
}
})
}
// 装载 list 数据
@action.bound
setList(data) {
this.list = data
}
}
export default new Store()
开始构建 Top.tsx 组件
在 pages/home
下创建 Top.tsx
文件:
// pages/home/Top.tsx
import React, { Component } from 'react'
import { Grid } from '@ant-design/react-native'
import { observer, inject } from 'mobx-react'
import {
View,
Text,
Image
} from 'react-native'
import styles from './style_home.js'
interface Props {
// store 作为组件的 props
store?: any
}
interface State {
}
// 注入 store 与 将类变为可观察的对象
@inject('store')
@observer
class Top10 extends Component {
renderTop10(el, index) {
return (
{el.name}
{el.all_click} {el.favorites}
)
}
render() {
return (
精品好菜
)
}
}
export default Top10
注意:expo-cli 构建的项目,默认 ts 配置不支持装饰器,会给出如下警告:
Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.
需要修改项目根目录下的 tsconfig.json,添加:
"experimentalDecorators": true
如果不能起作用,重新启动VSCode即可。
添加 top10 样式
// pages/home/style_home.js
import { StyleSheet } from 'react-native'
export default StyleSheet.create({
// top10
top10Container: {
paddingBottom: 44,
backgroundColor: '#eee'
},
top10Head: {
height: 50,
paddingLeft: 10,
justifyContent: 'flex-end',
},
top10HeadText: {
fontSize: 18
},
top10ItemContainer: {
flex: 1,
paddingRight: 10
},
top10DesContainter: {
marginLeft: 10,
paddingTop: 10,
paddingBottom: 10,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff'
},
top10ImgContainer: {
paddingLeft: 10,
paddingTop: 10,
flex: 1
},
Top10Img: {
width: '100%',
height: '100%',
},
top10Titie: {
fontSize: 20
},
Top10Desc: {
color: '#666'
}
})
更改Swiper和Home组件
Swiper 组件和 Top10 组件共享了数据,因此在 store 构建好后,需要改造一下:
// pages/home/Swiper.tsx
import React, { Component } from 'react'
import { Carousel } from '@ant-design/react-native'
import { get } from '../../utils/http'
import { observer, inject } from 'mobx-react'
import {
View,
Image
} from 'react-native'
import styles from './style_home'
interface Props {
// store 作为组件的 props
store?: any
}
interface State {
}
// 注入 store 与 将类变为可观察的对象
@inject('store')
@observer
class Swiper extends Component {
state = {
list: []
}
async componentDidMount() {
let list = await get('http://localhost:9000/api/swiper')
this.props.store.setList(list)
}
render() {
return (
{
this.props.store.swiper.map((value, index) => {
return (
)
})
}
)
}
}
export default Swiper
改造 Home.tsx
组件
在 Home.tsx 组件引入 Top10 组件,同时添加 ScrollView 组件,实现页面滚动效果。
// page/home/Home.tsx
import React, { Component } from 'react'
import { ScrollView } from 'react-native'
import Swiper from './Swiper'
import HotCate from './HotCate'
import Top10 from './Top10'
interface Props {
}
interface State {
}
class Home extends Component {
render() {
return (
)
}
}
export default Home
创建List组件
接下来构建另一个页面,首先在 pages 目录下创建 list 文件夹,在此文件夹里创建 List.tsx 组件文件和 style_list.js 样式文件。
List.tsx
// pages/list/List
import React, { Component, createRef } from 'react'
import {
inject,
observer
} from 'mobx-react'
import {
View,
Text,
Image,
FlatList
} from 'react-native'
import styles from './style_list'
interface Props {
store?: any
}
interface State {
// 记录上拉加载更多的当前页码
curPage: number,
// 页面显示的数据
datalist: Array
style_list.js
样式
import { StyleSheet } from 'react-native'
export default StyleSheet.create({
listWrap: {
flexDirection: 'row',
padding: 10,
borderBottomWidth: 1,
borderStyle: 'solid',
borderBottomColor: '#eee'
},
imgWrap: {
width: 135,
paddingRight: 10
},
image: {
width: 115,
height: 75
},
descWrap: {
flex: 1
},
title: {
fontSize: 20,
lineHeight: 30
},
subtitle: {
fontSize: 16,
color: '#666',
lineHeight: 30,
overflow: 'hidden'
},
desc: {
fontSize: 12,
color: '#666'
}
})
react-navigation
本项目应用 React Navigation 构建路由系统。
安装 React Navigation 环境
npm install @react-navigation/native
npm install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view
npm install @react-navigation/stack
给App.tsx配置路由
import React from 'react'
import { NavigationContainer } from '@react-navigation/native'
import { createStackNavigator } from '@react-navigation/stack'
import { Provider } from 'mobx-react'
import store from './store/'
import Index from './pages/index/Index'
import List from './pages/list/List'
import Detail from './pages/detail/Detail'
const Stack = createStackNavigator()
export default function App() {
// 这里配置了三个页面
return (
)
}
创建 Context
为了让组件能收到路由的信息,这里我们自己构建了一个 Context。
在根目录下创建一个context目录,在此目录下创建一个 navigation.js
文件,内容如下:
// context/navigations.js
import { createContext } from 'react'
const navigationContext = createContext()
let { Provider, Consumer } = navigationContext
export {
navigationContext,
Provider,
Consumer
}
修改 index.tsx
1.解构出Provider
import { Provider } from '../../context/navigation'
2.通过Context 的Provider,将props递交给后代组件
3.全部内容
// pages/index/Index.tsx
import React, { Component, ContextType } from 'react'
import TabNavigator from 'react-native-tab-navigator'
import * as Device from 'expo-device'
// 解构出 Provider
import { Provider } from '../../context/navigation'
import {
View,
Text
} from 'react-native'
import {
Img
} from './styled_index'
import styles from './style_index'
import cookbook from '../../assets/images/cookbook.png'
import cookbookActive from '../../assets/images/cookbook-active.png'
import category from '../../assets/images/menu.png'
import categoryActive from '../../assets/images/menu-active.png'
import map from '../../assets/images/location.png'
import mapActive from '../../assets/images/location-active.png'
import more from '../../assets/images/more.png'
import moreActive from '../../assets/images/more-active.png'
import Home from '../home/Home'
import List from '../list/List'
import Detail from '../detail/Detail'
interface Props {
navigation?: any
}
interface State {
selectedTab: string
}
class Index extends Component {
constructor(props: Props) {
super(props)
}
state: State = {
selectedTab: 'home'
}
componentDidMount() {
}
render() {
return (
<>
}
renderSelectedIcon={() => }
onPress={() => {
this.setState({ selectedTab: 'home' })
this.props.navigation.setOptions({ title: '美食大全' })
}}
>
{/* 通过Context 的Provider,将props递交给后代组件 */}
}
renderSelectedIcon={() => }
onPress={
() => {
this.setState({ selectedTab: 'category' })
this.props.navigation.setOptions({ title: '热门' })
}
}
>
{/* 通过Context 的Provider,将props递交给后代组件 */}
}
renderSelectedIcon={() => }
onPress={() => {
this.setState({ selectedTab: 'map' })
this.props.navigation.setOptions({ title: '地图' })
}}
>
{地图 }
}
renderSelectedIcon={() => }
onPress={() => {
this.setState({ selectedTab: 'more' })
this.props.navigation.setOptions({ title: '更多' })
}}
>
{更多 }
>
)
}
}
export default Index
修改 home.tsx
1. 将路由信息传给HotCate
2.定义Props
interface Props {
navigation?: any
}
3.全部内容
// pages/home/Home.tsx
import React, { Component } from 'react'
import { ScrollView, StatusBar } from 'react-native'
import Swiper from './Swiper'
import HotCate from './HotCate'
import Top10 from './Top10'
interface Props {
navigation?: any
}
interface State {
}
class Home extends Component {
render() {
return (
{/* 将路由信息传给HotCate */}
)
}
}
export default Home
修改 HotCate.tsx
1. 导入
import { Consumer } from '../../context/navigation'
2. 路由到“热门”页面
_onPress = (navigation) => {
return () => {
navigation.push('List')
}
}
{
({navigation}) => {
return (
)
}
}
3. 全部代码
// pages/home/HotCate.tsx
import React, { Component } from 'react'
import { Grid } from '@ant-design/react-native'
import { get } from '../../utils/http'
import { Consumer } from '../../context/navigation'
import styles from './style_home'
import {
View,
Text,
Image
} from 'react-native'
interface Props {
}
interface State {
hotCate: Array
修改 Top10.tsx
1. 通过 contextType 定义 context
import { navigationContext } from '../../context/navigation'
2. 导航到详情页,并传参
import { navigationContext } from '../../context/navigation'
static contextType = navigationContext
_onPress = (e) => {
this.context.navigation.push('Detail', { name: e.name })
}
3. 全部代码
// pages/home/Top10.tsx
import React, { Component } from 'react'
import { Grid } from '@ant-design/react-native'
import { observer, inject } from 'mobx-react'
import { navigationContext } from '../../context/navigation'
import {
View,
Text,
Image
} from 'react-native'
import styles from './style_home.js'
interface Props {
// store 作为组件的 props
store?: any
}
interface State {
}
// 注入 store 与 将类变为可观察的对象
@inject('store')
@observer
class Top10 extends Component {
static contextType = navigationContext
_renderTop10(el, index) {
return (
{el.name}
{el.all_click} {el.favorites}
)
}
_onPress = (e) => {
this.context.navigation.push('Detail', { name: e.name })
}
render() {
return (
精品好菜
)
}
}
export default Top10
修改 List.tsx
1. 载入路由相关模块,实现路由到详情页的功能,主要代码:
// 1. 载入Context
import { navigationContext } from '../../context/navigation'
// 2. 在 Props 里定义 navigation
interface Props {
store?: any,
navigation?: any
}
// 3. 在类里定义 contextType 静态变量
static contextType = navigationContext
// 4. 在组件类里定义路由跳转响应方法
_onPress = (name: string) => {
return () => {
// 鉴于此页面从 TabBar 和 首页两个入口进入
// 路由跳转的方式也不同
if (this.context) {
// 从Tabbar进入
this.context.navigation.push('Detail', {name})
} else {
// 从首页进入
this.props.navigation.push('Detail', {name})
}
}
}
// 5. 应用 TouchableOpacity 组件绑定路由跳转事件
{name}
{burdens}
{all_click} {favorites}
2. 全部代码
import React, { Component, createRef } from 'react'
import { navigationContext } from '../../context/navigation'
import {
inject,
observer
} from 'mobx-react'
import {
View,
Text,
Image,
FlatList,
TouchableOpacity
} from 'react-native'
import styles from './style_list'
interface Props {
store?: any,
navigation?: any
}
interface State {
// 记录上拉加载更多的当前页码
curPage: number,
// 页面显示的数据
datalist: Array
创建详情页
在路由信息定义好后,就可以构建详情页了。
Detail.tsx
// pages/detail/Detail.tsx
import React, { Component } from 'react'
import { get } from '../../utils/http'
import {
View,
ScrollView,
Text,
Image,
StatusBar,
TouchableOpacity,
Alert
} from 'react-native'
import styles from './style_detail'
interface Props {
navigation?: any,
route?: any
}
interface State {
detail: {}
}
export default class Detail extends Component {
state = {
detail: null
}
async componentDidMount() {
let result = await get('http://localhost:9000/api/detail')
this.setState({
detail: result
})
// 根据路由传递过来参数,修改本页的 title
this.props.navigation.setOptions({ title: this.props.route.params.name })
}
render() {
let detail = this.state.detail
return (
<>
{
detail && (
{detail.name}
{detail.all_click}浏览/{detail.favorites}收藏
Alert.alert('已经收藏.')}
>
收藏
心得
{detail.info}
做法
{
detail.makes.map((value) => {
return (
{value.num} {value.info}
)
})
}
)
}
>
)
}
}
style_detail.js 页面样式
// pages/detail/style_detail.js
import { StyleSheet } from 'react-native'
export default StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#eee',
paddingBottom: 34
},
// main
mainImg: {
width: '100%',
height: 250
},
mainInfo: {
height: 170,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff'
},
mainInfoName: {
fontSize: 24
},
mainInfoSubTitle: {
marginTop: 10,
fontSize: 12,
color: '#666'
},
// button
mainInfoButtonWrap: {
width: 140,
height: 40,
backgroundColor: '#df7b42',
marginTop: 20,
borderRadius: 6
},
mainInfoButton: {
textAlign: 'center',
lineHeight: 40,
color: '#fff',
fontSize: 16
},
// info
infoWrap: {
marginTop: 15,
backgroundColor: '#fff',
paddingTop: 25,
paddingLeft: 15,
paddingBottom: 25
},
infoTitle: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 20
},
infoText: {
fontSize: 16,
lineHeight: 20,
paddingRight: 20
},
// makes
makesTitle: {
fontSize: 16,
paddingRight: 30
},
makesImg: {
width: 300,
height: 210,
margin: 20
}
})
创建美食地图页
“美食地图” 主要应用的知识点是 WebView, 根据官网推荐,使用 react-native-webview 项目来实现在RN里嵌入Web页面。
模块安装
npm install --save react-native-webview
react-native link react-native-webview
Map.tsx 文件构建
在项目根目录 pages 下创建目录 map, 在 map 目录下创建 Map.tsx 文件,文件内容如下:
import React, { Component } from 'react'
import { View } from 'react-native'
import { WebView } from 'react-native-webview'
interface Props {
}
interface State {
}
export default class Map extends Component {
state = {}
render() {
return (
)
}
}
是否显示地图页签
更多页面实现了两个功能:
1、是否显示地图页签
2、拍照功能
是否显示地图页签
更新 store
添加了 isShow 属性,和 setVisible 方法。
// store/index.js
import {
observable,
action,
computed
} from 'mobx'
class Store {
// swiper 与 top10 共享的数据
@observable
list = []
// 定义是否显示地图按钮
@observable
isShow = true
// swiper 数据过滤
@computed
get swiper() {
return this.list.slice(0, 5).map((value, index) => {
return {
img: value.img
}
})
}
// top10 数据过滤
@computed
get top10() {
return this.list.slice(0, 10).map((value, index) => {
return {
img: value.img,
all_click: value.all_click,
favorites: value.favorites,
name: value.name
}
})
}
// 装载 list 数据
@action.bound
setList(data) {
this.list = data
}
// 修改是否显示地图按钮
@action.bound
setVisible(status) {
this.isShow = status
}
}
export default new Store()
添加 More.tsx 文件
在根目录pages下创建 more 文件夹,再创建 More.tsx 文件,内容如下
// pages/more/More.tsx
import React, { Component } from 'react'
import { View, Text, Switch, AsyncStorage } from 'react-native'
import { observer, inject } from 'mobx-react'
interface Props {
store?: any
}
interface State {
}
@inject('store')
@observer
export default class Profile extends Component {
state = {
}
async componentDidMount() {
}
render() {
return (
是否显示地图:
{
this.props.store.setVisible(value)
AsyncStorage.setItem('isShow', value.toString())
}}
>
)
}
}
修改 pages/index/Index.tsx 文件
1、修改代码的要点
// 定义 store
interface Props {
navigation?: any
store?: any
}
// 记录用户缓存
async componentDidMount() {
let isShow = await AsyncStorage.getItem('isShow')
this.props.store.setVisible(JSON.parse(isShow))
}
// 在 tabbar 里修改
{
this.props.store.isShow
? (
}
renderSelectedIcon={() => }
onPress={() => {
this.setState({ selectedTab: 'map' })
this.props.navigation.setOptions({ title: '地图' })
}}
>
)
: null
}
2. 全部代码
// pages/index/Index.tsx
import React, { Component, ContextType } from 'react'
import TabNavigator from 'react-native-tab-navigator'
import * as Device from 'expo-device'
import { observer, inject } from 'mobx-react'
import { Provider } from '../../context/navigation'
import {
View,
Text,
AsyncStorage
} from 'react-native'
import {
Img
} from './styled_index'
import styles from './style_index'
import cookbook from '../../assets/images/cookbook.png'
import cookbookActive from '../../assets/images/cookbook-active.png'
import category from '../../assets/images/menu.png'
import categoryActive from '../../assets/images/menu-active.png'
import map from '../../assets/images/location.png'
import mapActive from '../../assets/images/location-active.png'
import more from '../../assets/images/more.png'
import moreActive from '../../assets/images/more-active.png'
import Home from '../home/Home'
import List from '../list/List'
import Map from '../map/Map'
import More from '../more/More'
interface Props {
navigation?: any
store?: any
}
interface State {
selectedTab: string
}
@inject('store')
@observer
class Index extends Component {
constructor(props: Props) {
super(props)
}
state: State = {
selectedTab: 'home'
}
async componentDidMount() {
let isShow = await AsyncStorage.getItem('isShow')
this.props.store.setVisible(JSON.parse(isShow))
}
render() {
return (
<>
}
renderSelectedIcon={() => }
onPress={() => {
this.setState({ selectedTab: 'home' })
this.props.navigation.setOptions({ title: '美食大全' })
}}
>
}
renderSelectedIcon={() => }
onPress={
() => {
this.setState({ selectedTab: 'category' })
this.props.navigation.setOptions({ title: '热门' })
}
}
>
{
this.props.store.isShow
? (
}
renderSelectedIcon={() => }
onPress={() => {
this.setState({ selectedTab: 'map' })
this.props.navigation.setOptions({ title: '地图' })
}}
>
)
: null
}
}
renderSelectedIcon={() => }
onPress={() => {
this.setState({ selectedTab: 'more' })
this.props.navigation.setOptions({ title: '更多' })
}}
>
>
)
}
}
export default Index
拍照功能
更多页面实现了两个功能:
1、是否显示地图页签
2、拍照功能
安装模块
npm install expo-camera -S
改写 More.tsx 代码
以下代码是 拍照 和 切换显示地图按钮 的全部代码。
import React, { Component } from 'react'
import { View, Text, Switch, AsyncStorage, TouchableOpacity, Image } from 'react-native'
import { observer, inject } from 'mobx-react'
import * as Permissions from 'expo-permissions'
import { Camera } from 'expo-camera'
interface Props {
store?: any
}
interface State {
hasCameraPermission: boolean
type: boolean
isTakePic: boolean,
picUri: string
}
@inject('store')
@observer
export default class Profile extends Component {
camera = null
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
isTakePic: false,
picUri: 'http://placehold.it/240x180'
}
async componentDidMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({
hasCameraPermission: status === 'granted'
})
}
showTakePicScene() {
this.setState({
isTakePic: true
})
}
async takePicture() {
let result = await this.camera.takePictureAsync()
this.setState({
isTakePic: false,
picUri: result.uri
})
}
render() {
return (
<>
{
this.state.isTakePic
? (
{
this.camera = ref
}}
>
拍照
)
: (
是否显示地图:
{
this.props.store.setVisible(value)
AsyncStorage.setItem('isShow', value.toString())
}}
>
拍照
)
}
>
)
}
}
项目发布
本项目发布利用expo发布功能,详细可参考 构建独立的应用程序
安装 Expo CLI
此步骤已经完成。
配置 app.json
{
"expo": {
"name": "rn-cookbooks",
"slug": "rn-cookbooks",
"privacy": "public",
"sdkVersion": "36.0.0",
"platforms": [
"ios",
"android",
"web"
],
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"bundleIdentifier": "com.qianfeng.felixlu",
"buildNumber": "1.0.0"
},
"android": {
"package": "com.qianfeng.felixlu",
"versionCode": 1
}
}
}
开始Build
expo build:android
或:
expo build:ios
build 过程监测
输入以上命令后,会在控制台看到下边信息:
Build started, it may take a few minutes to complete.
You can check the queue length at https://expo.io/turtle-status
You can monitor the build at
https://expo.io/dashboard/felixlurt/builds/15b2ae11-c98d-48dc-879e-9ff05fb0b9f1
可以通过访问 https://expo.io/dashboard/felixlurt/builds/15b2ae11-c98d-48dc-879e-9ff05fb0b9f1
来监控build过程。(注意链接是终端打印的,这个链接只是个示例)
build 成功后,点击 “Download” 按钮即可下载打完的APP安装包了。
注:iOS 需要有开发者账号,没有账号的童鞋建议运行
expo build:android
进行试验项目源码下载
点击下载
感谢