# ReactNative

# 移动端开发

# App的分类

大致可以分为这3种:

  • native app(原生app : iOS或安卓)
  • web app (APIclound)
  • hybrid app(混合app)

# 各类开发方式的APP介绍

原生应用程序(NativeAPP)

原生(Native)应用程序是某一个移动平台(比如iOS或安卓)所特有的,使用相应平台支持的开发工具和语言(比如iOS平台支持Xcode和Objective-C,Swift,安卓平台支持Eclipse和Java)。原生应用程序外观和运行起来(性能)是最佳的。

H5应用程序(H5APP/WEBAPP)

HTML5应用程序使用标准的Web技术,通常是HTML5、JavaScript和CSS。这种只编写一次、可到处运行的移动开发方法构建的跨平台移动应用程序可以在多个设备上运行。虽然开发人员单单使用HTML5和JavaScript就能构建功能复杂的应用程序,但截至本文截稿时仍然存在一些重大的局限性,具体包括会话管理、安全离线存储以及访问原生设备功能(摄像头、日历和地理位置等)。

混合应用程序(HybridAPP)

混合(Hybrid)应用程序让开发人员可以把HTML5应用程序嵌入到一个细薄的原生容器里面,集原生应用程序和HTML5应用程序的优点(及缺点)于一体。

# 各类app的优缺点

native app 优点:

  • 提供最佳用户体验,最优质的用户界面,流畅的交互
  • 可以访问本地资源
  • 可以调用移动硬件设备,比如摄像头、麦克风等

缺点:

  • 开发成本高。每种移动操作系统都需要独立的开发项目,针对不同平台提供不同体验;
  • 发布新版本慢。下载是用户控制的,很多用户不愿意下载更新(比如说,版本发布到了3.0,但还是有很多1.0的用户,你可能就得继续维护1.0版本的API)
  • 应用商店发布审核周期长。安卓平台大概要1~3天,而iOS平台需要的时间更长

web app

优点:

  • 整体量级轻,开发成本低
  • 不需要用户进行手动更新,由应用开发者直接在后台更新,推送到用户面前的都是全新版本,更便于业务的开展
  • 基于浏览器,可以跨平台使用

缺点:

  • 资源的都在远程服务器。在网速受到限制时,交互效果也会受到限制。页面跳转费力,不稳定感更强。
  • 无法操作很多手机原生设备,摄像头,麦克风,不支持多点触控等。

Hybrid app 这类app集合了上面两种app各自的优势:

  • 在实现更多功能的前提下,使得app安装包不至于过大

  • 在应用内部打开web网页,省去了跳转浏览器的麻烦

  • 主要功能区相对稳定下,部分功能区采用web 形式,使得迭代更加方便

  • 融合了原生APP和WebApp优点(速度快,跨平台)

# ReactNative简介

React Native使你只使用JavaScript也能编写原生移动应用。 它在设计原理上和React一致,通过声明式的组件机制来搭建丰富多彩的用户界面。

React Native最终产品很贴近移动应用,从使用感受上和用Objective-C或Java编写的应用相比几乎是无法区分的。

React Native所使用的基础UI组件和原生应用完全一致。 你要做的就是把这些基础组件使用JavaScript和React的方式组合起来。

# 搭建RN开发环境

RN开发环境对版本要求极高,所有环境严格按照指定版本来安装,版本不一致会导致无法正常运行

各个环境的搭建遵循的三步曲:

1 安装环境 ====> 2、配置环境变量 ====> 3、检测使用

  1. 安装python 2.x版本

  2. 安装Java jdk 1.8版本

  3. Android环境安装

    检测使用 adb devices

# ReactNative脚手架的创建

确保Node是v12.10.0版本 (nvm use v12.10.0)

nvm install v12.10.0
nvm use v12.10.0
npm install -g yarn

Yarn设置镜像源:

yarn config set registry https://registry.npm.taobao.org --global 

yarn config set disturl https://npm.taobao.org/dist --global

r无法使用cnpm安装依赖。

需要使用facebook团队提供的yarn包管理工具:

yarn global add react-native-cli 

检测是否安装成功:

react-native -v

创建ReactNative

react-native init  项目名  --version 0.55.4 

要求必须跟上版本号

启动手机模拟器

# Android 模拟器安装 (夜神模拟器)

  1. adb connect 127.0.0.1:62001

  2. adb devices 查看设备是不是添加上

  3. 抖动 : a. Dev Settings - > Debug server host & port for device

    ​ b. 172.17.100.2:8081 //本地ipv4 ipconfig

  4. 重启夜神

  5. 正常启动顺序

    1. adb.exe connect 127.0.0.1:62001
      
    2. react-native run-android
      

# 自定义组件

./components 创建文件夹

./components/App1.js

使用RN基本组件的时候,需要先进行引入:

import {View, Text, StyleSheet} from 'react-native'

执行源码

// 引入核心库
import React, { Component } from 'react'
import {View, Text, StyleSheet} from 'react-native'

// 创建并到处类(组件)
export default class App1 extends Component {
    render() {
        return (
            <View style={styles.box1}>
                <Text style={styles.text1}>hello,你好!</Text>
            </View>
        )
    }
}

