ReactNative豆瓣电影

欢迎访问我的博客,祝码农同胞们早日走上人生巅峰,迎娶白富美

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
## 相关资源

**项目技能**:react,react-native,android环境

**项目目的**:只做项目环境搭建和小部分功能以练习react-native基本功能和android上app的打包构建

**项目地址:[ReactNative豆瓣电影](https://github.com/ForeManWang/ReactNative-douban)**

## ReactNative项目环境搭建

参考我这个文档[ReactNative项目环境搭建](https://qqqww.com/ReactNative%E9%A1%B9%E7%9B%AE%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/)

## 首页

`在进行这一步之前,先确认项目环境搭建并打包下载到手机上没有任何问题之后,进行之后的代码编写和调试`

### 如何更改首页

**知识点:`View组件`和`Text组件`**

**编写一个自己的首页**:根目录下创建`MyHomePage.js`作为自己的首页

**注意:在 RN 中只能使用 .js 不能使用 .jsx**

```javascript/** * 自己的首页 */

// 导入 reactimport React, { Component } from 'react'// 按需导入组件, View 组件就好比网页中的 div 元素import { View, Text } from 'react-native'

export default class MyHomePage extends Component {  constructor(props) {    super(props)    this.state = {}  }  render() {    // 1. 在 RN 中不能使用在网页中的所有标签    // 2. 如果想要实现布局,RN 提供了一个叫做 View 的组件,来实现布局, 想要使用,要先导入    return (      <View>          { /*3. 在 RN 中,所有的文本,必须使用 RN 提供的 Text 组件进行包裹,依然是按需导入*/ }        <Text>123456</Text>      </View>)  }}```

在`index.js`中导入自己的组件

```javascript// 导入自己的组件页面import MyHomePage from './MyHomePage.js'// 当使用 AppRegistry 注册项目的时候,方法中的第一个参数不要改// 第二个参数表示要把哪个页面注册为首页AppRegistry.registerComponent(appName, () => MyHomePage);```

## 组件的学习

[ReactNative组件](https://qqqww.com/ReactNative组件/)

## 正式开始豆瓣电影项目

练习了上面一些组件和属性之后,着手去做一个豆瓣电影的小项目

### Tabbar

#### 基本结构

使用组件`react-native-tab-navigator`

**react-native-tab-navigator使用方法**:老套路,对照官方文档,装包=>导入=>使用

这里的装包,不推荐使用`npm`,首先下载慢,其次如果是`npm5.X`在装新包的时候会把一些老包删除,可能会出现猝不及防的惊喜~

我这里使用的是facebook开发的yarn装包

1
yarn add react-native-tab-navigator // 默认是 --save
12
// 导入 Tabbvar 相关组件import TabNavigator from 'react-native-tab-navigator'
1234567891011121314151617181920212223242526
export default class MyHomePage extends Component {  constructor(props) {    super(props)    this.state = {}  }  render() {    // 1. 在 RN 中不能使用在网页中的所有标签    // 2. 如果想要实现布局,RN 提供了一个叫做 View 的组件,来实现布局, 想要使用,要先导入    return (      <View style={styles.container}>         <TabNavigator>          <TabNavigator.Item title="Home">            // 放入组件          </TabNavigator.Item>          <TabNavigator.Item title="Me">            // 放入组件          </TabNavigator.Item>              </TabNavigator>      </View>);  }}const styles = StyleSheet.create({  container: {    flex: 1  }});

这里的放入组件的位置是自己创建的组件,其他后面的自己创建的组件引入方式也是一样套路创建自己的组件=>引入=>使用

创建自己的新组件,components/tabars/Home.jscomponents/tabars/Me.js

两个组件都简单写下基本代码

12345678910
import React, { Component } from 'react'import { View, Text } from 'react-native'

export default class Search extends Component {  render() {    return <View>      <Text>这是 Home 组件</Text>    </View>  }}

引入

12
import Home from './components/tabbars/Home.js'import Me from './components/tabbars/Me.js'

使用

12345678910
<View style={styles.container}>   <TabNavigator>      <TabNavigator.Item title="Home">         <Home></Home>      </TabNavigator.Item>     <TabNavigator.Item title="Me">         <Me></Me>      </TabNavigator.Item>        </TabNavigator></View>);

组件高亮和切换

home组件举例子,官方文档上这两句加上就行了

12
selected={this.state.selectedTab === 'home'}onPress={() => this.setState({ selectedTab: 'home' })}>

Tab栏的四个组件都按照这样写完,就可以实现正常切换了

组件的图标

react-native-vector-icons的组件,安装=>配置=>导入=>使用

安装
1
yarn add react-native-vector-icons
配置
  • 在官方文档找到对应的手机平台配置,我这里应为开发的是Android,所以配置的是Android
  • 编辑 android/app/build.gradle( NOT android/build.gradle ) ,去这个文件中添加下面两行代码
12345
project.ext.vectoricons = [    iconFontNames: [ 'MaterialIcons.ttf', 'EvilIcons.ttf', 'FontAwesome.ttf' ] // Name of the font files you want to copy]

apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
  • fonts文件找到自己的字体文件C:\Users\wanggongtou\Desktop\douban\node_modules\react-native-vector-icons\Fonts下面的所有文件全部复制放到android/app/src/main/assets/fonts,下面没有assets/fonts就手动创建一个,再复制进来
  • 将下面两行代码放到android/settings.gradle下合适的位置,并把前面的+去掉
12
+ include ':react-native-vector-icons'+ project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
  • 编辑 android/app/build.gradle ,找到dependencies的大括号内部加上compile project(':react-native-vector-icons')
1234
dependencies {    ...    compile project(':react-native-vector-icons')}
  • 编辑android\app\src\main\java\com\douban\MainApplication.javapackage com.douban;

这句代码下面添加import com.oblador.vectoricons.VectorIconsPackage;这句代码,在同一个文件中,找到protected List<ReactPackage> getPackages()在其内部,加上, new VectorIconsPackage()

12345678910
package com.douban;import com.oblador.vectoricons.VectorIconsPackage;

@Override    protected List<ReactPackage> getPackages() {      return Arrays.<ReactPackage>asList(          new MainReactPackage()          , new VectorIconsPackage()      );    }

注意:只要修改了Android里面的配置文件,要重新打包构建

重新打包构建
1
react-native run-android

这里可能会遇到两点问题:

问题一:提示没有licenses协议

解决方案:去配置Android的环境目录下,找到 Android SDK Manager 安装 Android SDK Build-tools 23.0.1 并接受其 license;

注意:这里的 Android SDK Build-tools 23.0.1 版本号需要和自己的构建工具版本号相对应

问题二:打包构建之后,发现手机上的app打开没有页面了

解决方案:关闭APP进程,重新打开一下就有了

导入
1
import Icon from 'react-native-vector-icons/FontAwesome'
使用

可以去FontAwesome官网列表中查找对应组件需要的图标,Icon name=图标的名字就可以了

123456789
<TabNavigator>  <TabNavigator.Item    ...    renderIcon={() => <Icon name="home" size={25} color="gray" />} // 未选中状态下,展示的图标        renderSelectedIcon={() => <Icon name="home" size={25} color="#0079FF" />} // 选中状态下展示的图标    ...    <Home></Home>  </TabNavigator.Item></TabNavigator>

主页

主页轮播图

静态页面

找组件react-native-swiper去官网看, 安装、导入、使用

1
yarn add react-native-swiper

因为轮播图在主页,所以在主页的组件中进行导入

12
// 导入轮播图组件import Swiper from 'react-native-swiper'

把对应的结构和样式全部都拷贝过来

123456789101112131415161718192021222324252627282930313233343536373839404142434445
export default class Search extends Component {  render() {    return (      <Swiper style={styles.wrapper} showsButtons={true}>        <View style={styles.slide1}>          <Text style={styles.text}>Hello Swiper</Text>        </View>        <View style={styles.slide2}>          <Text style={styles.text}>Beautiful</Text>        </View>        <View style={styles.slide3}>          <Text style={styles.text}>And simple</Text>        </View>      </Swiper>    );  }}

const styles = StyleSheet.create({  wrapper: {  },  slide1: {    flex: 1,    justifyContent: 'center',    alignItems: 'center',    backgroundColor: '#9DD6EB',  },  slide2: {    flex: 1,    justifyContent: 'center',    alignItems: 'center',    backgroundColor: '#97CAE5',  },  slide3: {    flex: 1,    justifyContent: 'center',    alignItems: 'center',    backgroundColor: '#92BBD9',  },  text: {    color: '#fff',    fontSize: 30,    fontWeight: 'bold',  }})

这里轮播图就出来了,再设置自动轮播和轮播图高度,由于这里的轮播图默认充满全屏,所以可以给轮播图最外层套一个View,给它一个高度

123
<View style={{ height: 220 }}>   // 放轮播图代码</View>

Swiper盒子加上属性,自动轮播

123
<Swiper style={styles.wrapper} showsButtons={true} autoplay={true} loop={true}>    ...</Swiper>
渲染数据
12345678910111213141516171819202122232425262728293031323334
export default class Home extends Component {

  constructor(props) {    super(props)    this.state = {      banner: [] // 轮播图数组    }  }

  componentWillMount() {    fetch('http://www.liulongbin.top:3005/api/getlunbo')      .then(res => res.json())      .then(data => {        // console.warn(JSON.stringify(data, null, '  '))        this.setState({          banner: data.message        })      })  }

  render() {    return (      <View style={{ height: 220 }}>      <Swiper style={styles.wrapper} showsButtons={true} autoplay={true} loop={true}>       {this.state.banner.map((item, i) => {          return <View key={i}>             <Image source={{uri: item.img}} style={{width: '100%', height: '100%'}}></Image>             </View>        })}        </Swiper>      </View>    );    }}

主页六宫格

导入所需要的组件

1
import { AppRegistry, StyleSheet, View, Text, Image, TouchableHighlight } from 'react-native'
12345678910
// 六宫格区域{/* 在 RN 中,默认,就已经为 所有的 View 启用了弹性和模型,同时,默认的主轴是 纵向的 */}        <View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>

          <View style={styles.box}>            <Image source={require('../../images/menu1.png')} style={{ width: 60, height: 60 }}></Image>            <Text>新闻资讯</Text>          </View>      // ...这里根据需求可以增加主页的宫格数目,我这里是六个,上面代码复制六份就可以      </View>
1234567
var styles = StyleSheet.create({  box: {    width: '33.33%',    alignItems: 'center',    marginTop: 15  }})

热映电影

Main.js 配置路由

在项目根目录新建一个Main.js作为项目根组件,修改一下index.js中指向App.js的代码

12
import Main from './Main.js'AppRegistry.registerComponent(appName, () => Main);

去编辑Main.js的代码

1234
// Main 项目的根组件// 导入组件import React, { Component } from 'react'import { View, Image, Text, ActivityIndicator } from 'react-native'

安装一个配置路由规则的插件react-native-router-flux

1
yarn add react-native-router-flux

Main.js中导入

1
import { Router, Stack, Scene } from 'react-native-router-flux'

Router: 就相当于 HashRouter
Stack: 这是一个分组的容器,他不表示具体的路由,专门用来给路由分组的
Scene:就表示一个具体的路由规则,好比 昨天学到的 Route

新建两个组件components/movie/MovieList.jscomponents/movie/MovieDetail.js作为电影列表组件和电影描述组件

而且由于更换了项目的根组件,所以需要在Main.js中导入App.js

1234
// 导入组件import App from './App.js'import MovieList from './components/movie/MovieList.js'import MovieDetail from './components/movie/MovieDetail.js'

继续修改代码将App.js作为首页展示,在render渲染的时候,可以配置首页,并且一并配置其他页面路由

1234567891011121314
render() {  return <Router sceneStyle={{ backgroundColor: 'white' }}>    <Stack key="root">      {/* 配置路由规则 */}      {/* 注意,所有的路由规则,都应该写到这个位置 */}      {/* 第一个 Scene 就是默认要展示的首页 */}      {/* key 属性,表示路由的规则名称,将来可以使用这个 key ,进行编程式导航,每一个路由规则,都应该提供一个 唯一的key, key不能重复 */}      <Scene key="app" component={App} title="" hideNavBar={true} />      {/* 电影列表的路由规则 */}      <Scene key="movielist" component={MovieList} title="热映电影列表" />      <Scene key="moviedetail" component={MovieDetail} title="电影详情" />    </Stack>  </Router>}

这个时候,依然是不能点击跳转的,是因为没有给Home.js内部的热映电影组件绑定点击事件,所以去帮顶下

TouchableHighlight包裹Home.js中的热映电影的代码片段

1234567891011
// 在使用前应该现在最前面导入该组件import { AppRegistry, StyleSheet, View, Text, Image, TouchableHighlight } from 'react-native'      // 包裹      <TouchableHighlight onPress={this.goMovieList} underlayColor="white" style={styles.box}>            {/* 在 TouchableHighlight 内部,只能放置唯一的一个元素 */}            <View>              <Image source={require('../../images/menu5.png')} style={{ width: 60, height: 60 }}></Image>              <Text>热映电影</Text>            </View>          </TouchableHighlight>// goMovieList 方法在下面定义

导入Actions组件,实现编程式导航

123
// 导入 Actions 组件,实现编程式导航// Actions 表示要进行 JS 操作了import { Actions } from 'react-native-router-flux'

写一个跳转方法,并去TouchableHighlight中绑定这个方法

123456
render() {    ...    goMovieList = () => {      Actions.movielist()    }}
1
<TouchableHighlight onPress={this.goMovieList} underlayColor="white" style={styles.box}>

这样就实现了基本的路由跳转

豆瓣热映电影列表

基本页面
123456789101112131415161718192021222324252627
import React, { Component } from 'react'import { View, Image, Text, StyleSheet, ... } from 'react-native'

const styles = StyleSheet.create({  ...})

// 导入路由的组件import { Actions } from 'react-native-router-flux'

export default class MovieList extends Component {  constructor(props) {    super(props)    this.state = {     ...    }  }

  componentWillMount() {    ...  }

  render() {    return ...  }  ...}
豆瓣接口

这里先说一下豆瓣接口,根据接口可以进行数据的获取

访问https://api.douban.com/v2/movie/in_theaters?start=0&count=12可以看到豆瓣的电影数据,链接中的?后面的参数start表示开始页码,count表示每页显示的记录条数,可以根据需求修改,可以拷贝假数据用于测试,可以调用接口,用于测试方法成功之后进行动态数据的渲染

fetch获取电影列表数据
  1. 根据页码获取电影列表数据
  2. 渲染电影列表的方法测试
  3. 每项数据需要同步this.state
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
export default class MovieList extends Component {  constructor(props) {    super(props)    this.state = {      movies: [], // 电影列表      nowPage: 1, // 当前的页码      totalPage: 0, // 总页数      pageSize: 15, // 每页显示的记录条数      isloading: true // 是否正在加载数据    }  }

  componentWillMount() {    this.getMoviesByPage()  }

  render() {    return <View>      {this.renderList()}    </View>  }

  // 根据页码获取电影列表  getMoviesByPage = () => {    const start = (this.state.nowPage - 1) * this.state.pageSize    const url = `https://api.douban.com/v2/movie/in_theaters?start=${start}&count=${this.state.pageSize}`

    /*    fetch(url)      .then(res => res.json())      .then(data => {        this.setState({          isloading: false,          movies: this.state.movies.concat(data.subjects),          totalPage: Math.ceil(data.total / this.state.pageSize)        })      })    */

    /* 此代码用了拷贝的假数据用于测试 */    setTimeout(() => {      this.setState({        isloading: false,        movies: require('./test_list.json').subjects,        totalPage: 1      })    }, 1000)   }

  // 渲染电影列表的方法,此处用于测试,若能获取电影条数则方法可以继续往下写  renderList = () => {    if (this.state.isloading) {      return <ActivityIndicator size="large"></ActivityIndicator>    }    return <View>          <Text>{this.state.moives.length}</Text>        </View>  }  ...}
渲染电影列表数据
12345678910111213141516171819202122232425
// 渲染电影列表的方法  renderList = () => {    if (this.state.isloading) {      return <ActivityIndicator size="large"></ActivityIndicator>    }    return <FlatList      data={this.state.movies}      keyExtractor={(item, i) => i} // 解决 key 问题      renderItem={({ item }) => this.renderItem(item)} // 调用方法,去渲染每一项    />  }  // 渲染每项电影  renderItem = (item) => {    return <TouchableHighlight>      <View>        <Image source={{ uri: item.images.small }} style={{ width: 100, height: 140, marginRight: 10 }}></Image>        <View>          <Text><Text style={styles.movieTitle}>电影名称:</Text>{item.title}</Text>          <Text><Text style={styles.movieTitle}>电影类型:</Text>{item.genres.join(',')}</Text>          <Text><Text style={styles.movieTitle}>制作年份:</Text>{item.year}年</Text>          <Text><Text style={styles.movieTitle}>豆瓣评分:</Text>{item.rating.average}分</Text>        </View>      </View>    </TouchableHighlight>  }
美化布局

相当于加一些样式,不然太丑了

123456789101112131415161718192021222324252627282930313233
// 渲染电影列表的方法renderList = () => {  if (this.state.isloading) {    return <ActivityIndicator size="large"></ActivityIndicator>  }  return <FlatList    data={this.state.movies}    keyExtractor={(item, i) => i} // 解决 key 问题    renderItem={({ item }) => this.renderItem(item)} // 调用方法,去渲染每一项    ItemSeparatorComponent={this.renderSeparator} //渲染分割线的属性方法    onEndReachedThreshold={0.5} // 距离底部还有多远的时候,触发加载更多的事件    onEndReached={this.loadNextPage} // 当距离不足 0.5 的时候,触发这个方法,加载下一页数据  />}

// 渲染每项电影renderItem = (item) => {  return     <View style={{ flexDirection: 'row', padding: 10 }}>      <Image source={{ uri: item.images.small }} style={{ width: 100, height: 140, marginRight: 10 }}></Image>      <View style={{ justifyContent: 'space-around' }}>        <Text><Text style={styles.movieTitle}>电影名称:</Text>{item.title}</Text>        <Text><Text style={styles.movieTitle}>电影类型:</Text>{item.genres.join(',')}</Text>        <Text><Text style={styles.movieTitle}>制作年份:</Text>{item.year}年</Text>        <Text><Text style={styles.movieTitle}>豆瓣评分:</Text>{item.rating.average}分</Text>      </View>    </View>}

// 渲染分割线renderSeparator = () => {  return <View style={{ borderTopColor: '#ccc', borderTopWidth: 1, marginLeft: 10, marginRight: 10 }}></View>}
下拉加载更多

利用官方文档的属性onEndReachedThresholdonEndReached来控制

onEndReachedThreshold距离底部还有多远的时候,触发加载更多的事件

onEndReached:当距离不足 0.5 的时候,触发这个方法,加载下一页数据

123456789101112131415161718192021222324252627282930313233343536373839404142
// 渲染电影列表的方法 renderList = () => {   if (this.state.isloading) {     return <ActivityIndicator size="large"></ActivityIndicator>   }   return <FlatList     data={this.state.movies}     keyExtractor={(item, i) => i} // 解决 key 问题     renderItem={({ item }) => this.renderItem(item)} // 调用方法,去渲染每一项     ItemSeparatorComponent={this.renderSeparator} //渲染分割线的属性方法     onEndReachedThreshold={0.5} // 距离底部还有多远的时候,触发加载更多的事件     onEndReached={this.loadNextPage} // 当距离不足 0.5 的时候,触发这个方法,加载下一页数据   /> }

 // 渲染每项电影 renderItem = (item) => {   return <TouchableHighlight underlayColor="#fff" onPress={() => { Actions.moviedetail({ id: item.id }) }}>     <View style={{ flexDirection: 'row', padding: 10 }}>       <Image source={{ uri: item.images.small }} style={{ width: 100, height: 140, marginRight: 10 }}></Image>       <View style={{ justifyContent: 'space-around' }}>         <Text><Text style={styles.movieTitle}>电影名称:</Text>{item.title}</Text>         <Text><Text style={styles.movieTitle}>电影类型:</Text>{item.genres.join(',')}</Text>         <Text><Text style={styles.movieTitle}>制作年份:</Text>{item.year}年</Text>         <Text><Text style={styles.movieTitle}>豆瓣评分:</Text>{item.rating.average}分</Text>       </View>     </View>   </TouchableHighlight> }  // 加载下一页 loadNextPage = () => {   // 如果下一页的页码值,大于总页数了,直接return   if ((this.state.nowPage + 1) > this.state.totalPage) {     return   }

   this.setState({     nowPage: this.state.nowPage + 1   }, function () {     this.getMoviesByPage()   }) }
提升体验

写着写着,遇到了个问题,这时不时的访问不到服务,但是这好像和代码关系不大,因为我重连几次就可以了,我觉得和网速关系很大,我这网速太渣

所以我就加了几句提示错误的代码

123456789101112131415161718
getMoviesByPage = () => {    const start = (this.state.nowPage - 1) * this.state.pageSize    const url = `https://api.douban.com/v2/movie/in_theaters?start=${start}&count=${this.state.pageSize}`    fetch(url)      .then(res => {        if (res.ok) {          return res.json()        } else {          console.error('服务器忙,请稍后重试' + res.status)        }      })      .then(data => {        ...      })      .catch(err => {        console.error(err)      })  }

豆瓣热映电影详情

基本页面
12345678910111213141516171819202122232425
import React, { Component } from 'react'

import { View, Image, Text, ActivityIndicator, ScrollView } from 'react-native'

export default class MovieDetail extends Component {  constructor(props) {    super(props)    this.state = {      movieInfo: {}, // 电影信息      isloading: true    }  }

  componentWillMount() {    ...  }

  render() {    return <View>      ...    </View>  }

 ...}

同样的需要去Main.js配置路由规则,和配置MovieList组件的路由规则一个套路,这里由于我之前配置过了,不再配置

MovieDetilMovieList中的代码片段(就是渲染的每一部电影的代码片段)加上链接跳转,这里依然是要依靠TouchableHighlight组件

首先引入

1
import { View, Image, Text, ActivityIndicator, FlatList, StyleSheet, TouchableHighlight } from 'react-native'

在获取每项电影的时候,代码片段用TouchableHighlight包裹起来

12345678910111213
renderItem = (item) => {    return <TouchableHighlight underlayColor="#fff">      <View style={{ flexDirection: 'row', padding: 10 }}>        <Image source={{ uri: item.images.small }} style={{ width: 100, height: 140, marginRight: 10 }}></Image>        <View style={{ justifyContent: 'space-around' }}>          <Text><Text style={styles.movieTitle}>电影名称:</Text>{item.title}</Text>          <Text><Text style={styles.movieTitle}>电影类型:</Text>{item.genres.join(',')}</Text>          <Text><Text style={styles.movieTitle}>制作年份:</Text>{item.year}年</Text>          <Text><Text style={styles.movieTitle}>豆瓣评分:</Text>{item.rating.average}分</Text>        </View>      </View>    </TouchableHighlight>  }

绑定点击事件,并导入Actions,实现编程式导航

12
// 导入路由的组件import { Actions } from 'react-native-router-flux'
123456
// 渲染每项电影renderItem = (item) => {  return <TouchableHighlight underlayColor="#fff" onPress={() => { Actions.moviedetail({ id: item.id }) }}>   ...  </TouchableHighlight>}
数据渲染

在生命周期是componentWillMount()的时候从接口获取数据,再到this.state同步数据,然后去render()填充数据,这里为render()定义了一个方法,直接this.renderInfo()调用就可以

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
import React, { Component } from 'react'

import { View, Image, Text, ActivityIndicator, ScrollView } from 'react-native'

export default class MovieDetail extends Component {  constructor(props) {    super(props)    this.state = {      movieInfo: {}, // 电影信息      isloading: true    }  }

  componentWillMount() {    fetch('https://api.douban.com/v2/movie/subject/' + this.props.id)      .then(res => res.json())      .then(data => {        this.setState({          movieInfo: data,          isloading: false        })      })  }

  render() {    return <View>      {this.renderInfo()}    </View>  }

  renderInfo = () => {    if (this.state.isloading) {      return <ActivityIndicator size="large"></ActivityIndicator>    }    return <ScrollView>      <View style={{ padding: 4 }}>        <Text style={{ fontSize: 25, textAlign: 'center', marginTop: 20, marginBottom: 20 }}>{this.state.movieInfo.title}</Text>

        <View style={{ alignItems: 'center' }}>          <Image source={{ uri: this.state.movieInfo.images.large }} style={{ width: 200, height: 280 }}></Image>        </View>

        <Text style={{ lineHeight: 30, marginTop: 20 }}>{this.state.movieInfo.summary}</Text>      </View>    </ScrollView>  }}

提示:各种接口数据最好借助Postman工具,先看一下接口数据里哪些属性要用,不然挺乱的,不过不借助Psotman工具的话也可以去该接口连接把json数据拷贝下来到编辑器中,自己整理下格式

小功能体验:拍照功能

Me.js添加一个拍照功能

调用插件react-native-image-picker该插件可以调用摄像头

  1. 安装包
1
yarn add react-native-image-picker
  1. 导入包
123
import { View, Button, Image } from 'react-native'// 导入拍照的包import ImagePicker from 'react-native-image-picker'
  1. 创建拍照时的配置对象
1234567891011121314
var photoOptions = {  //底部弹出框选项  title: '请选择',  cancelButtonTitle: '取消',  takePhotoButtonTitle: '拍照',  chooseFromLibraryButtonTitle: '选择相册',  quality: 0.75, // 照片的质量  allowsEditing: true, // 允许被编辑  noData: false, // 拍照时候不附带日期  storageOptions: { // 存储选项    skipBackup: true, // 在IOS平台中,会自动把 照片同步到 云端的存储,如果此项为 true,表示跳过 备份,不会把照片上传到 云端    path: 'images'  }}
  1. 创建保存数据的容器
123456
constructor(props) {    super(props);    this.state = {      imgURL: 'https://qqqww.com/uploads/avatar.png' // 将来,拍摄的照片路径,会存到这里    }  }
  1. 渲染
123456
render() {    return <View style={{ alignItems: 'center', paddingTop: 70 }}>      <Image source={{ uri: this.state.imgURL }} style={{ width: 200, height: 200, borderRadius: 100 }}></Image>      <Button title="拍照" onPress={this.cameraAction}></Button>    </View>  }
  1. 定义一个拍照方法,在渲染的时候调用
12345678910111213
cameraAction = () => {   ImagePicker.showImagePicker(photoOptions, (response) => {     console.log('response' + response);     if (response.didCancel) { // 点击了取消按钮,此时,用户没有拍照       return     }

     // 用户已经拍摄了一张照片了     this.setState({       imgURL: response.uri     });   }) }

到这一步,在React-Native项目中的拍照功能就完成了,并且手机测试成功

发布安卓项目

说明:这只是用于测试react-native的一部分功能的小demo,并不能用于实际作用

  1. 先保证配置了一个正确的RN环境
  2. 在 cmd 命令行中,运行这一句话keytool -genkey -v -keystore my-release-key2.keystore -alias my-key-alias2 -keyalg RSA -keysize 2048 -validity 10000生成签名
    1. my-release-key.keystore 表示你一会儿要生成的那个 签名文件的 名称【很重要,包找个小本本记下来】
    2. -alias 后面的东西,也很重要,需要找个小本本记下来,这个名称可以根据自己的需求改动my-key-alias
    3. 当运行找个命令的时候,需要输入一系列的参数,找个口令的密码,【一定要找个小本本记下来】
  3. 当生成了签名之后,这个签名,默认保存到了自己的用户目录下C:\Users\liulongbin\my-release-key2.keystore
  4. 将你的签名证书copy到 android/app目录下。
  5. 编辑 android -> gradle.properties文件,在最后,添加如下代码:
1234
MYAPP_RELEASE_STORE_FILE=your keystore filenameMYAPP_RELEASE_KEY_ALIAS=your keystore aliasMYAPP_RELEASE_STORE_PASSWORD=*****MYAPP_RELEASE_KEY_PASSWORD=*****
  1. 编辑 android/app/build.gradle文件添加如下代码:
1234567891011121314151617181920
...android {    ...    defaultConfig { ... }    + signingConfigs {    +    release {    +        storeFile file(MYAPP_RELEASE_STORE_FILE)    +        storePassword MYAPP_RELEASE_STORE_PASSWORD    +        keyAlias MYAPP_RELEASE_KEY_ALIAS    +        keyPassword MYAPP_RELEASE_KEY_PASSWORD    +    }    +}    buildTypes {        release {            ...    +        signingConfig signingConfigs.release        }    }}...
  1. 进入项目根目录下的android文件夹,在当前目录打开终端,然后输入./gradlew assembleRelease开始发布APK的Release版;

  2. 第七步出了点小问题,报错信息是Execution failed for task ‘:app:validateSigningRelease’.后来看看原来是我的第五步里的MYAPP_RELEASE_KEY_ALIAS=your keystore alias这一块的签名写错了,忘了加后缀名,改了之后,又给了我一个响应超时的惊喜,不过我就重新运行了第二次就打包构建成功了

  3. 当发行完毕后,进入自己项目的android\app\build\outputs\apk目录中,找到app-release.apk,这就是我们发布完毕之后的完整安装包;安装到自己和朋友的手机上,测试成功,就可以上传到各大应用商店供用户使用啦。

注意:请记得妥善地保管好你的密钥库文件,不要上传到版本库或者其它的地方。

参考文章:

  • ReactNative之Android打包APK方法(趟坑过程)
  • React Native发布APP之签名打包APK

ReactNative豆瓣电影项目文档相关推荐

  1. ReactNative豆瓣电影项目

    欢迎访问我的博客https://qqqww.com/,祝码农同胞们早日走上人生巅峰,迎娶白富美~~~ 文章目录 1 ReactNative项目环境搭建 2 首页 2.1 如何更改首页 3 组件的学习 ...

  2. README 规范和项目文档规范

    1. README 规范 我们直接通过一个 README 模板,来看一下 README 规范中的内容: # 项目名称<!-- 写一段简短的话描述项目 -->## 功能特性<!-- 描 ...

  3. 【深入浅出项目管理视频1】-项目文档评审

    决定尝试用视频的方式来表达这些繁琐的内容, 比写长篇大论可能更直观些,这是我们产品中实现的项目文档评审过程,与大家分享: 不小心录的视频有些大,得有1280宽的显示器才能看完整,也不知道视频在cnbl ...

  4. github创建项目_用了 GitHub 上这款开源神器后,创建项目文档变得 so easy!

    大家好,我是小 G. 今天跟大家分享一个可用于快速创建项目文档的开源神器:MkDocs. https://github.com/mkdocs/mkdocs 该项目创建自 2014 年,目前 GitHu ...

  5. 使用Docfx生成项目文档

    使用docfx.console生成本项目的文档 使用docfx.console生成其他项目的文档 直接使用docfx.exe生成项目文档 指定配置文档模板 文档地址:http://gitlab.lig ...

  6. 信息系统项目文档及其管理

    信息系统项目文档及其管理 信息系统项目相关信息(文档) 配置管理 配置管理的概念 配置管理的目标和方针 日常配置管理活动 信息系统项目相关信息(文档) 1.软件文档一般分为三类:开发文档.产品文档.管 ...

  7. 【收藏】蘑菇博客mogu_blog项目文档

    项目文档 文档地址:http://moxi159753.gitee.io/mogu_blog_doc 备用文档地址:http://doc.moguit.cn 项目地址 目前项目托管在 Gitee 和  ...

  8. SAP项目文档 清单 考核标准

    SAP项目文档的考核标准 项目启动阶段 项目计划及对计划的调整 建议: 1. 对项目进度进行分类,定义每个阶段的关键任务. 2. 对每个阶段应形成的文档进行说明,哪类文档由谁制作,由谁签核必须做出统一 ...

  9. 知识管理≈内容管理≈文档管理≈项目文档管理

    事情的起因有点偶然,阿杜和我聊起系统的时候,说我们的系统现在开发的还可以,就畅想了一下能否有一个桌面文档管理系统,问起原因,主要是感觉很多人不会管理自己的硬盘,不会管理自己的文档. 我听了也是感觉一振 ...

最新文章

  1. 最新!2020中国高校毕业生薪资报告出炉
  2. LabelImg 批量生成标注图片文件夹序号(起始值+终值)
  3. 转:UNITY,如何为你的游戏选择正确的网络类型
  4. 模板方法(钩子函数)设计模式
  5. 感谢有你 | LiveVideoStackCon 2020 北京站优秀出品人、讲师与志愿者
  6. linux下vim编辑器快速掌握方法
  7. 你看到过哪些简短有内涵的一看就想发朋友圈的句子?
  8. python处理音频的库_Python中音频处理库pydub的使用教程
  9. EIGRP路由协议基本设置
  10. shell与shell脚本
  11. 动态规划实战篇--斐波那契数列
  12. Python_随机生成11位手机号
  13. 基于布谷鸟灰狼算法、灰狼算法求解复杂地形下三维无人机路径规划问题研究附matlab代码
  14. Clearcase no version selected issue
  15. 微信小程序—智能停车
  16. 三星电视与计算机连接网络设置,三星电视怎么连接有线网络?五步搞定网络设置...
  17. 《SRE Google运维解密》散文
  18. [ERROR] [MY-012576] [InnoDB] Unable to create temporary file; errno: 30
  19. DNS图解(秒懂 + 史上最全)
  20. 手把手教你搭建Nightingale夜莺监控系统

热门文章

  1. 昂达vi40精英版刷Linux,昂达vi40旗舰版刷机教程【图解】
  2. 【大数据入门核心技术-HBase】(七)HBase Python API 操作
  3. Selenium爬取MOOC网课程信息
  4. Alta 429板卡在安装厂家自带的驱动后,没法运行自带的例子的解决方法
  5. 【Android 组件化】为什么能极大提高工程编译速度?
  6. mbp安装steam显示“steam_osx”已损坏,mac安装steam方法
  7. java 聊天室开源_用java WebSocket做一个聊天室
  8. freebsd 启用网关
  9. Park变换输入输出前后都是交流量的问题及解决办法
  10. MDK-keil安装与使用