// 创建样式
const styles = StyleSheet.create({
    box1:{
        backgroundColor: 'pink'
    },
    text1:{
        fontSize:20,
        fontFamily:"宋体"
    }
})

index.js中,修改引入组件的路径

import { AppRegistry } from 'react-native';
import App from './components/App1';

AppRegistry.registerComponent('rn_demo', () => App);

# 内置组件

# View 组件

超出View组件的内容是不会显示的。

示例代码

// 1 自定义组件

// 引入核心模块
import React, { Component } from 'react'
import { View,Text ,StyleSheet} from 'react-native'

// 创建并导出组件
export default class App1 extends Component{ 
  render() { 
    return (
      <View style={styles.container}>
        <Text style={styles.txt1}> Hello ReactNative!</Text>
      </View>
    )
  }

}

// 样式代码
const styles = StyleSheet.create({
  txt1: {
    color:"red"
  },
  container: {
    backgroundColor:"#ccc"
  }
})

# ScrollView组件

# 初识

App2.js/新增

View 超出宽高元素不可见 ScrollView进行滚动条显示

<View style={styles.box1}>
    {/* 配合ScrollView组件,实现溢出可以出现滚动条 
    如果需要出现横向滚动条,需要给ScrollView组件加上 horizontal
    */}
    <ScrollView horizontal={true}>
    <Text style={styles.text1}>hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!hello,你好!</Text>    
    </ScrollView>
</View>

【注】 默认不写参数 ScrollView 组件自带竖线滚动条

horizontal={true} 出现横线滚动条

# ScrollView的使用

<ScrollView style={styles.box}
	horizontal={true}		//水平排列
	pagingEnabled={true}	//滚动条倍数滚动
	showsHorizontalScrollIndicator={false} 	//不显示横向滚动条(用小圆点代替)
>
	{/* 多屏展示 */}
	<View><Text>第1屏</Text></View>
	<View><Text>第2屏</Text></View>
</ScrollView>

执行源码:

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

export default class App5 extends Component {
    render() {
        return (
            <View >
                <ScrollView horizontal={true} pagingEnabled={true} showsHorizontalScrollIndicator={false}>
                    <View style={[styles.itemBox,styles.itemBox1]}>
                        <Text>第1屏</Text>
                    </View>
                    <View style={[styles.itemBox,styles.itemBox2]}>
                        <Text>第2屏</Text>
                    </View>
                    <View style={[styles.itemBox,styles.itemBox3]}>
                        <Text>第3屏</Text>
                    </View>
                </ScrollView>
                
            </View>
        )
    }
}
const styles = StyleSheet.create({
    itemBox:{
        width:360,
        height:100
    },
    itemBox1:{
        backgroundColor:"#fcf"
    },
    itemBox2:{
        backgroundColor:"#ccc"
    },
    itemBox3:{
        backgroundColor:"#ffc"
    }
})

horizontal={true} //水平排列 pagingEnabled={true} //滚动条倍数滚动 showsHorizontalScrollIndicator={false} //是否显示滚动条

# Button组件

[更多>>](展示按钮使用的是 Bttton组件,https://reactnative.cn/docs/button)

<Button style={styles.btn} title="点我!" color="pink" onPress={this.handlePress}></Button>
属性 作用
title 按钮上的展示文字
color 按钮的背景颜色(安卓),或者文本颜色(IOS)
onPress 按压事件(即点击事件)

执行源码

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

export default class App7 extends Component {

    constructor(props){
        super(props)
        this.state = {
            num:89
        }
        this.handlePress = this.handlePress.bind(this)
    }

    handlePress(){
        this.setState({
            num:this.state.num+1
        })
    }

    render() {
        return (
            <View>
                <Text>{this.state.num}</Text>
                <Button style={styles.btn} title="点我!" color="pink" onPress={this.handlePress}></Button>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    btn:{
        width:100
    }
})

# 自定义按钮组件

因为Button样式本身有局限性。TouchableOpacity组件可以自由定义组件样式。

import React, { Component } from 'react'
import {View, Text, Button, StyleSheet, TouchableOpacity} from 'react-native'

export default class App7 extends Component {

    constructor(props){
        super(props)
        this.state = {
            num:89
        }
        this.handlePress = this.handlePress.bind(this)
    }

    handlePress(){
        this.setState({
            num:this.state.num+1
        })
    }

    render() {
        return (
            <View>
                <TouchableOpacity style={styles.myBtn} onPress={this.handlePress}>
                    <Text>自定义按钮</Text>
                </TouchableOpacity>
                <Text>{this.state.num}</Text>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    myBtn:{
        width:100,
        height:100,
        backgroundColor:"pink",
        borderRadius:50,
        justifyContent:"center",
        alignItems:"center"
    }
})

# Image组件

引入方式一

import ImgUrl from '../res/banner1.png'
<Image source={ImgUrl}/>

引入方式二

<Image source={require('../res/logo.png')}/>

引入方式三

<Image source={{ uri:"https://www.baidu.com/img/baidu_resultlogo@2.png" }}  style={styles.st2}/> 

[注意]:这种引入方式需要给图片添加宽高样式,才能看得见图片

# 图片的另一种引入方式

\android\app\src\main\res\ 目录下

新建drawable文件夹,存放资源图片。

<Image source={{ uri:"图片名" } }  style={styles.st1} /> 

引入的是app内部资源图片

注意:

  1. uri后面直接书写图片名称,不需要后缀名,同样需要设置宽高样式

  2. 重新编译应用(重新执行react-native run android)

重新编译引用可能会报错: java.io.IOException 的问题, 此时只要把 android目录下的缓存清理掉即可,清理缓存的命令如下:

cd android

gradlew clean    (执行之前建议先在模拟器中关掉项目,否则有可能报错)

再退回项目根目录
cd ..


重新执行react-native run-android

img 将来会打包到app

# WebView组件

WebView组件相当于在app内部内嵌一个浏览器,可以通过它的source属性中的uri,

写入对应网页地址,从而展示一个页面。

执行源码

import React, { Component } from 'react'
import {WebView} from 'react-native'

export default class App10 extends Component {
    render() {
        return (
            <WebView source={{ uri: "https://m.jd.com/"}} ></WebView>    
        )
    }
}

# FlatList组件

组件使用


<View >
    <FlatList
        numColumns={1}		//每行显示个数
        data={ this.state.arr_data }	//数据源
        renderItem={ this.renderData }	//渲染函数
        />    
</View>

... ...

renderData({item}){  // item 就是数组数据中的每一项对象
    return (
        <View >
            <Text>{item.id}{item.data}</Text>    
        </View> 
    ) 
}

执行源码

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

const arr_data=[
    {
        id:1,
        data:10
    },
    {
        id:2,
        data:20
    },
    {
        id:3,
        data:30
    },
    {
        id:4,
        data:40
    },
]

export default class App1 extends Component {
    constructor(props){
        super(props)

        this.state = {
            arr_data
        }
    }
    renderData({item}){  // item 就是数组数据中的每一项对象
        return (
            <View >
                <Text>{item.id}{item.data}</Text>    
            </View> 
        ) 
    }
    render() {
        return (
            <View >
                <FlatList
                    numColumns={1}		//每行显示个数
                    data={ this.state.arr_data }	//数据源
                    renderItem={ this.renderData }	//渲染函数
                />    
            </View>
        )
    }
}

# FlatList上拉刷新

  1. 准备isLoading 数据源

    .......
    const CITY_NAMES = ["ItemContent01","ItemContent02","ItemContent03","ItemContent04","ItemContent05","ItemContent06"]
        export default class FlatListDemo extends Component {
          constructor(props) { 
            super(props)
            this.state = {
              isLoading:false,
              CITY_NAMES
            }
          }
    
    	......
    }
    
  2. FlatList

    <FlatList 
        data = {this.state.dataArr}
        renderItem = {this.renderItem}
    
        refreshing={this.state.isLoading}  //  是否正在加载数据
        onRefresh = {this.loadData.bind(this)} //  刷新的时候执行这个方法
    />
    
  3. 模拟刷新数据

    loadData(){
        this.setState({
            isLoading:true,
        })
    
        //模拟异步请求
        setTimeout(()=>{
            let data = CITY_NAMES.reverse()  //把数组反转,模拟新数据的获取
            this.setState({
                isLoading:false,
                dataArr: data
            })
        },500)
    }
    

执行源码

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

const CITY_NAMES = ["ItemContent01","ItemContent02","ItemContent03","ItemContent04","ItemContent05","ItemContent06"]

export default class FlatListDemo extends Component {
  constructor(props) { 
    super(props)
    this.state = {
      isLoading:false,
      CITY_NAMES
    }
  }

 //渲染Item
  renderData({ item }) { 
    
    return(
      <View style={styles.itemBox}>
        <Text style={ styles.itemTxt}>{item}</Text>
      </View>
    )
  }
 //加载Data
  loadData() { 
    // 修改 isLoading 值为 true
    this.setState({ 
      isLoading:true
    })
    // 模拟异步请求
    setTimeout(() => { 
      let NewData = CITY_NAMES.reverse()  //数组反转模拟新数据
      this.setState({
        CITY_NAMES: NewData,  //上拉刷新
        isLoading:false
      })
    }, 1000)
    
  
  }
  
  render() {
    return (
      <View>
        <FlatList
          // numColumns=
          data={this.state.CITY_NAMES}
          renderItem={this.renderData}
          refreshing={this.state.isLoading}//是否加载数据
          onRefresh={this.loadData.bind(this)}
        />
      </View>
    )
  }
}

//盒子样式
const styles = StyleSheet.create({
  itemBox: {
    width: 300,
    height: 200,
    backgroundColor: "#cfc",
    marginBottom: 15,

  }, itemTxt: {
    textAlign: "center",
    lineHeight:200
  }
})

# 滑动底部加载更多

  1. FlatList 组件使用

    <FlatList 
        data = {this.state.dataArr}
        renderItem = {this.renderItem}
        ListFooterComponent={this.genIndicator}  //一、定义尾部组件,用来书写加载的符号
        onEndReached={this.loadData.bind(this)} //三、到达底部调用加载数据的函数
    />
    
  2. 修改加载符号

    import {  ... ...,
        ActivityIndicator
    } from 'react-native';
    
    genIndicator(){ 
        return 
        <View>
            <ActivityIndicator 
                size="large"
                animating={true}
                color="red"
                />
            <Text style={{ textAlign:"center", flex:1, marginBottom:10}}>正在加载数据...</Text>
        </View>
    }
    
  3. 修改数据源

    loadData(){
        this.setState({
            isLoading:true,
        })
    
        //模拟异步请求
        setTimeout(()=>{
            let data = this.state.dataArr.concat(CITY_NAMES)  //合并数组,模拟加载新数据
            this.setState({
                isLoading:false,
                dataArr: data
            })
    
        },2000)
    }
    

执行源码

// 下拉刷新
import React, { Component } from 'react'
import { ActivityIndicator } from 'react-native';
import {
    Text,
    View,
    FlatList,
    StyleSheet
  } from 'react-native';

const CITY_NAMES = ["北京市","上海市","深圳市","广州市","成都","杭州"]

export default class FlatListDemo02 extends Component {

    //1 准备isLoading和数据源
    constructor(props){
        super(props)

        this.state = {
            isLoading:false,
            dataArr: CITY_NAMES
        }
    }

    renderItem({item}){
        return(
            <View style={styles.itemBox}>
                <Text style={styles.itemBoxTxt}>{item}</Text>
            </View>
        )
    }
    genIndicator() {  //---------------- 加载 符号样式添加
        return(
            <View>
                <ActivityIndicator
                    size="large"
                    animating={true}
                    color="red"
                />
                <Text style={{textAlign:"center",flex:1, marginBottom:10}}>正在加数据...</Text>
            </View>
        )
    }

    //3 修改数据源
    loadData(){
        this.setState({
            isLoading:true,
        })

        //模拟异步请求
        setTimeout(()=>{
            //------------- concat 连接后端请求数据源
            let data = this.state.dataArr.concat(CITY_NAMES) //新增数据
            this.setState({
                isLoading:false,
                dataArr: data
            })
            
        },2000)
    }
    //------------------------------------ 新增
    render() {
        return (
            <View>
                //修改 FlatList属性  并添加 组件样式
                <FlatList 
                    
                    data={this.state.dataArr} //数据源
                    renderItem={this.renderItem}  //渲染列表项
                    ListFooterComponent={this.genIndicator}	
                    //一、定义尾部组件,用来书写加载符号
                    onEndReached={this.loadData.bind(this)}
                    //三 到达底部调用加载数据的函数
                />
            </View>
        )
    }
}
//定义 盒子样式
const styles = StyleSheet.create({
    itemBox:{
        width:320,
        height:200,
        backgroundColor:"#ccc",
        marginLeft:20,
        marginBottom:20
    },
    itemBoxTxt:{
        textAlign:"center",
        lineHeight:200
    }
})

# TextInput 组件

TextInput 文本输入 组件

import React, { Component } from 'react'
import {View, StyleSheet, TextInput} from 'react-native'

export default class App3 extends Component {
    render() {
        return (
            <View>
            <TextInput 
            style={styles.txt}
    placeholder="请输入..."     // 占位符设置(提示文本)
placeholderTextColor="#ccc"    //   占位符颜色设置
// maxLength = {6}         //  可以输入的最大长度
// underlineColorAndroid='transparent'   // 底线设置透明
// secureTextEntry = {true}    //安卓下的密码设置
/>
    </View>
)
}
}
const styles = StyleSheet.create({
    txt:{
        borderWidth:1,
        // height:40,
    }
})

# 第三方组件

# react-navigation

React Navigation (opens new window)

tabnavigator 使用>> (opens new window)

项目目录下安装React Navigation:

yarn add react-navigation@2.18.1 -D

在导航页面Nav.js中:

TabNavigartor

import {TabNavigator} from 'react-navigation'

import Food from '../food/Food'
import Movie from '../movie/Movie'
import Hotel from '../hotel/Hotel'
import Bank from '../bank/Bank'



export default  MyApp = TabNavigator({
    Food: {
        screen: Food,   //food为组件
    },
    Movie: {
        screen: Movie,
    },
    Hotel: {
        screen: Hotel,
    },
    Bank: {
        screen: Bank,
    },
},{
    tabBarPosition: 'bottom',
    animationEnabled: false,
    tabBarOptions: {
        activeTintColor: globalStyle.baseColor,  //选中时候的颜色
        inactiveTintColor: '#000000', //未选中颜色
        showIcon: true,
        style: {
            backgroundColor: '#eee',
            height: 60
          },
        indicatorStyle: { //下面一条线的样式
            height: 0
        }
    },
})

# stackNavigator

  • 底部导航栏的时候,使用的是ReactNavigation的TabNavigator 组件。

  • 实现 跳转 导航

  • ReactNavigation 两个核心组件:StackNavigatorDrawerNavigator

React Navigation官网>> (opens new window)

StackNavigator 的使用>> (opens new window)

我们可以使用StackNavigator 实现界面的转场

# StackNavigator 基本使用

先定义视图所对应的组件:

具体代码:

Home.js中:

import React, { Component } from 'react'
import { View ,Button} from 'react-native' 
import { StackNavigator } from 'react-navigation'
// 导入组件
import Page1 from './pages/page1'

class Home extends Component {
  render() {
    return (
      <View>
        <Button onPress={() =>this.props.navigation.navigate("Page1") } title="点击跳转 page1" />
      </View>
    )
  }
}


export default StackNavigator({
    Home: {
      screen: Home,
    },
    Page1: {
      screen: Page1,
    },
  }, {
    headerMode:"none"  //隐藏顶部导航栏
})


Page.js中:

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

// import {navigation} from 'react-navigation'
export default class Page1 extends Component {
    render() {
        return (
            <View>
                <Text>欢迎来到page1</Text>
            </View>
        )
    }
}

onPress事件来实现转场效果:

onPress={() => this.props.navigation.navigate('Page1')}

# StackNavigator 携带参数的跳转

Home.js

import React, { Component } from 'react'
import { View ,Button} from 'react-native' 
import { StackNavigator } from 'react-navigation'
// 导入组件  
import Page1 from './pages/page1'
import Page2 from './pages/page2'

class Home extends Component {
  render() {
    return (
      <View>
        <Button onPress={() => this.props.navigation.navigate("Page1") } title="点击跳转 page1" />
         //带参传递参数  
        <Button onPress={() => this.props.navigation.navigate("Page2",{name:"wolfcode"}) } title="点击跳转 page2" />
      </View>
    )
  }
}

export default StackNavigator({
    Home: {
      screen: Home,
    },
    Page1: {
      screen: Page1,
  }, Page2: {
      //此处使用声明 传递参数
      path:"page2/:name",
      screen: Page2,
    },
  }, {
    headerMode:"none"  //隐藏顶部导航栏
})

Page2.js

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

export default class Page2 extends Component {
  render() {
    const { navigation } = this.props
    //解构获取参数
    return (
      <View>
        <Text> 欢迎来到Page2 </Text>
        <Text> 接收到参数为: { navigation.state.params.name} </Text>
        <Button title="返回Home页面" onPress={() => { navigation.goBack()}}/>
      </View>
    )
  }
}

# StackNavigator 一些样式的配置

Home.js

import React, { Component } from 'react'
import { View ,Button} from 'react-native' 
import { StackNavigator } from 'react-navigation'
// 导入组件
import Page1 from './pages/page1'
import Page2 from './pages/page2'

class Home extends Component {
  render() {
    return (
      <View>
        <Button onPress={() => this.props.navigation.navigate("Page1") } title="点击跳转 page1" />
        <Button onPress={() => this.props.navigation.navigate("Page2",{name:"wolfcode"}) } title="点击跳转 page2" />
      </View>
    )
  }
}

export default StackNavigator({
    Home: {
      screen: Home,
      //添加 参数
      navigationOptions: {
        title:"Home 界面"
      }
    },
    Page1: {
      screen: Page1,
  }, Page2: {
      path:"page2/:name",
      screen: Page2,
      navigationOptions: {
        // header:null
        title:"Page2",
        headerStyle: {
          backgroundColor: "#fcf",
          height:40,
        },
        
      }
    },
}, {
    //全局的界面标题
    navigationOptions: {
    title:"默认界面标题",
    headerTitleStyle: {
      color:"#ccc"
    }
  }
})

# DrawerNavigator 基本使用

DrawerNavigator的使用>> (opens new window)

DrawerNavigator组件可以用来制作抽屉类型的导航,

也可以用来制作自定义内容的抽屉。

点击函数中通过:this.props.navigation.openDrawer() 打开抽屉

import React, { Component } from 'react'
import {
    Button,
  } from 'react-native';
import {DrawerNavigator} from 'react-navigation'
import Page1 from './pages/page1';
import Swiper from './Swiper';
class MyHome extends Component {


    // navigation.openDrawer()  打开抽屉
    render() {
        return (
            <Button
                onPress={() => this.props.navigation.openDrawer()}
                title="点我打开抽屉"
            />
        );
    }
}

export default MyApp = DrawerNavigator({

    //这里定义的页面就会出现在抽屉当中
    Home: {
      screen: MyHome,
      //此处必须定义当前组件为跟组件
    },
    Page1: {
      screen:Page1
    },
    Swiper: {
      screen:Swiper
    },
    
});

# DrawerNavigator核心用法

import React, { Component } from 'react'
import {
    Text,
    View,
    Button,
  } from 'react-native';
import {DrawerNavigator} from 'react-navigation'

class MyHome extends Component {


    // navigation.openDrawer()
    render() {
        return (
            <Button
                onPress={() => this.props.navigation.openDrawer()}
                title="点我打开抽屉"
            />
        );
    }
}
  
class Page1 extends Component {
  
    render() {
        return (
            <View>
                <Text>Page1</Text>
                <Button onPress={() => this.props.navigation.goBack()} title="点我返回"></Button>
            </View>
            
        );
    }
}

export default MyApp = DrawerNavigator({

    //这里定义的页面就会出现在抽屉当中
    Home: {
      screen: MyHome,
    },
    P1: {
      screen: Page1,
    },
});

# DrawerNavigator配置 自定义抽屉内容

import React, { Component } from 'react'
import {
    Text,
    View,
    Button,
    ScrollView,
  } from 'react-native';//引入配置项================================= 
import {DrawerNavigator, DrawerItems, SafeAreaView} from 'react-navigation'

class MyHome extends Component {


    // navigation.openDrawer()
    render() {
        return (
            <Button
                onPress={() => this.props.navigation.openDrawer()}
                title="点我打开抽屉"
            />
        );
    }
}
  
class Page1 extends Component {
  
    render() {
        return (
            <View>
                <Text>Page1</Text>
                <Button onPress={() => this.props.navigation.goBack()} title="点我返回"></Button>
            </View>
            
        );
    }
}
//主要配置代码========================= 
export default MyApp = DrawerNavigator({

    //这里定义的页面就会出现在抽屉当中
    Home: {
      screen: MyHome,
    },
    P1: {
      screen: Page1,
    //   navigationOptions:{
    //     title:"P1页面"
    //   }
    },
  },{
    drawerWidth:200,    //设置抽屉的宽度
    drawerPosition:"right",   //设置抽屉的方向
    
    // 以下contentComponent属性可以自定义抽屉内容
    contentComponent:(props) => (
        <ScrollView style={{backgroundColor:'#ccc',flex:1}}>
            <SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}>
                <DrawerItems {...props} />
            </SafeAreaView>
            {/* <Text>设置其他组件</Text> */}
        </ScrollView>
    )
  });

# 自定义抽屉传递参数到页面

// 从自定义抽屉中传递参数到页面
import React, { Component } from 'react'
import {
    Text,
    View,
    Button,
    ScrollView
  } from 'react-native';
import {DrawerNavigator} from 'react-navigation'

class MyHome extends Component {


    // navigation.openDrawer()
    render() {
        return (
            <Button
                onPress={() => this.props.navigation.openDrawer()}
                title="点我打开抽屉"
            />
        );
    }
}
  
class Page1 extends Component {
  
    render() {
        return (
            <View>
                <Text>Page1</Text>
                {/* 接收从抽屉中传来的参数name */}
                <Text>{this.props.navigation.state.params.name}</Text> 
                <Button onPress={() => this.props.navigation.goBack()} title="点我返回"></Button>
            </View>
            
        );
    }
}

export default MyApp = DrawerNavigator({

    //这里定义的页面就会出现在抽屉当中
    Home: {
        screen: MyHome,
    },
    P1: {
        screen: Page1,
    },
},{
    contentComponent:(props) => (
        //自定义抽屉组件 
        <ScrollView style={{backgroundColor:'#ccc',flex:1}}>
            
            {/* 在抽屉中点击按钮返回 P1几面,并传递了参数*/}
            <Button
                onPress={() => props.navigation.navigate("P1",{name:"wolfcode"})}
                title="点我回到P1"
            />
        </ScrollView>
    )
});

# react-native-swiper

react-native-swiper 实现轮播图

安装:npm i react-native-swiper --save

查看:npm view react-native-swiper 删除:npm rm react-native-swiper --save

swiper的一些属性

属性 默认值 类型 描述
horizontal true bool 如果只true时,那么滚动的内容 将是横向排列 反之 垂直于列
loop true bool 设置为 false 滑动到最后一张时 再次滑动将不会展示第一张图片
index 0 number 初始进入页面标识为0 的页面
showButtons false bool 设置为true 那么就可以使控制按钮 (即:左右两侧的箭头)可见
autoplay false bool 设置为 true 则页面可以自动跳转
onIndexCHanged (index) => null func 当用户拖拽时更新索引调用
autoplayTimeout 2.5 number 设置轮播间隙
autoplauyDirection ture bool 控制轮播图是否循环

索引指示器

属性 默认值 类型 描述
showsPagination true bool 在下边显示圆点
paginationStyle {...} style 设置页面圆点样式 自定义的样式和默认样式进行合并
renderPagination - function 通过三个参数(index,total,context)去渲染如何分页
dot <View style={\{backgroundColor:'#ccc', width: 8, height: 8,borderRadius: 4, marginLeft: 3, marginRight: 3, marginTop: 3, marginBottom: 3,}} /> element 允许自定义点元素样式
activeDot <View style={\{backgroundColor: 'yellow', width: 8, height: 8, borderRadius: 4, marginLeft: 3, marginRight: 3, marginTop: 3, marginBottom: 3}} /> element 允许自定义当前选择点元素样式
dotStyle {{}} object 允许自定义当前选择点元素样式

基本代码

// react-native-swiper组件的使用,轮播图
import React, { Component } from 'react'
import {
    StyleSheet,
    Text,
    View,
    Image,
    Dimensions
  } from 'react-native';
  import Swiper from 'react-native-swiper';
  const {width} = Dimensions.get('window'); 
  export default class App extends Component{
    render() {
      return (
          <View style={styles.container}>
              {/*设置horizontal为竖向排列 autoplay为自动播放*/}
               <Swiper style={styles.wrapper} horizontal={true} autoplay autoplayTimeout={1} >
                    <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>
          </View>
      );
    }
  }

  const styles = StyleSheet.create({
    container: {
        height:200
    },

    wrapper: {
    },

    slide: {
        flex: 1,
        justifyContent: 'center',
        backgroundColor: 'transparent'
    },

    slide1: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#9DD6EB'
    },
    slide2: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#fcc'
    },
    slide3: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#cff'
    },
    text: {
        color: '#fff',
        fontSize: 30,
        fontWeight: 'bold'
    }
});

【注意】 移动端 选中状态 切换 屏页 无法点击 选中也无法实现暂停

完整代码

// 实现自定义 轮播图
import React, { Component } from 'react'
import { View, StyleSheet, Text, Dimensions,Image } from 'react-native'
const { width } = Dimensions.get("window");
import Swiper from 'react-native-swiper'

export default class swiper extends Component {
  render() {
    return (
      <View style={styles.container}>
        {/*设置horizontal为竖向排列 autoplay为自动播放*/}
        <Swiper
          style={styles.wrapper}
          horizontal={true}
          autoplay
          autoplayTimeout={2}
          dot={<View style={{backgroundColor:'#ccc', width: 8, height: 8,borderRadius: 4, marginLeft: 3, marginRight: 3, marginTop: 3, marginBottom: 3,}} />}
          activeDot={<View style={{backgroundColor: 'yellow', width: 8, height: 8, borderRadius: 4, marginLeft: 3, marginRight: 3, marginTop: 3, marginBottom: 3}} />}
          paginationStyle={{
              bottom: 5, left: null, right: 10
          }}
          loop
        >
          <View style={styles.slide1}>
            <Image source={require('../res/banner1.png')} style={{ width, height: 201 }}></Image>
            <View style={styles.titleBox}>
              <Text style={styles.titleTxt}>前端让IT互联有魅力</Text>
            </View>
          </View>
          <View style={styles.slide2}>
            <Image source={require('../res/banner2.png')} style={{ width, height: 201 }}></Image>
            <View style={styles.titleBox}>
              <Text style={styles.titleTxt}>Java薪未来</Text>
            </View>
          </View>
          <View style={styles.slide3}>
            <Image source={require('../res/banner3.png')} style={{ width, height: 201 }}></Image>
            <View style={styles.titleBox}>
              <Text style={styles.titleTxt}>全栈UI设计</Text>
            </View>
          </View>
        </Swiper>

      </View>
    )
  }
}
const styles = StyleSheet.create({
  container: {
    height: 200
  },
  wrapper: {
  },

  slide: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: 'transparent'
  },

  slide1: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#9DD6EB'
  },
  slide2: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fcc'
  },
  slide3: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#cff'
  },

//------ 替换的 轮播图主体内容
  itemBox:{
    width,
    overflow:"hidden"
  },
  titleBox:{
      position:"absolute",
      bottom:0,
      backgroundColor:"rgba(0, 0, 0, 0.3)",
      height:30,
      width
  },
  titleTxt:{
      color:"#fff",
      lineHeight:30,
      marginLeft:10
  }
})

# Flex布局

Flexbox 可以在不同屏幕尺寸上提供一致的布局结构。

一般来说,使用flexDirectionalignItemsjustifyContent三个样式属性就已经能满足大多数布局需求。

# display属性

display 设置此组件的显示类型。它的值只支持'flex'和'none'。'flex'是默认值。

# flex属性

在React Native 中,flex的值是一个正数,它的大小将与其弹性值成比例。

# flexDirection属性

  1. flexDirection控制主轴的方向(父容器属性)

  2. flexDirection的4个值:'row','row-reverse','column','column-reverse'

  • 'row' 设置主轴为水平正向 123

  • 'row-reverse' 设置主轴为水平反向 321

  • 'column' 设置主轴为垂直正向 123

  • 'column-reverse' 设置主轴为垂直反向 321

示例代码

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

export default class App5 extends Component {
  render() {
    return (
      <View style={styles.container1}>
        <View><Text style={styles.box1}>Item1</Text></View>
        <View><Text style={styles.box2}>Item2</Text></View>
        <View><Text style={styles.box3}>Item3</Text></View>
        <View><Text style={styles.box4}>Item4</Text></View>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container1: {
    display: "flex",
    flex:1,
    // flexDirection的4个值:'row','row-reverse','column','column-reverse'
    flexDirection: "column-reverse",
  },

  box1: {
    width: 100,
    height: 100,
    backgroundColor:"#ccc"
  },
  ....
  box4: {
    width: 100,
    height: 100,
    backgroundColor:"#cff"
  },

})

# justifyContent属性

  1. justifyContent控制主轴的对齐方式(父容器属性)

  2. justifyContent的5个值:'flex-start','flex-end','center','space-between','space-around'

  • 'flex-start': 左(或顶)对齐 默认值

  • 'flex-end' 右(或底)对齐

  • 'center' 居中对齐

  • 'space-between' 中间留白

  • 'space-around' 中间及两侧留白

const styles = StyleSheet.create({
    container:{
        flexDirection:'row',
        flex:1,
        justifyContent:'flex-start'
    },
    ...
})

# alignItems属性

  1. alignItems属性控制侧轴对其方式{竖直方向排列方向}

  2. alignItems属性的5个值: 'flex-start','flex-end','center','stretch','baseline'

  • 'flex-start': 左(或顶)对齐 默认值

  • 'flex-end' 右(或底)对齐

  • 'center' 居中对齐

  • 'stretch' 设置侧轴拉伸,此时侧轴方向(宽或者高)不要给固定数值,才能看得见拉伸效果

  • 'baseline' 设置基线对齐,文字底部对齐

# 屏幕宽高及屏幕像素比

更多>> (opens new window)

RN中通过Dimensions组件来获取屏幕宽高

示例代码:

// width宽度逻辑像素
// height高度逻辑像素  对于不同像素的屏幕,屏幕逻辑像素不一样
// scale缩放比
import React, { Component } from 'react'
import { View, Text, Dimensions } from 'react-native'

const { width, height,scale} = Dimensions.get("window")

export default class App3 extends Component {
  render() {
    return (
      <View>
        <Text> 屏幕宽度是: {width} </Text>
        <Text> 屏幕高度是: {height} </Text>
        <Text> 屏幕的像素比: {scale } </Text>
      </View>
    )
  }
}

面试题: 为什么获取屏幕分辨率,为什么获取的屏幕高度和真实高度不一致?

Dimensions.get("window")获取的是屏幕可用范围的宽高(不是绝对宽高)

Dimensions宽高不一样

1080(360 x 3)像素的机型上书写340宽度的盒子,则其他机型(width x scale)要书写多少宽度

const {width,scale} = Dimensions.get('window')//获取宽度和缩放比

340/360*3 =/width*scale

所以 ?= width*scale*340/1080

所以 ?= (width*scale/1080)*340
先抽取这个物理比率
const wuli = width * scale/1080 
然后让这个物理比率乘以 我们本来要书写的宽度340

所以,适配所有机型的宽度可以这么写:  width: wuli*340
import React, { Component } from 'react'
import {View, StyleSheet,Dimensions, Image, Text, FlatList} from 'react-native'

const {width,scale} = Dimensions.get('window')
const wuli = width * scale/1080   

... ...

const styles = StyleSheet.create({
    box:{
        width: wuli*340,
        height: wuli*200
    },
})

# 关闭所有黄色警告的代码

index.js中的AppRegistry.registerComponent()前面:

console.ignoredYellowBox = ['Warning: BackAndroid is deprecated. Please use BackHandler instead.','source.uri should not be an empty string','Invalid props.style key'];
 
console.disableYellowBox = true // 关闭警告

# AsyncStorage 使用

AsyncStorage是一个简单的、异步的、持久化的 Key-Value 存储系统,它对于 App 来说是全局性的。

可用来代替 LocalStorage。

Android下,

  • AsyncStorage会将数据存储在RocksDB或者SQLite中,具体存在RocksDB中还是SQLite中这取决于设备支持哪一种存储方式。

IOS下,

  • 如果存储的内容较小,那么AsyncStorage会将存储的内容放在一个序列化的字典中,如果存储的内容较大,那么AsyncStorage会将存储的内容放在一个单独的文件中。

三个主要的方法:

AsyncStorage.setItem(键名,,回调函数)    //保存数据
AsyncStorage.getItem(键名,回调函数)    //获取数据
AsyncStorage.removeItem(键名,回调函数)    //删除数据

执行代码

import React, { Component } from 'react'
import {View,Text,Button,TextInput,AsyncStorage } from 'react-native'
export default class AsyncSstorage01 extends Component {
  constructor(props) { 
    super(props)

    this.state = ({
      val:""
    })
  }

  onSet() {
    AsyncStorage.setItem("Key",this.state.val,(error)=>{
          if(!error){
              alert("保存成功!")
          }else{
              alert("保存失败!")
          }
      })
  }

  onGet(){
    AsyncStorage.getItem("Key",(error,ret)=>{
          if(!error){
             alert("获取成功!Key的值为:"+ret)
          }else{
              alert("获取失败!")
          }
      })
  }
 
  onRemove(){
    AsyncStorage.removeItem("Key",(error,ret)=>{
          if(!error){
              alert("删除成功!")
          }else{
              alert("删除失败!")
          }
      })
  }

  handleChang(text) { 
    this.setState({
      val:text
    })
  }

  render() {
    return (
      <View>
        <TextInput
          placeholder="请输入"
          onChangeText={this.handleChang.bind(this)}
        />
        <Button title="设置" onPress={this.onSet.bind(this)}/>
        <Button title="获取" onPress={ this.onGet}/>
        <Button title="删除" onPress={ this.onRemove}/>

      </View>
    )
  }
}