ReactNative开发总结

https://www.jianshu.com/u/b09c3959ab3b

ReactNative之项目结构介绍

  • 一、初始化ReactNative工程
    • 自动创建iOS和安卓工程,和对应的JS文件,index.ios.js,index.android.js
    • 并且通过Npm加载package.json中描述的第三方框架,放入node_modules文件夹中
react-native init ReactDemo
  • 二、打开iOS工程,找到AppDelegate.m文件,查看程序启动完成
    • 注意:加载控件方法(initWithBundleURL:moduleName:initialProperties:launchOptions:)
    • moduleName不能乱传,必须跟js文件中注册的模块名字保持一致
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  // 1.获取js文件url
  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];

  // 2.加载控件
  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"ReactDemo"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  // 3.创建窗口
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  
  // 4.设置窗口根控制器的View
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  
  // 5.显示窗口
  [self.window makeKeyAndVisible];
  
  return YES;
}

  • 三、打开index.ios.js文件,使用webStorm打开。webStorm代码提示

    • iOS程序一启动,就会加载这个文件,去创建组件,并且把加载完的组件显示到界面上
  • index.ios.js实现步骤

    • 1.加载React模块,因为需要用到JSX,加载Compoent,需要用到里面的Compoent组件
      • React默认组件,Compoent非默认组件,都在react文件夹中。
    • 2.加载AppRegistry,StyleSheet,Text,View原生组件,在react-native文件夹中
    • 3.自定义组件,作为程序入口组件
    • 4.创建样式表
    • 5.注册组件,程序入口,程序一启动就会自动加载注册组件.
// 1.加载React,Componet组件
import React,{compoent} from 'react'

// 2.加载原生组件
import
{
    AppRegistry,
    StyleSheet,
    View,
    Text
}
from 'react-native'

// 3.自定义组件,作为程序入口组件
export default class ReactDemo extends Component {

    // 当加载组件的时候,就会调用render方法,去渲染组件
    render(){
        return (
            <View style={styles.mainStyle}>

            </View>
        )
    }
}

// 4.创建样式表
// 传入一个样式对象,根据样式对象中的描述,创建样式表
var styles = Stylesheet.create({
    mainStyle:{
        flex:1,
        backgroundColor:'red'
    }
})

// 5.注册组件,程序入口
// 第一个参数:注册模块名称
// 第二个参数:函数, 此函数返回组件类名, 程序启动就会自动去加载这个组件
AppRegistry.registerComponent('ReactDemo',()=>ReactDemo)

ReactNative语法

  • 对于第一次接触ReactNative的同学,最痛苦的是什么时候使用{},什么时候使用(),当然我也经历过那段时间,为此简单总结了下。
  • ReactNative中,使用表达式的时候需要用{}包住
style={styles.mainStyle}
  • ReactNative中,在字符串中使用变量的时候,需要用{}包住
var str = 'hello'
<Text>{str}</Text>
  • ReactNative中,对象,字典需要用{}包住
    • style = {},最外层表达式,用{}包住
    • {flex:1},对象,用{}包住
<View style={{flex:1}}></View>
  • 创建组件<View></View>,必须要用()包住
    • 因此只要返回组件,都需要用()
    render(){
        return (
            <View style={styles.mainStyle}>

            </View>
        )
    }

一、ReactNative之CSS布局

  • 一款App要想吸引人的眼球,必须要有好的界面布局,一直以来我的风格都是根据需求讲解,接下来本篇文章将通过需求给大家介绍ReactNative中的布局
  • ReactNative支持CSS中的布局属性,因此可以使用CSS布局属性
  • CSS颜色大全,下面会用到,点击这CSS颜色代码大全

1.1 视图边框

  • 什么时候使用?想设置自己周边有边框的时候
  • 注意点:一定要设置边框宽度
borderBottomWidth number 底部边框宽度
borderLeftWidth number 左边框宽度
borderRightWidth number 右边框宽度
borderTopWidth number 顶部边框宽度
borderWidth number 边框宽度
border<Bottom|Left|Right|Top>Color 各方向边框的颜色,<>表示连着一起,例如borderBottomColor
borderColor 边框颜色

1.2 尺寸

width number
height number

1.3 外边距

  • 设置组件与组件之间的间距
    • 注意:第一个组件比较特殊,参照父组件,与父控件之间的间距。
    • 其他组件间距,相对于上一个组件
  • 什么时候使用?想设置自己在父控件的位置的时候使用
margin number 外边距
marginBottom number 下外边距
marginHorizontal number 左右外边距
marginLeft number 左外边距
marginRight number 右外边距
marginTop number 上外边距
marginVertical number 上下外边距
  • 注意marginRight和width冲突,如果设置了width,marginRight无效。
  • 正确做法

 

 

1.4 内边距

  • 设置子控件与当前控件的位置
  • 什么时候使用?想设置自己的子控件相对自己的位置的时候使用
padding number 内边距
paddingBottom number 下内边距
paddingHorizontal number 左右内边距
paddingLeft number 做内边距
paddingRight number 右内边距
paddingTop number 上内边距
paddingVertical number 上下内边距

1.5 一个子组件代码演示

  • 渲染组件
export default class ReactDemo extends Component {
  render() {
    return (
      <View style={styles.rootView}>
          <View style={styles.innerView}>
              <View style={styles.paddingView}></View>
          </View>
      </View>
    );
  }
}
  • 样式描述
const styles = StyleSheet.create({
    rootView:{
        backgroundColor:'darkorange',
        flex:1
    },
    innerView:{
        backgroundColor:'green',
        // 设置底部边框,一定要设置宽度才行
        borderBottomColor:'yellow',
        borderBottomWidth:2,
        // 设置外间距
        marginTop:50,
        marginLeft:100,
        // 设置内间距
        paddingTop:30,
        paddingLeft:50,
        // 设置宽度
        width:200,
        height:300
    },
    paddingView:{
        backgroundColor:'blue',
        width:50,
        height:50
    }
});
  • 一个子组件运行效果

 

 

1.6 二个子组件代码演示

  • 渲染组件

  render() {
    return (
      <View style={styles.rootView}>
          <View style={styles.innerView}>
              <View style={styles.paddingView}></View>
          </View>
          <View style={styles.innerView}>
              <View style={styles.paddingView}></View>
          </View>
      </View>
    );
  }
}
  • 样式描述
const styles = StyleSheet.create({
    rootView:{
        backgroundColor:'darkorange',
        flex:1
    },
    innerView:{
        backgroundColor:'green',
        // 设置底部边框
        borderBottomColor:'yellow',
        borderBottomWidth:2,
        // 设置外间距
        marginTop:50,
        marginLeft:100,
        // 设置内间距
        paddingTop:30,
        paddingLeft:50,
        // 设置宽度
        width:200,
        height:300
    },
    paddingView:{
        backgroundColor:'blue',
        width:50,
        height:50
    }
});
  • 两个子组件运行效果

 

 

相对定位和绝对定位

  • 边缘
left   number  左边缘。
right  number  右边缘。
top    number  顶部边缘。
bottom number  底部边缘。
  • 定位(position):
    • 通过 "left", "top", "right" 以及 "bottom" 边缘属性进行定位。
absolute:绝对定位,参照父控件位置定位
relative:相对定位,参照当前控件原始位置定位
  • 什么时候使用绝对定位,当想把一个已经设置了宽度的控件快速的放在左下角,右下角的时候,可以使用绝对定位。

 

 

  • 什么时候使用相对定位,当想相对自己做出一些改变的时候,采用相对定位,比如相对自己,往下移动一点

 

 

ReactNative之Flex布局

  • 一般使用ReactNative开发App,一般都采用Flex布局,使用这套布局就非常快。

Flex简介

  • Flex又叫弹性布局,会把当前组件看做一个容器,他的所有子组件都是他容器中的成员,通过Flex,就能迅速的布局容器中的成员。
  • 使用场景:当想快速布局一个组件中所有子组件的时候,可以使用Flex布局

Flex主轴和侧轴

  • Flex中有两个主要的概念:主轴和侧轴
  • 主轴与侧轴的关系:相互垂直的。
  • 主轴:决定容器中子组件默认的布局方向:水平,垂直
  • 侧轴:决定容器中子组件与主轴垂直的布局方向
    • 比如主轴水平,那么子组件默认就是水平布局排布,侧轴就是控制子组件在垂直方向的布局

flexDirection属性

  • flexDirection:决定主轴的方向,水平或者垂直,这样子组件就会水平排布或者垂直排布
  • flexDirection共有四个值,在RN中默认为column。
row(默认值):主轴为水平方向,从左向右。依次排列
row-reverse:主轴为水平方向,从右向左依次排列
column:主轴为垂直方向,默认的排列方式,从上向下排列
column-reverse:主轴为垂直方向,从下向上排列
  • 使用
export default class ReactDemo extends Component {
  render() {
    return (
      <View style={styles.rootView}>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>1</Text>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>2</Text>
          <Text style={[styles.text2Style,styles.baseTextStyle]}>3</Text>
          <Text style={[styles.text3Style,styles.baseTextStyle]}>4</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    rootView:{
        backgroundColor:'darkorange',
        flex:1,
        flexDirection:'row'
    },
    baseTextStyle:{
        backgroundColor:'deepskyblue',
        width:50,
        height:50,
        fontSize:15,
        textAlign:'center',
        margin:20,
    }
});
  • 效果:
  • row
 
row.png
  • row-reverse
 
row-reverse.png
  • column
 
column.png
  • column-reverse
 
column-reverse .png

flexWrap属性

  • flexWrap:决定子控件在父视图内是否允许多行排列。
  • flexWrap共有两个值,默认为nowrap。
nowrap 组件只排列在一行上,可能导致溢出。
wrap   组件在一行排列不下时,就进行多行排列
  • 使用

  render() {
    return (
      <View style={styles.rootView}>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>1</Text>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>2</Text>
          <Text style={[styles.text2Style,styles.baseTextStyle]}>3</Text>
          <Text style={[styles.text3Style,styles.baseTextStyle]}>4</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    rootView:{
        backgroundColor:'darkorange',
        flex:1,
        flexDirection:'row',
        flexWrap:'wrap'
    },
    baseTextStyle:{
        backgroundColor:'deepskyblue',
        width:75,
        height:50,
        fontSize:15,
        textAlign:'center',
        margin:20,
    }
});

  • 效果
  • nowrap
 
nowrap.png
  • wrap
 
wrap.png

justifyContent

  • justifyContent:决定子组件在主轴中具体布局,是靠左,还是居中等
  • justifyContent共有五个值,默认为flex-start
flex-start: 子组件向主轴起点对齐,如果主轴水平,从左开始,主轴垂直,从上开始。
flex-end 子组件向主轴终点对齐,如果主轴水平,从右开始,主轴垂直,从下开始。
center 居中显示,注意:并不是让某一个子组件居中,而是整体有居中效果
space-between 均匀分配,相邻元素间距离相同。每行第一个组件与行首对齐,每行最后一个组件与行尾对齐。
space-around 均匀分配,相邻元素间距离相同。每行第一个组件到行首的距离和每行最后一个组件到行尾的距离将会是相邻元素之间距离的一半
  • 使用

export default class ReactDemo extends Component {
  render() {
    return (
      <View style={styles.rootView}>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>1</Text>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>2</Text>
          <Text style={[styles.text2Style,styles.baseTextStyle]}>3</Text>
          <Text style={[styles.text3Style,styles.baseTextStyle]}>4</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    rootView:{
        backgroundColor:'darkorange',
        flex:1,
        flexDirection:'row',
        justifyContent:'space-around'
    },
    baseTextStyle:{
        backgroundColor:'deepskyblue',
        width:50,
        height:50,
        fontSize:15,
        textAlign:'center',
        marginTop:20,
    }
});
  • 效果
  • flex-start
 
flex-start.png
  • flex-end
 
flex-end.png
  • center
 
center.png
  • space-between
 
space-between .png
  • space-around
 
space-around.png

alignItems

  • alignItems:决定子组件在测轴中具体布局
    • 一直都没有管过侧轴,如果侧轴垂直,决定子组件在上,还是下,或者居中
  • alignItems共有四个值,默认为stretch。
flex-start 子组件向侧轴起点对齐。
flex-end 子组件向侧轴终点对齐。
center 子组件在侧轴居中。
stretch 子组件在侧轴方向被拉伸到与容器相同的高度或宽度。
  • 使用

  render() {
    return (
      <View style={styles.rootView}>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>1</Text>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>2</Text>
          <Text style={[styles.text2Style,styles.baseTextStyle]}>3</Text>
          <Text style={[styles.text3Style,styles.baseTextStyle]}>4</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    rootView:{
        backgroundColor:'darkorange',
        flex:1,
        flexDirection:'row',
        justifyContent:'space-around',
        alignItems:'stretch'
    },
    baseTextStyle:{
        backgroundColor:'deepskyblue',
        width:50,
        // height:50,
        fontSize:15,
        textAlign:'center',
        marginTop:20,
    }
});
  • 效果
  • flex-start
 
flex-start .png
  • flex-end
 
flex-end .png
  • center
 
center .png
  • stretch
 
stretch .png

alignSelf

  • alignSelf:自定义自己的侧轴布局,用于一个子组件设置。
    • 注意:当某个子组件不想参照默认的alignItems时,可以设置alignSelf,自定义自己的侧轴布局。
  • alignSelf共有五个值,默认为auto。
auto 继承它的父容器的alignItems属性。如果没有父容器则为 "stretch"
flex-start 子组件向侧轴起点对齐。
flex-end 子组件向侧轴终点对齐。
center 子组件在侧轴居中。
stretch 子组件在侧轴方向被拉伸到与容器相同的高度或宽度。
  • 使用
export default class ReactDemo extends Component {
  render() {
    return (
      <View style={styles.rootView}>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>1</Text>
          <Text style={[styles.text2Style,styles.baseTextStyle]}>2</Text>
          <Text style={[styles.text3Style,styles.baseTextStyle]}>3</Text>
          <Text style={[styles.text4Style,styles.baseTextStyle]}>4</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    rootView:{
        backgroundColor:'darkorange',
        flex:1,
        flexDirection:'row',
        justifyContent:'space-around',
        alignItems:'center'
    },
    baseTextStyle:{
        backgroundColor:'deepskyblue',
        width:50,
        // height:50,
        fontSize:15,
        textAlign:'center',
        marginTop:20,
    },
    text3Style:{
        alignSelf:'flex-start'
    }
});
  • 效果
 
alignSelf.png

flex

  • flex: 决定子控件在主轴中占据几等分。

  • flex: 任意数字,所有子控件flex相加,自己flex占总共多少,就有多少宽度.

  • 使用

export default class ReactDemo extends Component {
  render() {
    return (
      <View style={styles.rootView}>
          <Text style={[styles.text1Style,styles.baseTextStyle]}>1</Text>
          <Text style={[styles.text2Style,styles.baseTextStyle]}>2</Text>
          <Text style={[styles.text3Style,styles.baseTextStyle]}>3</Text>
          <Text style={[styles.text4Style,styles.baseTextStyle]}>4</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    rootView:{
        backgroundColor:'darkorange',
        flex:1,
        flexDirection:'row',
        justifyContent:'space-around',
        alignItems:'center'
    },
    baseTextStyle:{
        // width:50,
        // height:50,
        fontSize:15,
        textAlign:'center',
        marginTop:20,
    },
    text1Style:{
        flex:1,
        backgroundColor:'red',
    },
    text2Style:{
        flex:1,
        backgroundColor:'deepskyblue',
    },
    text3Style:{
        flex:3,
        backgroundColor:'green'
    },
    text4Style:{
        flex:1,
        backgroundColor:'blue',
    }
});
  • 效果
 
flex.png

ReactNative之组件属性(Props、State)

  • 在App开发中,少不了组件之间的传值,在RN中组件之间通信需要用到Props和State。

Props(属性)

  • 什么是Props?一般用于自定义组件,大多数组件在创建时就可以使用各种参数来进行定制,用于定制的这些参数就称为props(属性)。
  • name:就是Props,通过this.props.name访问
<Room name="小码哥" />
  • 注意:props是在父组件中指定,而且一经指定,在整个组件的生命周期中都不再改变。
  • 使用
class Room extends Component {
    render() {
        return (
            <View style={styles.viewStyle}>
                <Text sytle={styles.textStyle}>欢迎来到{this.props.name}直播间</Text>
            </View>

        );
    }

}

export default class ReactDemo extends Component {
  render() {
    return (
        <Room name="小码哥" />
    );
  }
}

const styles = StyleSheet.create({
    viewStyle:{
        flex:1,
        justifyContent:'center',
        alignItems:'center',

    },
    textStyle:{
        marginTop:20
    }

});
  • 效果

State

  • State:如果以后想修改某个属性,就修改界面,就需要用state。

  • 注意:State属性一般在constructor中声明(ES6),在setState中修改数据.

  • 定义state属性

this.state = {
            num:1,
        };
  • 修改state属性
this.setState({
            num : number
        })
  • 每隔一秒,人数+1,定时器

  • 注意:这里定时器方法必须用bind.

  • 使用

// 自定义房间组件
class Room extends Component {

    timeUpdate() {
        var number = this.state.num;

        number++;

        this.setState({
            num : number
        })

    }

    // 构造方法
    constructor(props){
        super(props);

        // 定义state属性
        this.state = {
            num:1,
        };

        console.log('初始化对象');

        // 创建定时器 1秒 = 1000
        // 这里必须绑定,bind会生成了一个新的函数,并且由绑定者调用,否则this不明确
        setInterval(this.timeUpdate.bind(this),1000);

    }

    render() {
        return (
            <View style={styles.viewStyle}>
                <Text sytle={styles.textStyle}>欢迎来到{this.props.name}直播间</Text>
                <Text>观众数 {this.state.num}</Text>
            </View>

        );
    }

}

// 主组件

export default class ReactDemo extends Component {
  render() {
    return (
        <Room name="小码哥" />
    );
  }
}

const styles = StyleSheet.create({
    viewStyle:{
        flex:1,
        justifyContent:'center',
        alignItems:'center',

    },
    textStyle:{
        marginTop:20
    }

});

ReactNative之父子组件传值

顺传(父传子)

  • 1.通过props传值
  • 案例:父控件给子控件传递名称,子控件根据父控件提供的名称显示谁的儿子

// 子组件
class  SonCompoent extends Component{

    render() {
        return (
            <View style={styles.sonViewStyle}>
                <Text style={{fontSize:20}}>{this.props.name}的baby</Text>
            </View>
        );
    };
}

// 父组件
class FatherComponet extends Component {
    
    render() {
        return (
            <View style={{flex:1,alignItems:'center'}}>
                <SonCompoent  name={this.props.name}/>
            </View>
        );
    }
}

// 主组件
export default class ReactDemo extends Component {
    render() {
        return (
            <FatherComponet name="xmg" />
        );
    }
}

const styles = StyleSheet.create({
    sonViewStyle:{
        flex:1,
        justifyContent:'center',
        alignItems:'center',

    }

});
  • 2.有时候需要拿到组件传值,通过ref拿到控件给控件传值。
  • 案例:爸爸每个月,给儿子生活费.
  • 使用箭头函数解决绑定this问题
import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

class  SonCompoent extends Component{

    constructor(props){
        super(props);

        this.state = {
            money:0
        }
    }

    receiveMoney(money){
        this.setState({
            money:money
        });
    }

    render() {
        return (
            <View style={styles.sonViewStyle}>
                <Text style={{fontSize:20}}>{this.props.name}的baby</Text>
                <Text>总共收到{this.state.money}生活费</Text>
            </View>
        );
    };
}

class FatherComponet extends Component {


    render() {
        return (
            <View style={{flex:1,alignItems:'center'}}>
                <Text style={{marginTop:100}} onPress={()=>{
                    this.refs.son.receiveMoney(1000);
                }}>发生活费</Text>
                <SonCompoent ref="son" name={this.props.name} />
            </View>
        );
    }
}

export default class ReactDemo extends Component {
  render() {
    return (
        <FatherComponet name="xmg" />
    );
  }
}

const styles = StyleSheet.create({
    sonViewStyle:{
        flex:1,
        justifyContent:'center',
        alignItems:'center',

    }

});

AppRegistry.registerComponent('React', () => ReactDemo);

逆传(子传父)

  • 使用方法回调
    • 1.在父组件中定义一个处理接收到值的方法
    • 2.把这个方法传递给子组件,并且绑定this,子组件就能通过this.props拿到这个方法调用
  • 案例:儿子赚钱了,把赚的钱传递给父亲

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

class  SonCompoent extends Component{

    makeMoney (money) {
        // 传递给父控件
        console.log('赚了'+ money);
        console.log(this)
        this.props.receiveMoney(money);

    }

    render() {
        return (
            <View style={styles.sonViewStyle}>
                <Text style={{fontSize:20}}>{this.props.name}的baby</Text>
                <Text onPress={this.makeMoney.bind(this,100)}>赚100块钱</Text>
            </View>
        );
    };
}

class FatherComponet extends Component {

    constructor(props){
        super(props);
        console.log(this);
        this.state = {
            money:0
        };
    }

    receiveMoney(money) {
        console.log('爸爸收到'+ money);

        console.log(this);

        var m = this.state.money;

        m += money;

        this.setState({
            money:m
        });

    }

    render() {
        return (
            <View style={{flex:1,alignItems:'center'}}>
                <SonCompoent  name={this.props.name+'A'} receiveMoney={this.receiveMoney.bind(this)}/>
                <SonCompoent  name={this.props.name+'B'} receiveMoney={this.receiveMoney.bind(this)}/>
                <Text style={{marginBottom:50}}>爸爸收到{this.state.money}</Text>
            </View>
        );
    }
}



export default class ReactDemo extends Component {
  render() {
    return (
        <FatherComponet name="xmg" />
    );
  }
}

const styles = StyleSheet.create({
    sonViewStyle:{
        flex:1,
        justifyContent:'center',
        alignItems:'center',

    }

});

AppRegistry.registerComponent('React', () => ReactDemo);

无关联组件传值(通知)

  • 当两个组件,互相之间拿不到谁的时候,可以用通知传值.
    • 哥哥赚钱了,给弟弟花
import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
    DeviceEventEmitter
} from 'react-native';

class  DiDiCompoent extends Component{

    constructor(props){
        super(props);

        this.state = {
            money:0
        }
    }


    componentDidMount() {

        this.age = 10;
        this.age = 20;

        console.log(this.age);

        // 定义属性,但是对这个属性的修改不会触发render
        this.lister = DeviceEventEmitter.addListener('makeMoney',(money)=>{
            this.setState({
                money:money
            });
        })
    }

    componentWillUnmount() {
        this.lister.remove();
    }


    render() {
        return (
            <View style={styles.didiStyle}>
                <Text> 弟弟</Text>
                <Text>收到{this.state.money}零花钱</Text>
            </View>
        );
    };
}

class GeGeComponet extends Component {

    render() {
        return (
            <View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
                <Text>哥哥</Text>
                <Text onPress={()=>{
                    DeviceEventEmitter.emit('makeMoney',100);
                }}>发生活费</Text>
            </View>
        );
    }
}

export default class ReactDemo extends Component {
    render() {
        return (
            <View style={{flex:1}}>
                <GeGeComponet />
                <DiDiCompoent />
            </View>

        );
    }
}

const styles = StyleSheet.create({
        didiStyle:{
            flex:1,
            justifyContent:'center',
            alignItems:'center'
        }

});

AppRegistry.registerComponent('React', () => ReactDemo);

ReactNaive组件生命周期

  • 任何一个组件都是有生命周期的,我们经常需要在组件的生命周期中做一些事情,比如创建组件的时候或者组件销毁的时候。
  • 组件生命周期大致分为三个阶段,实例化阶段,运行阶段,销毁阶段。

组件生命周期

 

 

实例化阶段

  • constructor:
    • 什么时候调用:在一开始实例化组件的时候调用
    • 作用:初始化state.
  • componentWillMount:
    • 什么时候调用:即将加载组件的时候调用
    • 作用:在render之前做事情
  • render:
    • 什么时候调用:渲染组件的时候调用
    • 作用:通过这个方法
  • componentDidMount:
    • 什么时候调用:加载组件完成的时候调用
    • 作用:在render之后做事情,发送请求
  • 注意:constructor,componentWillMount,componentDidMount只会调用一次
  • 运行结果
 
创建阶段.png

运行阶段

  • componentWillReceiveProps:

    • 什么时候调用:每次传入Props,就会调用
    • 作用:拦截props
  • shouldComponentUpdate:

    • 什么时候调用:每次props,或者state改变,就会调用
    • 作用:控制是否刷新界面
  • componentWillUpdate:

    • 什么时候调用:组件即将更新调用
    • 作用:在render更新前做事情
  • componentDidUpdate:

    • 什么时候调用:组件更新完成调用
    • 作用:在render更新后做事情
  • 注意:绝对不要在componentWillUpdate,componentDidUpdate中调用this.setState方法,否则将导致无限循环调用,在componentWillReceiveProps,shouldComponentUpdate可以。

  • 运行效果

 
修改state.png
 
修改props.png

销毁阶段

  • componentWillUnmount:
    • 什么时候调用:组件即将销毁的时候调用
    • 作用:移除观察者,清空数据

使用

class LifeCompoent extends Component {

    constructor(props){
        super(props);

        self.state = {
            age:0
        }

        console.log('constructor');
    }

    componentWillMount() {
        console.log('componentWillMount');
    }

    componentDidMount() {
        console.log('componentDidMount');
    }

    shouldComponentUpdate() {
        console.log('shouldComponentUpdate');

        return true;
    }

    componentWillReceiveProps() {
        console.log('componentWillReceiveProps');

    }

    componentWillUpdate() {
        console.log('componentWillUpdate');
        this.setState({
            age:1
        });
    }

    componentDidUpdate() {
        console.log('componentDidUpdate');
    }

    componentWillUnmount() {
        console.log('componentWillUpdate');
    }

    render(){
        console.log('render');
        return (
            <View style={styles.lifeStyle} >
                <Text onPress={()=>{
                    this.setState({
                        age:1
                    });
                }}>修改state</Text>
            </View>
        );
    }
}

export default class ReactDemo extends Component {
    constructor(props){
        super(props);
        this.state = {
            name:'xmg'
        }
    }

    render() {
        return (
            <View style={{flex:1,marginBottom:50}}>
            <LifeCompoent name={this.state.name} />
                <Text onPress={()=>{
                    this.setState({
                        name : 'yz'
                    })
                }}>修改props</Text>
            </View>

        );
    }
}

const styles = StyleSheet.create({
    lifeStyle:{
        flex:1,
        justifyContent:'center',
        alignItems:'center'
    }
});

AppRegistry.registerComponent('React', () => ReactDemo);

ReactNative之PropTypes

  • 问题:在自定义组件的时候,通常需要暴露属性出去,并且设置属性类型,外界在使用自定义属性的时候,就自动有提示属性功能,。
  • 解决:使用PropTypes就能解决
  • PropTypes用处:
    • 可以实现类型检查,当传入错误的属性值,会报警告,但是不会报错
    • 用PropTypes定义属性,外界使用的时候,会有提示。
  • 注意:
    • PropTypes必须要用static声明,否则无效果。
    • PropTypes 只能用于React框架的自定义组件,默认JS是没有的,因为它是React框架中的。
  •  static:用来定义类方法或者类属性,定义类的方法和属性,生成的对象就自动有这样的属性了。
    

PropTypes使用步骤

  • PropTypes:属性检测,使用的时候需要先导入,在React框架中
import React, { Component,PropTypes } from 'react';

  • 使用

    // 订阅类属性类型,检查属性类型
    static propTypes = {
        name : PropTypes.string,
        age : PropTypes.number
    }
  • 效果
 
效果.png

属性类型PropTypes

# 数组类型
PropTypes.array

# 布尔类型
PropTypes.bool

# 函数类型
PropTypes.func

# 数值类型
PropTypes.number

# 对象类型
PropTypes.object

# 字符串类型
PropTypes.string

# 规定prop为必传字段
PropTypes.func.isRequired

# prop可为任意类型
PropTypes.any.isRequired

给自定义属性设置初始化值

  • defaultProps:如果想给自定义属性设置初始化值,需要使用defaultProps
  •  注意:也需要添加static
    
  • 使用
//  自定义属性,设置初始值
    static defaultProps = {
        name:'xmg',
        age:20
    }

案例代码

/**
 * Created by ithinkeryz on 2017/5/10.
 */

import React, { Component,PropTypes } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,

} from 'react-native';

export default class XMGView extends Component {

    // 定义属性
    static propTypes = {
        name:PropTypes.string,
        age:PropTypes.number
    }

    // 初始值
    static defaultProps = {
        name:'xmg',
        age:2
    }

    render() {
        // 打印出来, xmg
        console.log(this.props.name)
        return (
            <View>

            </View>
        )
    }

}

ListView原理

  • ListView内部是通过ListViewDataSource这个对象,显示数据,因此使用ListView必须先创建ListViewDataSource对象。
  • ListViewDataSource构造方法(创建对象):可选择性传入4个参数,描述怎么提取数据,怎么刷新cell
  • 这些参数:都是函数,当产生对应的事件的时候,会自动执行这些函数.
构造函数可以接受下列四种参数(都是可选):

getRowData(dataBlob, sectionID, rowID);
getSectionHeaderData(dataBlob, sectionID);
rowHasChanged(prevRowData, nextRowData);
sectionHeaderHasChanged(prevSectionData, nextSectionData);
  • ListViewDataSource为ListView组件提供高性能的数据处理和访问。我们需要调用clone方法从原始输入数据中抽取数据来创建ListViewDataSource对象。
  • 要更新datasource中的数据,请(每次都重新)调用cloneWithRows方法(如果用到了section,则对应cloneWithRowsAndSections方法)clone方法会自动提取新数据并进行逐行对比(使用rowHasChanged方法中的策略),这样ListView就知道哪些行需要重新渲染了

ListView基本使用步骤

  • 1.创建数据源,但还没有给它传递数据
    • 使用state保存数据源,因为数据源的数据改变的时候,需要刷新界面。
    • ListView.DataSource:获取ListViewDataSource构造方法
    • ListViewDataSource构造方法:决定ListView怎么去处理数据,需要传入一个对象,这个对象有四个可选属性,都是方法
      • getRowData(dataBlob, sectionID, rowID); 怎么获取行数据
      • getSectionHeaderData(dataBlob, sectionID); 怎么获取每一组头部数据
      • rowHasChanged(prevRowData, nextRowData); 决定什么情况行数据才发生改变,当行数据发生改变,就会绘制下一行cell
      • sectionHeaderHasChanged(prevSectionData, nextSectionData);决定什么情况头部数据才发生改变,当行数据发生改变,就会绘制下一行cell
    • 注意:初始化ListViewDataSource的时候,如果不需要修改提取数据的方式,只需要实现rowHasChanged,告诉什么时候刷新下一行数据.
    • 注意:默认ListViewDataSource有提取数据方式,可以使用默认的提取方式.
constructor(props) {
  super(props);
  var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
 }
  • 2.给数据源设置数据
constructor(props) {
  super(props);
  var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
  
  this.state = {
     // 给数据源设置数据,创建新的数据,不过保存了之前的行改变方法属性
    dataSource: ds.cloneWithRows(['row 1', 'row 2']),
  };
 }


  • 3.实现数据源方法,确定cell
    • 这个方法会自动传入四个参数(rowData,sectionID,rowID,highlightRow)
    • rowData:当前行数据
    • sectionID:当前行所在组ID
    • rowID:哪一行的角标
    • highlightRow:高亮函数
render() {
        return (

            <View style={{flex:1}}>
                <ListView dataSource={this.state.ds}
                          renderRow={this._renderRow.bind(this)}
                />
            </View>
        )

    }

    // 实现数据源方法,每行cell外观
    _renderRow(rowData, sectionID, rowID, highlightRow) {
        return (
            <View>
                <Text>{rowData}</Text>
            </View>
        );
    }

ListView分割线

 // 哪一组,哪一行,相邻行是否高亮
    _renderSeparator(sectionID, rowID, adjacentRowHighlighted)  {
        console.log(sectionID,rowID,adjacentRowHighlighted);
        return (
            <View style={{height:1,backgroundColor:'black'}}></View>
        )
    }

ListView头部视图

 _renderHeader() {
        return (
            <View>
                <Text>头部视图</Text>
            </View>
        )
    }

ListView尾部视图

 _renderFooter() {
        return (
            <View>
                <Text>尾部视图</Text>
            </View>
        )
    }


ListView点击cell高亮


    _renderRow(rowData, sectionID, rowID, highlightRow) {

        return (
            <TouchableOpacity onPress={()=>{
                AlertIOS.alert(rowID);
                highlightRow(sectionID,rowID)
            }}>
                <View>
                    <Text>{rowData}</Text>
                </View>
            </TouchableOpacity>

        );
    }

ReactNative之解决主头文件问题

RN没有头文件

  • 在RN开发中,可能经常需要封装一个模块,这个模块中有很多组件,有可能在使用一个组件的时候,需要依赖其他组件,这样就会导致使用这个模块一个组件,就常常需要把所有模块的组件导入。
  • 在iOS中,有主头文件,搞个公用的头文件,导入这个公用的头文件就好了,那么同理RN也可以,关键点,RN没有头文件这个说话

RN如何实现头文件

  • 可以先搞个公用的js文件,在这里导入所有组件,在暴露出去
/*
*
* @providesModule CommonGroupListViewHeader
*
* **/

import CommonGroupListView from '../CommonGroupListView/CommonGroupListView'

import CommonGroupItem from '../CommonGroupListView/CommonGroupItem'

import CommonArrowItem from '../CommonGroupListView/CommonArrowItem'

import CommonSwitchItem from '../CommonGroupListView/CommonSwitchItem'

// 这句话的意思:把当前文件作为一个模块导出,模块里面有这些子组件
// 以后导入这个模块的时候,就能获取了这个模块里面的东西.
module.exports = {
    CommonGroupListView,
    CommonGroupItem,
    CommonArrowItem,
    CommonSwitchItem
};
  • 如何使用主头文件
    • 以后就导入CommonGroupListViewHeader这个文件就好了
import CommonGroupListViewHeader from 'CommonGroupListViewHeader'

// 创建行模型
var item0 = new CommonGroupListViewHeader.CommonSwitchItem('','消息推送','');
var item1 = new CommonGroupListViewHeader.CommonSwitchItem('','图书借阅','');
var item2 = new CommonGroupListViewHeader.CommonArrowItem('','解绑设备','');

// 创建第0组
var group = new CommonGroupListViewHeader.CommonGroupItem([item0,item1,item2],10);

groups.push(group);

ReactNative之解决文件导入路径问题

RN文件路径问题

  • 在RN开发中,可能相对于原生iOS开发最麻烦的,就是导入自定义的组件
  • iOS中,导入自定义类,直接导入头文件就好了,不需要关心路径
  • RN中,每次导入自定义组件,都需要描述该组件的相对路径,这个比较浪费时间

解决RN文件路径问题

  • ReactNative提供了一个关键字@providesModule,可以解决文件路径问题,以后只要有这个关键字,导入组件就可以不需要路径,直接类名就好了

  • @providesModule使用

  • 在文件的顶部,嵌套一个多行注释,把@providesModule放在注释里,@providesModule后添加类名,以后就直接使用类名就能导入了。

  • 注意点,带有@providesModule的多行注释,一定要是文件的第一个多行注释。

/**
 * @providesModule Common
 */

import {
    Dimensions
} from 'react-native';

export default class Common {

    static bgColor = 'rgb(232,232,232)';

    static screenW = Dimensions.get('window').width;

    static screenH = Dimensions.get('window').height;
}
  • 外界使用Common
// 以前需要这样
// import Common from './../Common/Common'

// 现在可以直接用类名
import Common from 'Common'

使用@providesModule注意点

  • 前端在设计时候引入路径是必要的。可以很明确文件位置,无论调试还是维护,都知道这个文件来源。如果全部通过非路径导入,等想找这个文件的时候,就不知道这个文件在哪。
  • 当然也能解决,使用cmd+shirt+o就能快速查找文件
  • 所以,只有在公用率较高的模块,并未开发者知道这些模块是如何产生的情况下,再去使用。
  • 这种方式,只能在RN这种环境下用,不能用于前端的项目。

@providesModule原理

  • RN在打包脚本的时候会检测该类型文件,并在整个项目文件查找到对应文件替换成对应的模块代码。打出来的包还是跟导入相对路径是一样的。

ReactNative之利用MVC瞬间搭建设置界面

  • 在RN中,没有分组样式的ListView,而设置界面一般都需要分组样式,因此又得自己封装一套了。
  • 这里我采用MVC的思想快速搭建设置界面。
  • 组与组之间的间距:就用每一组的头部视图去做。

先看如何使用

  • 用法非常简单,就配置下模型,界面就换了.
  • 点击cell跳转,也封装到模型中了.
constructor(props){
        super(props);

        var groups = [];

        this.setupGroup0(groups);

        this.setupGroup1(groups);
        
        this.state = {
            groups:groups
        }
    }

    // 设置第0组
    setupGroup0(groups){
        // 创建行模型
        var item0 = new CommonSwitchItem('','消息推送','');
        var item1 = new CommonSwitchItem('','图书借阅','');
        var item2 = new CommonArrowItem('','解绑设备','');

        // 创建第0组
        var group = new CommonGroupItem([item0,item1,item2],10);

        groups.push(group);
    }

    // 设置第1组
    setupGroup1(groups){
        // 创建行模型
        var item0 = new CommonArrowItem('','意见反馈','');
        var item1 = new CommonArrowItem('','关于技术圈','');

        // 创建第1组
        var group = new CommonGroupItem([item0,item1],10);

        groups.push(group);
    }
    
    render() {
        return (
            <View style={styles.viewStyle}>
                <CommonNavigationBar title={this.props.title}
                                     leftBarButtonItem={this.renderLeftBarButtonItem()}
                />
                <CommonGroupListView commonGroupListViewStyle={{backgroundColor:Common.bgColor}}
                                     dataBlob={this.state.groups}
                                     subTitleStyle={{position:'absolute',right:0}}
                                     navigator={this.props.navigator}
                                     renderFooter={this._renderFooter.bind(this)}
                />
            </View>
        );
    }

    // 渲染底部View
    _renderFooter(){
        return (
            <TouchableOpacity style={{height:60,justifyContent:'flex-end',alignItems:'center'} }>
                <Text style={styles.loginStyle}>立即登录</Text>
            </TouchableOpacity>
        )
    }
}
  • 效果

思路:1.自定义分组ListView

  • 暴露属性
static propTypes = {
        // 组模型数据CommonGroupItem
        dataBlob:PropTypes.array,
        // listView样式
        commonGroupListViewStyle:PropTypes.object,
        // cell title样式:因为一个ListViewcell就一种样式,就定义在ListView中,如果不一样,就定义在模型
        titleStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        // cell image样式
        imageStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        // cell 子标题样式
        subTitleStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        // 渲染头部
        renderHeader:PropTypes.func,
        // 渲染尾部
        renderFooter:PropTypes.func
    };
  • CommonGroupListView代码
/**
 * Created by ithinkeryz on 2017/5/17.
 */
/**
 * Created by ithinkeryz on 2017/5/15.
 */

import React, { Component,PropTypes } from 'react';

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

import CommonArrowItem from './CommonArrowItem'
import CommonRowCell from './CommonRowCell'

export default class CommonGroupListView extends Component {

    static propTypes = {
        // 组模型数据CommonGroupItem
        dataBlob:PropTypes.array,
        // listView样式
        commonGroupListViewStyle:PropTypes.object,
        // cell title样式:因为一个ListViewcell就一种样式,就定义在ListView中,如果不一样,就定义在模型
        titleStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        // cell image样式
        imageStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        // cell 子标题样式
        subTitleStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        // 渲染头部
        renderHeader:PropTypes.func,
        // 渲染尾部
        renderFooter:PropTypes.func
    };

    constructor(props){
        super(props);

        var ds = new ListView.DataSource({
            rowHasChanged:(r1,r2)=>r1 != r2,
            sectionHeaderHasChanged:(s1,s2)=>s1!=s2
        });

        // 处理组数据,把传递过来的组模型,转换为{s1:[rowData]}
        var dataBlob = this.props.dataBlob;

        var sectionData = {};

        dataBlob.forEach((obj,index)=>{
            sectionData[index.toString()] = obj.rowData;
        });

        ds = ds.cloneWithRowsAndSections(sectionData);

        this.state = {
            ds:ds
        }

    }

    render() {

        console.log(this.props.renderFooter);

        return (
            <ListView dataSource={this.state.ds}
                      renderRow={this._renderRow.bind(this)}
                      renderSectionHeader={this._renderSectionHeader.bind(this)}
                      style={this.props.commonGroupListViewStyle}
                      renderFooter={this.props.renderFooter?this.props.renderFooter:undefined}
                      renderHeader={this.props.renderHeader?this.props.renderHeader:undefined}

            />
        );
    }

    // 渲染行
    _renderRow(rowData, sectionID, rowID){

        return (
            <CommonRowCell rowData={rowData} {...this.props}/>
        )
    }

    // 渲染组头部视图
    _renderSectionHeader(sectionData, sectionID){


        var sectionData = this.props.dataBlob[sectionID];

        return (
            <View style={[styles.sectionHeaderStyle,{height:sectionData.sectionHeight}]}>

            </View>
        )
    }
}

var styles = StyleSheet.create({
    viewStyle:{
        backgroundColor:'red',
        flex:1
    },
    sectionHeaderStyle:{
        backgroundColor:'transparent'
    }
});

思路:2.设计组模型

function CommonGroupItem(rowData,sectionHeight) {

    // 组与组之间间距,每组头部视图的高度
    this.sectionHeight = sectionHeight;

    // 每一组的数据
    this.rowData = rowData;
}

module.exports = CommonGroupItem;

思路:3.设计行模型

  • 因为Cell有不同的样式,比如有开关,有箭头,可以根据不同类型cell模型,展示不同的cell样式,搞个基本的cell行模型,存放公用的属性

  • 基本cell模型

function CommonRowItem(image,title,subTitle) {
    this.image = image;
    this.title = title;
    this.subTitle = subTitle;

    // 跳转界面
    this.route = null;

    // 标题样式没必要定义在模型,样式并不是每个都不一样

    // 样式应该定义到ListView,每个ListView一个

}

module.exports = CommonRowItem;
  • 开关cell模型
 
  • 箭头cell模型
import CommonRowItem from './CommonRowItem'


function CommonSwitchItem(image,title,subTitle) {

    CommonRowItem.call(this,image,title,subTitle);

    // 开关
    this.isOn = false;

    // 不能使用,一般开关cell不需要点击
    this.disabled = true;
    // 标题样式没必要定义在模型,样式并不是每个都不一样

    // 样式应该定义到ListView,每个ListView一个
}

module.exports = CommonSwitchItem;

思路:4.自定义cell

  • 暴露属性
static propTypes = {
        rowData:PropTypes.object,
        cellStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        titleStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        imageStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        subTitleStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
    };
  • 设置右边样式
  • 根据类型判断,每个对象的构造方法的名称就是类名
  • 监听cell点击,点击的时候跳转,记得把navigator传递进来,模型保存route属性
this.props.rowData.constructor.name
  • 完整代码
/**
 * Created by ithinkeryz on 2017/5/19.
 */


import React, { Component,PropTypes } from 'react';

import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    Switch,
    TouchableOpacity
} from 'react-native';


export default class CommonRowCell extends Component {

    static propTypes = {
        rowData:PropTypes.object,
        cellStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        titleStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        imageStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
        subTitleStyle:PropTypes.oneOfType([PropTypes.object,PropTypes.string]),
    };

    constructor(props){
        super(props);

        this.state = {
            isOn:this.props.rowData.isOn
        }

    }



    render() {

        return (

            <TouchableOpacity style={styles.cellStyle}
                              disabled={this.props.rowData.disabled}
                              onPressOut={this.clickCell.bind(this)}
            >

                {/*image*/}
                {this.props.rowData.image?<Image source={{uri:this.props.rowData.image}} style={[styles.imageStyle,this.props.imageStyle]}/>:null}

                <View style={styles.middleViewStyle}>
                    {/*title*/}
                    <Text style={[styles.titleStyle,this.props.titleStyle]}>{this.props.rowData.title}</Text>

                    {/*subtitle*/}
                    <Text style={[styles.subTitleStyle,this.props.subTitleStyle]}>{this.props.rowData.subTitle}</Text>
                </View>

                {/*accessoryView*/}
                {this.setupAccessoryView()}

            </TouchableOpacity>
        );
    }

    // onValueChange={(value)=>{}
    // 设置右边附近View
    setupAccessoryView(){

        // console.log(typeof this.props.rowData);

        // 如何判断当前对象属于哪个类,获取构造方法名称,构造方法名称都是类名
        if (this.props.rowData.constructor.name === 'CommonArrowItem'){

            return <Image source={require('./icon_shike_arrow.png')} style={{marginLeft:10,width:7,height:12,marginRight:10}}/>

        } else if (this.props.rowData.constructor.name === 'CommonSwitchItem') {

            return <Switch style={{marginLeft:10,marginRight:10}}
                           onValueChange={(value => {
                               this.setState({
                                   isOn:value,
                               });
                               this.props.rowData.isOn = value
                           })}
                           value={this.state.isOn}
                   />
        }

    }

    // 点击Cell
    clickCell(){
        if (this.props.rowData.route){
            this.props.navigator.push({
                component: this.props.rowData.route.component,
                title: this.props.rowData.route.title,
                passPros: this.props.rowData.route.passPros,
                backButtonIcon: this.props.rowData.route.backButtonIcon,
                backButtonTitle: this.props.rowData.route.backButtonTitle,
                leftButtonIcon: this.props.rowData.route.leftButtonIcon,
                leftButtonTitle: this.props.rowData.route.leftButtonTitle,
                onLeftButtonPress: this.props.rowData.route.onLeftButtonPress,
                rightButtonIcon: this.props.rowData.route.rightButtonIcon,
                rightButtonTitle: this.props.rowData.route.rightButtonTitle,
                onRightButtonPress: this.props.rowData.route.onRightButtonPress
            })
        }
    }
}

var cellH = 44;
var styles = StyleSheet.create({
    imageStyle:{
        width:20,
        height:20,
        marginLeft:10
    },
    cellStyle:{
        flexDirection:'row',
        alignItems:'center',
        height:cellH,
        backgroundColor:'white',
        borderBottomWidth:1,
        borderBottomColor:'#e5e5e5'


    },
    middleViewStyle:{
        height:cellH,
        flexDirection:'row',
        alignItems:'center',
        flex:1,
        marginLeft:10
    },
    titleStyle:{
        color:'rgb(96,96,96)'
    },
    subTitleStyle:{
        color:'rgb(207,207,207)',
        marginLeft:10
    }

});

思路:5.创建组模型,搭建界面

/**
 * Created by ithinkeryz on 2017/5/19.
 */

import React, { Component } from 'react';

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

import CommonNavigationBar from '../Common/CommonNavigationBar'
import CommonHighButton from '../Common/CommonHighButton'
import Common from './../Common/Common'

import CommonGroupListView from '../CommonGroupListView/CommonGroupListView'

import CommonGroupItem from '../CommonGroupListView/CommonGroupItem'

import CommonArrowItem from '../CommonGroupListView/CommonArrowItem'

import CommonSwitchItem from '../CommonGroupListView/CommonSwitchItem'

export default class Setting extends Component {

    constructor(props){
        super(props);

        var groups = [];

        this.setupGroup0(groups);

        this.setupGroup1(groups);

        this.state = {
            groups:groups
        }
    }

    // 设置第0组
    setupGroup0(groups){
        // 创建行模型
        var item0 = new CommonSwitchItem('','消息推送','');
        var item1 = new CommonSwitchItem('','图书借阅','');
        var item2 = new CommonArrowItem('','解绑设备','');

        // 创建第0组
        var group = new CommonGroupItem([item0,item1,item2],10);

        groups.push(group);
    }

    // 设置第1组
    setupGroup1(groups){
        // 创建行模型
        var item0 = new CommonArrowItem('','意见反馈','');
        var item1 = new CommonArrowItem('','关于技术圈','');

        // 创建第1组
        var group = new CommonGroupItem([item0,item1],10);

        groups.push(group);
    }

    render() {
        return (
            <View style={styles.viewStyle}>
                <CommonNavigationBar title={this.props.title}
                                     leftBarButtonItem={this.renderLeftBarButtonItem()}
                />
                <CommonGroupListView commonGroupListViewStyle={{backgroundColor:Common.bgColor}}
                                     dataBlob={this.state.groups}
                                     subTitleStyle={{position:'absolute',right:0}}
                                     navigator={this.props.navigator}
                                     renderFooter={this._renderFooter.bind(this)}
                />
            </View>
        );
    }

    // 渲染底部View
    _renderFooter(){
        return (
            <TouchableOpacity style={{height:60,justifyContent:'flex-end',alignItems:'center'} }>
                <Text style={styles.loginStyle}>立即登录</Text>
            </TouchableOpacity>
        )
    }


    renderLeftBarButtonItem(){
        return (
            <CommonHighButton imageUri='btn_backitem'
                              imageStyle={{width:20,height:20}}
                              buttonStyle={{position:'absolute',left:5}}
                              onPressOut={()=>{
                                  this.props.navigator.pop();
                              }}
            />
        )
    }



}

var styles = StyleSheet.create({
    viewStyle:{
        backgroundColor:Common.bgColor,
        flex:1
    },
    loginStyle:{
        height:35,
        width:Common.screenW,
        textAlign:'center',
        color:'red',
        backgroundColor:'white',
        lineHeight:35,
        fontSize:15
    }
});

ReactNative之自己实现继承

  • 在JS中,通过function自定义类,是没有继承这个功能的,要想实现继承,必须自己实现。
  • JS中要想实现继承,可以通过call这个方法实现。

call方法作用和原理

  • 1.交换方法调用
    • 如果call方法传入的参数是方法(sayHi),那么call的作用就是交换方法调用
function sayHello(a,b) 
{ 
    alert('hello'); 
} 

function sayHi(a,b) 
{ 
    alert('hi');  
} 


sayHello.call(sayHi);  // 打印 hello,交换方法调用,其实是调用sayHello方法,sayHello交换sayHi

  • 2.交换方法调用者
    • 如果call方法传入的参数是对象(sayHi),那么call的作用就是交换方法
function Class1() 
{ 
    this.name = "类1";

    this.log = function() 
    { 
        alert(this.name); 
    } 
} 

function Class2() 
{ 
    this.name = "类2"; 
} 

var c1 = new Class1(); 

var c2 = new Class2(); 

c1.log.call(c2); // 打印类2,让c2调用c1的log方法,交换方法调用者
 

call实现继承

  • 由于call可以实现方法调用者交换,利用这个特性,就能实现继承。

  • 比如类B想要继承类A,那么本质无非就是把类A中的所有属性和方法拷贝一份到类B中

  • 只要生成类B对象的时候,底层去调用类A的构造方法,就能实现了。

  • 继承具体实现原理和代码

function A(name,age)
{
    this.name = name;

    this.age = age;

    this.log = function()
    {
        alert(this.name);
    }
}

function B(name,age)
{
    // 这句话就会去让b对象调用A的构造方法,因为A只有构造方法,如果一个方法实现中有this,一般都是构造方法.
    // A的构造方法,就是在定义属性和方法,因此B也有了
    A.call(this,name,age);
}

var b = new B('yz',18);

console.log(b);

ReactNative之模块导出

  • 什么是模块?在JS开发中每一个文件可以称为一个模块,RN基于JS开发,因此也是可以这么叫。

自定义组件导出

  • 在RN中,一般自定义组件都需要弄一个单独文件,那怎么导出这个文件中的组件了。

  • 导出有两种形式,一种叫默认组件,一种叫非默认组件,开发中一般使用默认组件

  • 在ES6中,一般都使用默认组件,ES5使用非默认组件,因为ES6支持import导出,ES5支持require。

  • 非默认组件定义

export  class CommonGroupListView extends Component
  • 外界如何引入非默认组件
import {CommonGroupItem} from '../CommonGroupListView/CommonGroupItem'
  • 默认组件定义
export default class CommonGroupListView extends Component
  • 外界如何引入默认组件
import CommonGroupItem from '../CommonGroupListView/CommonGroupItem'
  • 非默认组件需要加入{},比默认组件麻烦

自定义类(定义与导出)

  • 在RN中,有时候需要搞一个文件自定义类,一样可以采用导出组件的方法导出自定义类
  • 自定义类与自定义组件的区别:
    • 组件继承与Component
    • 自定义组件:必须使用class定义类,自定义类可以不使用class定义类

没有对象属性的自定义类 (定义与导出)

  • 如果一个自定义类,没有对象属性,都是类方法和类属性,一般使用组件的定义方式(class)和导出方式(export default)。

export default class XMGRequest {
    static PostWithJsonParam(url,param,success,failure) {

        var paramStr = JSON.stringify(param);

        // post请求描述
        var requestDesc = {
            method:'POST',
            headers:{
                'Content-Type':'application/json'
            },
            body:paramStr
        };

        // 发送post请求
        fetch(url,requestDesc)
            .then((response)=>response.json())
            .then((json)=>{
                success(json);
            })
            .catch((error)=>{
                failure(error);
            })
    }

}
 
  • 外界如何引入自定义类
import XMGRequest from './XMGRequest'

有对象属性的自定义类(定义与导出)

  • 如果一个自定义类,有对象属性,通常不用使用组件的定义方式(class)和导出方式(export default),一般用function定义类,module.exports导出。

  • 因为使用class,没法生成带有属性的构造方法,就不能在初始化对象的时候,给属性赋值。

  • 导出方式:一般规范是module.exports,也可以使用export default。

  • class定义类与function定义类的区别:

  • 使用class定义类,在类中声明任何属性,和方法都会自动生成对象的属性和方法.

  • 使用class定义类

export default class Person {

    // 需要创建对象调用
    // 定义属性
    age = 0

    name = ''

    // 定义对象方法
    eat(){
       console.log('吃饭');
    }

}
  • 使用function类,要想定义属性和方法,属性和方法前面必须添加this.

function CommonGroupItem(sectionID,rowData) {

    this.sectionID = sectionID;

    this.rowData = rowData;

    this.eat = function () {

    }
}

  • 导出自定义类两种方式
  • module.exports
function CommonGroupItem(sectionID,rowData) {

    this.sectionID = sectionID;

    this.rowData = rowData;

    this.eat = function () {

    }
}

module.exports = CommonGroupItem;
  • export default
export default function CommonGroupItem(sectionID,rowData) {

    this.sectionID = sectionID;

    this.rowData = rowData;

    this.eat = function () {

    }
}
  • 外界如何引入自定义类
import CommonGroupItem from '../CommonGroupListView/CommonGroupItem'

ReactNative之App引导页实现逻辑

  • 在RN中实现引导页,相比原生实现复杂多了。
  • 原因:
  • 1.RN中不能读取原生的配置信息info.plist文件,这样也就没法判断当前是不是最新版本,是最新版本就展示引导页
  • 2.RN的本地存储是异步的,不是同步的,这样就导致在一开始的时候,想去获取本地存储信息,根据存储信息判断显示引导页还是主页,就会报错
    • 报错原因很简单,程序一启动,就需要立马显示界面,但是由于异步,并不能那么快返回.

RN引导页解决思路:

  • 自己写一个启动界面,一开始的时候显示启动界面
  • 然后在显示完启动界面的方法,去判断待会显示引导页,还是主页

如何判断显示引导页还是主页

  • 第一次进入界面,写个属性,记录下第一次加载。
  • 每次启动,获取之前是否保存过第一次加载的属性,如果加载过,就显示主页,没加载过,就显示引导页

App引导页实现代码

/**
 * Created by ithinkeryz on 2017/5/15.
 */

import React, { Component } from 'react';

import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    AsyncStorage,
    Image
} from 'react-native';

import Main from './Main/Main'

import {Navigator} from 'react-native-deprecated-custom-components'

import Guide from './Guide/Guide'

import Common from './Common/Common'

class LaunchView extends Component {
    render(){
        return (
            <Image source={{uri:'LaunchImage'}} style={{width:Common.screenW,height:Common.screenH}}/>
        )
    }

    componentDidMount() {
        // 延迟点
        setTimeout(this.openApp.bind(this),2000);
        // this.openApp();
    }

    openApp(){
        AsyncStorage.getItem('isFirst',(error,result)=>{

            if (result == 'false') {
                console.log('不是第一次打开');

                this.props.navigator.replace({
                    component:Main
                })

            } else  {

                console.log('第一次打开');

                // 存储
                AsyncStorage.setItem('isFirst','false',(error)=>{
                    if (error) {
                        alert(error);
                    }
                });

                this.props.navigator.replace({
                    component:Guide
                })
            }
        });
    }
}

export default class App extends Component {

    // 渲染场景
    _renderScene(route, navigator){
        return (
            <route.component navigator={navigator} {...route} />
        )
    }



    render() {
        // 判断是不是第一次打开


        return (
            <Navigator  initialRoute={{
                component: LaunchView
            }}
                        renderScene={this._renderScene.bind(this)}

                        style={{flex:1}}
            />
        );


    }
    
}

ReactNative之本地存储

  • 在RN开发中,如何持久化存储数据,可以使用AsyncStorage,对原生的API进行了一层封装,通过它,就能把数据保存到真机中。

AsyncStorage使用

  • 存储
    • 注意:`AsyncStorage``只能存储字符串,需要把对象转换为字符串才行
  save(){

        var object = {
            name:'xmg',
            age:18
        };

        // JSON.stringify(object): JSON对象转换为字符串 用来存储
        AsyncStorage.setItem('object',JSON.stringify(object),(error)=>{
            if (error) {
                alert('存储失败');
            } else  {
                alert('存储成功');
            }
        });
    }
  • 读取
    read(){
        AsyncStorage.getItem('object',(error,result)=>{
            if (!error) {
                console.log(result);
            }
        })
    }

  • 删除
 delete(){
        AsyncStorage.removeItem('object',(error)=>{
            if (error) {
                alert('删除失败');
            } else  {
                alert('删除成功');
            }
        });
    }

AsyncStorage原理:

  • AsyncStorage存储数据,在iOS中,底层会把数据保存到沙盒中的Documents中,并生成manifest.json文件。保存的数据都在manifest.json中。
 
存储文件路径.png
 
存储数据.png
  • AsyncStorage删除数据,也仅仅是删除manifest.json文件中的数据,并不是删除manifest.json文件

 

ReactNative之自定义选中按钮

  • 在RN开发中,自带的按钮组件非常不好用,没有选中状态,更加不可以自定义样式,要达到开发需求只能自定义按钮了。

自定义选中按钮

  • 暴露以下属性

    static propTypes = {
        // 普通状态
        title:PropTypes.string,
        imageUri:PropTypes.string,
        titleStyle:PropTypes.object,
        imageStyle:PropTypes.object,

        // 高亮状态
        selectedImageUri:PropTypes.string,
        selectedTitleStyle:PropTypes.object,

        // 监听点击
        onPressIn:PropTypes.func,
        onPressOut:PropTypes.func,

        // 选中状态
        selected:PropTypes.bool,

        // 按钮样式
        buttonStyle:PropTypes.object

    };
  • 实现代码
/**
 * Created by ithinkeryz on 2017/5/16.
 */
/**
 * Created by ithinkeryz on 2017/5/15.
 */

import React, { Component,PropTypes} from 'react';

import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity

} from 'react-native';

export default class CommonSelectButton extends Component {
    
    static propTypes = {
        // 普通状态
        title:PropTypes.string,
        imageUri:PropTypes.string,
        titleStyle:PropTypes.object,
        imageStyle:PropTypes.object,

        // 高亮状态
        selectedImageUri:PropTypes.string,
        selectedTitleStyle:PropTypes.object,

        // 监听点击
        onPressIn:PropTypes.func,
        onPressOut:PropTypes.func,

        // 选中状态
        selected:PropTypes.bool,

        // 按钮样式
        buttonStyle:PropTypes.object

    };

    constructor(props){
        super(props);

        this.state = {
            selected:this.props.selected
        }
    }

    render() {
        return (
            <TouchableOpacity style={[styles.buttonStyle,this.props.buttonStyle]}
                              onPressIn={()=>{
                                  if (this.props.onPressIn){
                                      this.props.onPressIn(this);
                                  }

                              }}
                              onPressOut={()=>{
                                  if (this.props.onPressOut){
                                      this.props.onPressOut(this);
                                  }
                              }}
                              activeOpacity={this.props.selectedImageUri || this.props.selectedTitleStyle?0.9:0.3}
            >

                {/*文字*/}
                {this.props.title?<Text style={[this.props.titleStyle,this.props.selected?this.props.selectedTitleStyle:null]}>{this.props.title}</Text>:null}

                {/*头像*/}
                {this.props.imageUri?<Image source={{uri:this.state.selected && this.props.selectedImageUri?this.props.selectedImageUri:this.props.imageUri}} style={[styles.imageStyle,this.props.imageStyle]}/> : null}

            </TouchableOpacity>
        );
    }


}

var styles = StyleSheet.create({
    buttonStyle:{
        backgroundColor:'white',
        flexDirection:'row',
        justifyContent:'center',
        alignItems:'center'
    },
    imageStyle:{
        marginLeft:3
    }
});
  • 如何使用自定义选中按钮
<CommonSelectButton imageUri='mine-sun-icon-click'
                                selectedImageUri='mine-moon-icon'
                                imageStyle={{width:20,height:20}}
                                onPressIn={(button)=>{
                                    var selected = button.state.selected;
                                    selected = !selected;
                                    button.setState({
                                        selected:selected
                                    })
                                }}
            />

ReactNative之自定义高亮按钮

  • 在RN开发中,自带的按钮组件非常不好用,没有高亮状态,更加不可以自定义样式,要达到开发需求只能自定义按钮了。

自定义高亮按钮

  • 暴露以下属性
static propTypes = {
        // 普通状态
        title:PropTypes.string,
        imageUri:PropTypes.string,
        titleStyle:PropTypes.object,
        imageStyle:PropTypes.object,

        // 高亮状态
        highImageUri:PropTypes.string,
        highTitleStyle:PropTypes.object,

        // 监听点击
        onPressIn:PropTypes.func,
        onPressOut:PropTypes.func,

        // 按钮样式
        buttonStyle:PropTypes.object

    };
  • 实现代码
/**
 * Created by ithinkeryz on 2017/5/16.
 */
/**
 * Created by ithinkeryz on 2017/5/15.
 */

import React, { Component,PropTypes} from 'react';

import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity

} from 'react-native';

export default class CommonHighButtonButton extends Component {

    static propTypes = {
        // 普通状态
        title:PropTypes.string,
        imageUri:PropTypes.string,
        titleStyle:PropTypes.object,
        imageStyle:PropTypes.object,

        // 高亮状态
        highImageUri:PropTypes.string,
        highTitleStyle:PropTypes.object,

        // 监听点击
        onPressIn:PropTypes.func,
        onPressOut:PropTypes.func,

        // 按钮样式
        buttonStyle:PropTypes.object

    };

    constructor(props){
        super(props);

        this.state = {
            highLighted:false
        }
    }

    render() {
        return (
            <TouchableOpacity style={[styles.buttonStyle,this.props.buttonStyle]}
                              onPressIn={()=>{
                                  this.setState({
                                      highLighted:true
                                  });

                                  if (this.props.onPressIn){
                                      this.props.onPressIn(this);
                                  }

                              }}
                              onPressOut={()=>{
                                  this.setState({
                                      highLighted:false
                                  });
                                  if (this.props.onPressOut){
                                      this.props.onPressOut(this);
                                  }
                                }
                              }
                              activeOpacity={this.props.highTitleStyle || this.props.highImageUri?0.9:0.3}
            >

                {/*文字*/}
                {this.props.title?<Text style={[this.props.titleStyle,this.state.highLighted?this.props.highTitleStyle:null]}>{this.props.title}</Text>:null}

                {/*头像*/}
                {this.props.imageUri?<Image source={{uri:this.state.highLighted && this.props.highImageUri?this.props.highImageUri:this.props.imageUri}} style={[styles.imageStyle,this.props.imageStyle]}/> : null}

            </TouchableOpacity>
        );
    }


}

var styles = StyleSheet.create({
    buttonStyle:{
        backgroundColor:'white',
        flexDirection:'row',
        justifyContent:'center',
        alignItems:'center'
    },
    imageStyle:{
        marginLeft:3
    }
});
  • 如何使用自定义高亮按钮
 <CommonHighButton imageUri='nav_item_game_icon'
                              imageStyle={{width:20,height:20}}
                              title='按钮'
                              highImageUri='nav_item_game_click_icon'
                              highTitleStyle={{color:'red'}}
            />

ReactNative之自定义导航条

  • 用RN开发App,肯定需要用到导航条,系统自带的导航条不好用,一般都需要自定义导航条。
  • 自定义导航条思想:模仿iOS导航控制器封装了一套RN的导航条
  • 自定义导航条暴露属性
    • 导航条展示什么控件,全部由外界传进来,这样设计,拓展性比较好
 static propTypes = {
        // 左边按钮
        leftBarButtonItem:PropTypes.object,
        // 中间View
        middleView:PropTypes.object,
        // 右边按钮
        rightBarButtonItem:PropTypes.object,
        // 中间标题
        title:PropTypes.string,
        // 中间标题样式
        titleStyle:PropTypes.object,
        // 导航条整个内容,完全由自己自定义
        contentView:PropTypes.object
    };

自定义导航条封装代码

/**
 * Created by ithinkeryz on 2017/5/15.
 */

import React, { Component,PropTypes } from 'react';

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

import Common from './Common'

const NavigatorBarHeight = 64;

export default class CommonNavigationBar extends Component {

    // 暴露属性
    static propTypes = {
        // 左边按钮
        leftBarButtonItem:PropTypes.object,
        // 中间View
        middleView:PropTypes.object,
        // 右边按钮
        rightBarButtonItem:PropTypes.object,
        // 中间标题
        title:PropTypes.string,
        // 中间标题样式
        titleStyle:PropTypes.object,
        // 导航条整个内容,完全由自己自定义
        contentView:PropTypes.object
    };

    constructor(props){
        super(props);

        // 不能同时设置中间标题和中间View
        if (this.props.title && this.props.middleView)  throw "导航控制器不能同时设置title,middleView"

        // 设置了contentView,不要同时设置其他属性
        if (this.props.contentView && (this.props.middleView ||
            this.props.rightBarButtonItem || this.props.title  ||
            this.props.titleStyle || this.props.contentView)
        ) throw "设置了contentView,其他设置无效,不要同时设置"

    }

    // 渲染内容层
    renderContentView(){
        return (
            <View style={styles.contentViewStyle}>
                {/*左边*/}
                <View style={styles.leftStyle}>
                    {this.props.leftBarButtonItem}
                </View>

                {/*中间*/}
                <View style={styles.middleStyle}>
                    {this.props.title?this.renderMiddleTitle():this.props.middleView}
                </View>

                {/*右边*/}
                <View style={styles.rightStyle}>
                    {this.props.rightBarButtonItem}
                </View>
            </View>
        )
    }

    // 渲染中间标题
    renderMiddleTitle(){
        return <Text style={[styles.middleTitleStyle,this.props.titleStyle]}>{this.props.title}</Text>
    }

    render() {
        return (
            <View style={[styles.barStyle,this.props.barStyle]}>
                {this.props.contentView?this.props.contentView:this.renderContentView()}
            </View>
        );
    }
}

var styles = StyleSheet.create({
    barStyle:{
        backgroundColor:'white',
        width:Common.screenW,
        height:NavigatorBarHeight,
        flexDirection:'row'
    },
    contentViewStyle:{
        flexDirection:'row',
        width:Common.screenW,
        height:44,
        backgroundColor:'white',
        position:'absolute',
        bottom:0
    },
    leftStyle:{
        flex:1,
        justifyContent:'center',
        alignItems:'center'
    },
    middleStyle:{
        flex:4,
        justifyContent:'center',
        alignItems:'center'
    },
    rightStyle:{
        flex:1,
        justifyContent:'center',
        alignItems:'center'
    },
    middleTitleStyle:{
        fontSize:20,
        color:'black',
        fontWeight:'bold'
    }
});

自定义导航条使用

  • 这样封装,使用非常简单
  • 封装思想:易用,可扩展,两个条件都达到了
export default class Reading extends Component {

    render() {
        return (
            <View style={styles.viewStyle}>
                <CommonNavigationBar middleView={this.renderMiddleView()}
                                     leftBarButtonItem={this.renderLeftBarButtonItem()}
                                     rightBarButtonItem={this.renderRightBarButtonItem()}
                />

            </View>
        );
    }


    renderMiddleView(){
        return (
            <View>
                <Text>微信</Text>
            </View>
        )
    }

    renderLeftBarButtonItem(){
        return (
            <TouchableOpacity>
                <Image source={{uri:'nav_item_game_click_icon'}} style={{width:20,height:20}}/>
            </TouchableOpacity>
        )
    }
    renderRightBarButtonItem(){
        return (
            <TouchableOpacity>
                <Text>右边</Text>
            </TouchableOpacity>
        )
    }
}

ReactNative之主流架构搭建

  • 无论是iOSApp,还是安卓App,很多App都有同样一种界面结构,上面有个导航条,下面有个TabBar条.
  • 比如网易新闻界面.
  • 在iOS中,是用TabbarController+导航控制器实现,因此RN也是一样.
    • 在iOS中,TabbarController中包装导航控制器就能实现.
  • 在RN中,TabBar包装导航,会有个一个问题,跳转的时候,底部条隐藏不了,但是通常跳转的时候,都需要隐藏底部条.
<TabNavigator>
              <TabNavigator.Item
                  selected={this.state.selecedIndex == 0}
                  title="首页"
                  renderIcon={() => <Image source={{uri:'tabbar_icon_news_normal'}} style={styles.tabIconStyle}/>}
                  renderSelectedIcon={() => <Image source={{uri:'tabbar_icon_news_highlight'}} style={styles.tabIconStyle}/>}
                  onPress={() => {
                        this.setState({
                            selecedIndex:0
                        })
                  }}>

                  <NavigatorIOS initialRoute=
                                    {{
                                        component:XMGHome,
                                        title:'首页',
                                    }}
                                style={{flex:1}}
                  />
</TabNavigator>

ReactNative之网络请求

  • 任何App都少不了从服务器获取数据,那就需要进行网络请求,那在RN中如何进行网络请求了。

fetch API

  • RN网络请求常用方法
fetch: 发送请求,默认Get请求
then : 传入一个回调函数,当上一次操作处理完,就会自动执行then的回调函数,并且自动把处理完的结果,作为参数传递给then的回调函数
response.json(): 把请求到的数据转换为json
catch : 在请求或者处理数据失败的时候,就会执行catch里的回调函数,捕获异常

GET请求

fetch(http://192.168.0.102:3000/home?name=xmg) // 发送GET请求
            .then((response)=>response.json()) // 请求成功,把请求数据转换为 JSON
            .then((json)=>{                    // 获取JSON数据做事情
                console.log(json)
            })
            .catch((error)=>{               // 请求失败或者处理JSON数据失败,调用
                console.log(error)
            })

GET请求封装

  • 每次请求,都要自己拼接url和自己转JSON数据,比较麻烦.
  • 自定义XMGRequest请求类
  • 封装技巧:
    • 直接使用class定义类,不需要继承谁
    • GET方法,声明类方法就好,没必要创建对象去调用,用static声明
    • 方法需要添加文档注释(/** + 回车),就有文档注释
    • GET方法:提供四个参数,url,参数字典,成功回调,失败回调
    • for in 遍历参数字典,拼接参数
export default class XMGRequest {


    /**
     * GET请求
     * @param {字符串} url
     * @param {字典} param
     * @param {成功回调(param:JSON)} success
     * @param {失败回调(param:ERROR)} failure
     * @returns 功能:GET请求
     */
    static GET(url,param,success,failure){

        // 总长度
        var totalParamStr = '';

        // 判断字典参数是否有值
        // 把字典转换为字符串,如果字典为空,转换为'{}'
        var jsonStr = JSON.stringify(param);

        if (jsonStr != '{}') {

            // 符合
            var mark = '?';

            var i = 0;

            for (key in param){

                if (i > 0) {
                    mark = '&'
                }

                var value = param[key];

                var paramStr = mark + key + '=' + value;

                totalParamStr += paramStr;

                i++;

            }

        }



        // 拼接url
        url += totalParamStr;

        fetch(url)
            .then((response)=>response.json())
            .then((json)=>{
                success(json);
            })
            .catch((error)=>{
                failure(error)
            })
    }

}

搭建Get请求服务器


// 获取express模块
var express = require('express');

// 获取http请求
var httpRequest = require('request');

// 创建服务器
var server = express();

server.get('/home',function (request,response) {

    // /home?a=list&c=data&type=1

    // 字符串截取,也可以使用request.query获取请求参数
    var url = request.url;

    var i = url.indexOf('?');

    var paramStr = url.substring(i);

    var baseUrl = 'http://api.budejie.com/api/api_open.php';

    url = baseUrl + paramStr;

    httpRequest(url,function (error, res, data) {

        response.send(data)

    });
    
});

server.listen(3000);

Post请求

  • Post请求有三种方式
    • application/x-www-form-urlencoded: 普通http请求方式,参数是普通的url参数拼接
    • application/json: JSON请求方式,参数是json格式
    • multipart/form-data: 文件上传
  •  Fetch还有可选的第二个参数,可以用来定制HTTP请求一些参数。你可以定制header参数,请求方式,提交数据。
    

application/x-www-form-urlencoded请求

  • 注意:Content-Type:一定不要写错,否则服务器解析不出来
    // application/x-www-form-urlencoded
    Post(){

        var requestOptional = {
            method:'POST',
            headers:{
                'Content-Type':'application/x-www-form-urlencoded'
            },
            body:'account=xmg&pwd=123'
        };

        fetch('http://192.168.0.102:3000/login',requestOptional)
            .then((response)=>response.json())
            .then((json)=>{
                console.log(json)
            })
            .catch((error)=>{
                console.log(error)
            })
    }

application/x-www-form-urlencoded服务器搭建

  • bodyParser:用于解析post参数

// 获取express模块
var express = require('express');

// 获取http请求
var httpRequest = require('request');

// post解析对象
var bodyParser = require('body-parser');

// 创建服务器
var server = express();

// 这个代码必须写在post之前,因为bodyParser框架,用于中间件,而中间件优于get,post请求调用
// 正确的逻辑也是,先把post请求参数解析出来,然后在post中就能通过body拿到了
// 由于bodyParser必须写在post之前,因此接口文档就应该写清楚post请求参数类型,否则传入错误,就不能解析了.
// 不同post参数类型,就必须用对应的解析器,否则解析出来就不对了

// 解析post的参数,post参数是url拼接类型

// application/x-www-form-urlencoded
var jsonParser = bodyParser.urlencoded({extended:true});

// 使用中间件,当拦截到login,就立马执行,bodyParser因为用于中间件
server.use('/login',jsonParser);


server.post('/login',function (request,response) {

    // 获取post请求参数
    console.log(request.body);

    // 根据这个去查询数据

});

server.listen(3000);

application/json请求

  • JSON.stringify(param) => JSON对象转字符串 {name:xmg} => '{name:xmg}'
  • 因为body:只能放字符串,所以必须要把JSON对象转字符串
  • 注意:Content-Type:一定不要写错,否则服务器解析不出来
// application/json
    
    Post(){

        var param = {
            account:'xmg',
            pwd:'123'
        };

        var paramStr = JSON.stringify(param);

        console.log(paramStr)
        
        // post请求描述
        var requestDesc = {
            method:'POST',
            headers:{
                'Content-Type':'application/json'
            },
            body:paramStr
        };

        // 发送post请求
        fetch('http://192.168.0.102:3000/login',requestDesc)
            .then((response)=>response.json())
            .then((json)=>{
                console.log(json)
            })
            .catch((error)=>{
                console.log(error)
            })
    }

application/json服务器搭建


// 获取express模块
var express = require('express');

// 获取http请求
var httpRequest = require('request');

// post解析对象
var bodyParser = require('body-parser');

// 创建服务器
var server = express();

// 解析post的参数,post参数是url拼接类型
// application/json
var jsonParser =  bodyParser.json();

// 使用中间件,当拦截到login,就立马执行,bodyParser因为用于中间件
server.use('/login',jsonParser);

// 监听post请求
server.post('/login',function (request,response) {

    // 获取post请求参数
    console.log(request.body);

    // 根据这个去查询数据

});


server.listen(3000);

Post请求封装

  • application/x-www-form-urlencoded
/**
     * POST请求,application/x-www-form-urlencoded
     * @param {字符串} url
     * @param {字典} param
     * @param {成功回调(param:JSON)} success
     * @param {失败回调(param:ERROR)} failure
     * @returns 功能:POST请求 application/x-www-form-urlencoded
     */
    static PostWithHttpParam(url,param,success,failure){

        var body = '';

        // 判断字典参数是否有值
        // 把字典转换为字符串,如果字典为空,转换为'{}'
        var jsonStr = JSON.stringify(param);

        if (jsonStr != '{}') {

            // 符合
            var mark = '';

            var i = 0;

            for (key in param){

                if (i > 0) {
                    mark = '&'
                }

                var value = param[key];

                var paramStr = mark + key + '=' + value;

                body += paramStr;

                i++;

            }

        }

        console.log(body);

        var requestOptional = {
            method:'POST',
            headers:{
                'Content-Type':'application/x-www-form-urlencoded'
            },
            body:body
        };

        fetch(url,requestOptional)
            .then((response)=>response.json())
            .then((json)=>{
                success(json);
            })
            .catch((error)=>{
                failure(error);
            })
    }
  • application/json
 /**
     * POST请求,application/json
     * @param {字符串} url
     * @param {字典} param
     * @param {成功回调(param:JSON)} success
     * @param {失败回调(param:ERROR)} failure
     * @returns 功能:POST请求 application/json
     */
    static PostWithJsonParam(url,param,success,failure) {
        
        var paramStr = JSON.stringify(param);
        
        // post请求描述
        var requestDesc = {
            method:'POST',
            headers:{
                'Content-Type':'application/json'
            },
            body:paramStr
        };

        // 发送post请求
        fetch(url,requestDesc)
            .then((response)=>response.json())
            .then((json)=>{
                success(json);
            })
            .catch((error)=>{
                failure(error);
            })
    }

ReactNative之自定义类(七)

  • 在RN开发中,少不了自定义类,那么如何自定义类.
  • 使用class,如果需要继承,使用extends
import  { PropTypes } from 'react';

export default class Person {

    // 需要创建对象调用
    // 定义属性
  // 注意:定义属性用=,不能用:
    age = 0

    name = ''

    // 定义对象方法
    eat(){
       console.log('吃饭');
    }

    // 类方法和类属性,通过类名调用
    // 定义类属性
    static money = 0;

    // 定义类方法
    static eat(){
        console.log('类方法 吃饭')
    }

}

自定义类使用

// 创建Person对象
var p = new Person();
        
p.age = 'a'
        
console.log(p)

p.eat();

Person.eat();

ReactNative之基本组件

一、View

  • View组件在RN开发中是最常用的
    • 一般常用于容器,往里面添加子控件,任何子组件都可以,View里面也可以在放View.
    • 没有点击事件,不能监听点击。

二、TouchableOpacity

  • TouchableOpacity点击控件
    • 如果想让一个没有点击事件的组件,能点击,就需要在外层包装一个TouchableOpacity,这个View,就能点击了。
    • 一个View,被TouchableOpacity包装后,点击这个View,就会有透明效果,这个效果可以通过activeOpacity属性调整
    • activeOpacity:不透明度,0~1,1表示不透明,点击就没透明效果了。
    • 注意:TouchableOpacity默认点击区域,就是所有子控件的区域,因为默认一个组件的尺寸由子控件绝对,因此TouchableOpacity也是一样。
  • 使用
<TouchableOpacity activeOpacity={0.7}>
  <View style={styles.childSytle}>
  </View>
</TouchableOpacity>

2.1 如何监听TouchableOpacity点击

  • 注意:onPress与onPressIn,onPressOut,有冲突,不要同时实现
onLongPress function :长按的时候调用

onPress function :点击的时候调用

onPressIn function :手指按下的时候调用

onPressOut function :手指抬起的时候调用
  • 使用

2.2 disabled属性

  • 如果设为true,则禁止此组件的一切交互
  • 通过disabled,可以控制一个被TouchableOpacity包装的组件什么时候能点击。

Text

  • Text:用于展示一段文字
  • 常用属性:
numberOfLines:最大行数,超出最大行数,就不会完全显示,超出部分显示...
selectable:决定用户是否可以长按选择文本,以便复制和粘贴,默认false
suppressHighlighting:默认情况下,文本被按下时会有一个灰色的阴影,如果想取消就设置为true

  • 监听文字点击

    • onPress
  • 常用样式属性

color:字体颜色

fontSize:字体

fontWeight enum('normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900')
指定字体的粗细。大多数字体都支持'normal'和'bold'值。并非所有字体都支持所有的数字值。如果某个值不支持,则会自动选择最接近的值。

lineHeight:行高

textAlign enum('auto', 'left', 'right', 'center', 'justify')
指定文本的对齐方式。其中'justify'值仅iOS支持,在Android上会变为left
  • 使用
            <View style={styles.mainViewStyle}>
                <Text style={styles.textStyle}
                      numberOfLines={1}
                      selectable={true}
                      onPress={()=>{alert('点击文字')}}
                >
                    文本
                </Text>
            </View>

Button

  • Button:按钮,当一个文字想要点击效果,可以使用按钮

  • 注意:Button没有样式,设置样式无效,最大的弊端,开发中一般不使用,一般自定义按钮,自己包装一个Text用于按钮.

  • 常用属性

color color :文本的颜色(iOS),或是按钮的背景色(Android)

disabled bool :设置为true时此按钮将不可点击

onPress function :用户点击此按钮时所调用的处理函数

title string :按钮内显示的文本
  • 使用
 <View style={styles.mainViewStyle}>
                <Button style={styles.buttonSytle} title="按钮" color="red"/>
 </View>

TextInput

  • TextInput:文本输入框
    • 默认没有边框,需要自己添加borderWidth
  • 常用属性
autoFocus:自动获取焦点,如果为true,在componentDidMount后会获得焦点。默认值为false

blurOnSubmit:点击键盘,右下角return,或者按回车的时候,是否自动退出键盘,true:是。注意:键盘必须是英文输入键盘,数字键盘无效.

editable:文本框是否可以编辑,默认值为true,如果为false,文本框是不可编辑的

keyboardType enum("default", 'numeric', 'email-address', "ascii-capable", 'numbers-and-punctuation', 'url', 'number-pad', 'phone-pad', 'name-phone-pad', 'decimal-pad', 'twitter', 'web-search')  键盘类型

maxLength:最大字符数,显示输入文本长度

multiline:是否是多行输入框,默认文本输入框只能一行,true,就能多行输入

placeholder:占位文字

placeholderTextColor:占位字符串显示的文字颜色

returnKeyType enum('done', 'go', 'next', 'search', 'send', 'none', 'previous', 'default', 'emergency-call', 'google', 'join', 'route', 'yahoo') #:决定键盘右下角按钮显示的内容

secureTextEntry:是否安全输入,注意:多行无效果

selectionColor:设置光标颜色

clearButtonMode enum('never', 'while-editing', 'unless-editing', 'always') :是否要在文本框右侧显示“清除”按钮

clearTextOnFocus:每次重新输入文本框,是否清空之前的文本

enablesReturnKeyAutomatically:如果为true,键盘会在文本框内没有文字的时候禁用确认按钮。默认值为false
  • 常用方法
clear() :清空输入框的内容。
  • 监听文本框事件
onBlur:监听文本框失去焦点
onChange:当文本框内容变化时调用此回调函数
onChangeText:当文本框内容变化时调用此回调函数。改变后的文字内容会作为参数传递
onEndEditing:当文本输入结束后调用此回调函数
onFocus:当文本框获得焦点的时候调用此回调函数
onSubmitEditing:此回调函数当软键盘的确定/提交按钮被按下的时候调用此函数。如果multiline={true},此属性不可用
onKeyPress:当一个键被按下的时候调用此回调。传递给回调函数的参数为{ nativeEvent: { key: keyValue } },其中keyValue即为被按下的键。会在onChange之前调用
  • 使用
            <View style={styles.mainViewStyle}>
                <TextInput placeholder={'占位文字'}
                           style={styles.textStyle}

                           //multiline={true}
                           clearButtonMode="always"
                           secureTextEntry={true}
                           autoFocus={true}
                           blurOnSubmit={true}
                           selectionColor="red"
                           clearTextOnFocus="true"
                           onBlur={()=>{
                               console.log('文本框失去焦点');
                           }}
                           onChangeText={(text)=>{
                               console.log('文字改变'+text)
                           }}
                           onEndEditing={()=>{
                               console.log('文本框结束编辑');
                           }}
                           onFocus={()=>{
                               console.log('获取焦点');
                           }}
                           onSubmitEditing={()=>{
                               console.log('点击提交按钮');
                           }}


                />
            </View>

ReactNative之基本组件

一、View

  • View组件在RN开发中是最常用的
    • 一般常用于容器,往里面添加子控件,任何子组件都可以,View里面也可以在放View.
    • 没有点击事件,不能监听点击。

二、TouchableOpacity

  • TouchableOpacity点击控件
    • 如果想让一个没有点击事件的组件,能点击,就需要在外层包装一个TouchableOpacity,这个View,就能点击了。
    • 一个View,被TouchableOpacity包装后,点击这个View,就会有透明效果,这个效果可以通过activeOpacity属性调整
    • activeOpacity:不透明度,0~1,1表示不透明,点击就没透明效果了。
    • 注意:TouchableOpacity默认点击区域,就是所有子控件的区域,因为默认一个组件的尺寸由子控件绝对,因此TouchableOpacity也是一样。
  • 使用
<TouchableOpacity activeOpacity={0.7}>
  <View style={styles.childSytle}>
  </View>
</TouchableOpacity>

2.1 如何监听TouchableOpacity点击

  • 注意:onPress与onPressIn,onPressOut,有冲突,不要同时实现
onLongPress function :长按的时候调用

onPress function :点击的时候调用

onPressIn function :手指按下的时候调用

onPressOut function :手指抬起的时候调用
  • 使用

2.2 disabled属性

  • 如果设为true,则禁止此组件的一切交互
  • 通过disabled,可以控制一个被TouchableOpacity包装的组件什么时候能点击。

Text

  • Text:用于展示一段文字
  • 常用属性:
numberOfLines:最大行数,超出最大行数,就不会完全显示,超出部分显示...
selectable:决定用户是否可以长按选择文本,以便复制和粘贴,默认false
  • 监听文字点击

    • onPress
  • 常用样式属性

color:字体颜色

fontSize:字体

fontWeight enum('normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900')
指定字体的粗细。大多数字体都支持'normal'和'bold'值。并非所有字体都支持所有的数字值。如果某个值不支持,则会自动选择最接近的值。

lineHeight:行高

textAlign enum('auto', 'left', 'right', 'center', 'justify')
指定文本的对齐方式。其中'justify'值仅iOS支持,在Android上会变为left
  • 使用
            <View style={styles.mainViewStyle}>
                <Text style={styles.textStyle}
                      numberOfLines={1}
                      selectable={true}
                      onPress={()=>{alert('点击文字')}}
                >
                    文本
                </Text>
            </View>

Button

  • Button:按钮,当一个文字想要点击效果,可以使用按钮

  • 注意:Button没有样式,设置样式无效,最大的弊端,开发中一般不使用,一般自定义按钮,自己包装一个Text用于按钮.

  • 常用属性

color color :文本的颜色(iOS),或是按钮的背景色(Android)

disabled bool :设置为true时此按钮将不可点击

onPress function :用户点击此按钮时所调用的处理函数

title string :按钮内显示的文本
  • 使用
 <View style={styles.mainViewStyle}>
                <Button style={styles.buttonSytle} title="按钮" color="red"/>
 </View>

TextInput

  • TextInput:文本输入框
    • 默认没有边框,需要自己添加borderWidth
  • 常用属性
autoFocus:自动获取焦点,如果为true,在componentDidMount后会获得焦点。默认值为false

blurOnSubmit:点击键盘,右下角return,或者按回车的时候,是否自动退出键盘,true:是。注意:键盘必须是英文输入键盘,数字键盘无效.

editable:文本框是否可以编辑,默认值为true,如果为false,文本框是不可编辑的

keyboardType enum("default", 'numeric', 'email-address', "ascii-capable", 'numbers-and-punctuation', 'url', 'number-pad', 'phone-pad', 'name-phone-pad', 'decimal-pad', 'twitter', 'web-search')  键盘类型

maxLength:最大字符数,显示输入文本长度

multiline:是否是多行输入框,默认文本输入框只能一行,true,就能多行输入

placeholder:占位文字

placeholderTextColor:占位字符串显示的文字颜色

returnKeyType enum('done', 'go', 'next', 'search', 'send', 'none', 'previous', 'default', 'emergency-call', 'google', 'join', 'route', 'yahoo') #:决定键盘右下角按钮显示的内容

secureTextEntry:是否安全输入,注意:多行无效果

selectionColor:设置光标颜色

clearButtonMode enum('never', 'while-editing', 'unless-editing', 'always') :是否要在文本框右侧显示“清除”按钮

clearTextOnFocus:每次重新输入文本框,是否清空之前的文本

enablesReturnKeyAutomatically:如果为true,键盘会在文本框内没有文字的时候禁用确认按钮。默认值为false
  • 监听文本框事件
onBlur:监听文本框失去焦点
onChange:当文本框内容变化时调用此回调函数
onChangeText:当文本框内容变化时调用此回调函数。改变后的文字内容会作为参数传递
onEndEditing:当文本输入结束后调用此回调函数
onFocus:当文本框获得焦点的时候调用此回调函数
onSubmitEditing:此回调函数当软键盘的确定/提交按钮被按下的时候调用此函数。如果multiline={true},此属性不可用
onKeyPress:当一个键被按下的时候调用此回调。传递给回调函数的参数为{ nativeEvent: { key: keyValue } },其中keyValue即为被按下的键。会在onChange之前调用
  • 使用
            <View style={styles.mainViewStyle}>
                <TextInput placeholder={'占位文字'}
                           style={styles.textStyle}

                           //multiline={true}
                           clearButtonMode="always"
                           secureTextEntry={true}
                           autoFocus={true}
                           blurOnSubmit={true}
                           selectionColor="red"
                           clearTextOnFocus="true"
                           onBlur={()=>{
                               console.log('文本框失去焦点');
                           }}
                           onChangeText={(text)=>{
                               console.log('文字改变'+text)
                           }}
                           onEndEditing={()=>{
                               console.log('文本框结束编辑');
                           }}
                           onFocus={()=>{
                               console.log('获取焦点');
                           }}
                           onSubmitEditing={()=>{
                               console.log('点击提交按钮');
                           }}


                />
            </View>

Image

  • Image:用于加载图片

    • 图片可以是本地图片也可以是网络中的图片
  • Image常用属性

source {uri: string}, number : 设置Image图片资源

blurRadius number:让图片模糊

defaultSource  {uri: string, width: number, height: number, scale: number}, number 

占位图片,在读取图片时默认显示的加载提示图片

resizeMode enum('cover', 'contain', 'stretch', 'repeat', 'center') 

决定图片尺寸大小。

cover: 在保持图片宽高比的前提下缩放图片,直到宽度和高度都大于等于容器视图的尺寸(如果容器有padding内衬的话,则相应减去)。译注:这样图片完全覆盖甚至超出容器,容器中不留任何空白。

contain: 在保持图片宽高比的前提下缩放图片,直到宽度和高度都小于等于容器视图的尺寸(如果容器有padding内衬的话,则相应减去)。译注:这样图片完全被包裹在容器中,容器中可能留有空白

stretch: 拉伸图片且不维持宽高比,直到宽高都刚好填满容器。

repeat: 重复平铺图片直到填满容器。图片会维持原始尺寸。仅iOS可用。

center: 居中不拉伸。

  • 本地图片存放在哪
    • 1.可以直接存放在RN项目中
 
存放RN.png
 *  2.可以存放到iOS项目中,打开iOS项目,存入到Images.xcassets文件中
 
存放iOS项目中.png
*   3.可以存放到安卓项目中,必须放入drawable-xxhdpi文件中
      *    注意安卓的图片名称,不能以数字开头,也不能有大写字母
          *   自己生成drawable-xxhdpi文件夹,把图片放进去,然后把文件夹drawable-xxhdpi存放到安卓文件中res文件夹中
 
存放安卓图片.png
  • 如何加载本地图片
    • RN中加载资源:require(文件路径),用于加载RN中的资源,不管是图片,还是json都是一样的
    • uri:指定一个资源路径,就会自动加载
    • uri加载注意:通过uri加载资源,必须设置图片尺寸,否则不显示
    • 如果网络加载http图片,iOS默认不支持,需要做配置
 
iOS加载http图片.png
<View style={styles.mainViewStyle}>
                <Text>1.加载RN项目的资源</Text>
                <Image source={require('./Img/chaolan.jpeg')} style={styles.imageSytle}/>
                <Text>2.加载App中的资源</Text>
                <Image source={{uri:'wukong'}} style={styles.imageSytle}/>
                <Text>3.加载网络中的资源</Text>
                {/*https*/}
                {/*<Image source={{uri:'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1493026646896&di=b77d170bbb31db812fced6d3b2f24499&imgtype=0&src=http%3A%2F%2Fcnews.chinadaily.com.cn%2Fimg%2Fsite1%2F20170410%2F448a5bd66cd11a556e112b.jpeg'}} style={styles.imageSytle}/>*/}
                {/*http*/}
                <Image source={{uri:'http://img01.youxiaoshuo.com/portal/201703/21/083647y43dl1j14s8s3g99.jpg'}} style={styles.imageSytle}/>
</View>
  • 监听图片加载方法
onLoad:图片加载完成时调用此函数
onLoadStart:图片加载开始时调用
onLoadEnd:加载结束后,不论成功还是失败,调用此回调函数
onError:当加载错误的时候调用此回调函数,参数为{nativeEvent: {error}}
onProgress:在加载过程中不断调用,参数为{nativeEvent: {loaded, total}}
  • Image类方法,通过类调用的方法,叫做类方法
static getSize(uri: string, success: (width: number, height: number) => void, failure: (error: any) => void) 

在显示图片前获取图片的宽高(以像素为单位)。如果图片地址不正确或下载失败,此方法也会失败。

一般在componentDidMount调用,先获取图片尺寸,然后在设置图片尺寸。

要获取图片的尺寸,首先需要加载或下载图片(同时会被缓存起来)。这意味着理论上你可以用这个方法来预加载图片,虽然此方法并没有针对这一用法进行优化,而且将来可能会换一些实现方案使得并不需要完整下载图片即可获取尺寸。所以更好的预加载方案是使用下面那个专门的预加载方法。
  • 使用
constructor(props){
        super(props)
        Image.getSize('http://img01.youxiaoshuo.com/portal/201703/21/083647y43dl1j14s8s3g99.jpg',(width,height)=>{
                console.log('获取图片尺寸')
                console.log(width,height)
        },(error,)=>{

        })
    }


    render() {

        return (
            <View style={styles.mainViewStyle}>
                <Text>1.加载RN项目的资源</Text>
                <Image source={require('./Img/chaolan.jpeg')}
                       style={styles.imageSytle}
                       blurRadius={10}
                />
                <Text>2.加载App中的资源</Text>
                <Image source={{uri:'wukong'}} style={styles.imageSytle}/>
                <Text>3.加载网络中的资源</Text>
                {/*https*/}
                {/*<Image source={{uri:'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1493026646896&di=b77d170bbb31db812fced6d3b2f24499&imgtype=0&src=http%3A%2F%2Fcnews.chinadaily.com.cn%2Fimg%2Fsite1%2F20170410%2F448a5bd66cd11a556e112b.jpeg'}} style={styles.imageSytle}/>*/}
                {/*http*/}
                <Image source={{uri:'http://img01.youxiaoshuo.com/portal/201703/21/083647y43dl1j14s8s3g99.jpg'}}
                       style={styles.imageSytle}
                       defaultSource={{uri:'wukong'}}
                       onLoad={()=>{
                           alert('图片加载完成');
                       }}
                       onProgress={(e)=>{
                           console.log(e.nativeEvent);
                       }}
                />
            </View>

        );
    }

ReactNative之TabNavigator

TabNavigator常用属性

 
TabNavigator属性.png

iOS和安卓适配

 
TabNavigator.png

使用

  • 1.安装第三方框架
npm install react-native-tab-navigator --save
  • 2.导入框架
import TabNavigator from 'react-native-tab-navigator';
  • 3.使用
  • 注意:renderIcon是传入一个函数,这个函数返回一个Image组件,Image通过url加载,一定要记得设置尺寸,否则不显示
render() {
        return (
            <TabNavigator>
                <TabNavigator.Item
                    title="消息"
                    selected={0==this.state.selectIndex}
                    renderIcon={() => <Image source={{uri:'tab_recent_nor'}} style={styles.iconStyle}/>}
                    onPress={() => this.setState({ selectIndex: 0 })}>
                    <View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
                        <Text>消息</Text>
                    </View>
                </TabNavigator.Item>
                <TabNavigator.Item
                    title="联系人"
                    selected={1==this.state.selectIndex}
                    renderIcon={() => <Image source={{uri:'tab_recent_nor'}} style={styles.iconStyle}/>}
                    onPress={() => this.setState({ selectIndex: 1 })}>
                    <View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
                        <Text>联系人</Text>
                    </View>
                </TabNavigator.Item>
                <TabNavigator.Item
                    title="动态"
                    selected={2==this.state.selectIndex}
                    renderIcon={() => <Image source={{uri:'tab_qworld_nor'}} style={styles.iconStyle}/>}
                    onPress={() => this.setState({ selectIndex: 2 })}>
                    <View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
                        <Text>动态</Text>
                    </View>
                </TabNavigator.Item>
            </TabNavigator>
        )

    }

安卓加载图片

  • 需要生成一个文件夹,名称固定drawable-xxhdpi,把图片放入这个文件夹中,然后放入安卓文件的res文件夹中

ReactNative之ScrollView

  • 都知道普通的View,是不能滚动的,那么想要一个View,能滚动,就需要使用ScrollView
  • ScrollView什么时候能滚动,当内容超出自己尺寸的时候就能滚动,

ScrollView常用的属性

horizontal bool 
当此属性为true的时候,所有的子视图会在水平方向上排成一行,而不是默认的在垂直方向上排成一列。默认值为false。

showsHorizontalScrollIndicator bool
当此属性为true的时候,显示一个水平方向的滚动条。

showsVerticalScrollIndicator bool
当此属性为true的时候,显示一个垂直方向的滚动条。

alwaysBounceHorizontal bool 
当此属性为true时,水平方向即使内容比滚动视图本身还要小,也可以弹性地拉动一截。当horizontal={true}时默认值为true,否则为false。

(ios) alwaysBounceVertical bool
当此属性为true时,垂直方向即使内容比滚动视图本身还要小,也可以弹性地拉动一截。当horizontal={true}
时默认值为false,否则为true。

(ios) automaticallyAdjustContentInsets bool
当滚动视图放在一个导航条或者工具条后面的时候,iOS系统是否要自动调整内容的范围。默认值为true。(译注:如果你的ScrollView或ListView的头部出现莫名其妙的空白,尝试将此属性置为false)

(ios) bounces bool
当值为true时,如果内容范围比滚动视图本身大,在到达内容末尾的时候,可以弹性地拉动一截。如果为false,尾部的所有弹性都会被禁用,即使alwaysBounce*
属性为true。默认值为true。

(ios) bouncesZoom bool 
当值为true时,使用手势缩放内容可以超过min/max的限制,然后在手指抬起之后弹回min/max的缩放比例。否则的话,缩放不能超过限制。

(ios) contentInset {top: number, left: number, bottom: number, right: number} 
内容范围相对滚动视图边缘的坐标。默认为{0, 0, 0, 0}
。

(ios) contentOffset PointPropType
用来手动设置初始的滚动坐标。默认值为{x: 0, y: 0}

pagingEnabled bool
当值为true时,滚动条会停在滚动视图的尺寸的整数倍位置。这个可以用在水平分页上。默认值为false。

scrollEnabled bool
当值为false的时候,内容不能滚动,默认值为true。

(ios) scrollEventThrottle number
这个属性控制在滚动过程中,scroll事件被调用的频率(单位是每秒事件数量)。更大的数值能够更及时的跟踪滚动位置,不过可能会带来性能问题,因为更多的信息会通过bridge传递。默认值为0,意味着每次视图被滚动,scroll事件只会被调用一次。

(ios)scrollIndicatorInsets {top: number, left: number, bottom: number, right: number} 
决定滚动条距离视图边缘的坐标。这个值应该和contentInset
一样。默认值为{0, 0, 0, 0}。

(ios) scrollsToTop bool 
当此值为true时,点击状态栏的时候视图会滚动到顶部。默认值为true。

stickyHeaderIndices [number]
一个子视图下标的数组,用于决定哪些成员会在滚动之后固定在屏幕顶端。举个例子,传递stickyHeaderIndices={[0]}
会让第一个成员固定在滚动视图顶端。这个属性不能和horizontal={true}
一起使用。

ScrollView常用的方法

  • 开发中,常需要在滚动的时候做事情,那怎么监听ScrollView滚动
                            // 监听滚动开始
                            onMomentumScrollBegin={this._onMomentumScrollBegin.bind(this)}

                            // 监听滚动结束
                            onMomentumScrollEnd={this._onMomentumScrollEnd.bind(this)}

                            // 监听开始拖拽
                            onScrollBeginDrag={this._onScrollBeginDrag.bind(this)}

                            // 监听结束拖拽
                            onScrollEndDrag={this._onScrollEndDrag.bind(this)}

                            // 监听滚动动画完成
                            onScrollAnimationEnd={this._onScrollAnimationEnd.bind(this)}

                            // 监听滚动的时候
                            onScroll={this._onScroll.bind(this)}

                            // 设置滚动频率,一滚动就监听,需要和onScroll配套使用
                            scrollEventThrottle={1}

获取ScrollView偏移量

  • 滚动的时候,会传入一个合成事件作为监听滚动方法的参数,每个方法都会有这个合成事件
  • 通过合成事件能获取原生事件nativeEvent,原生事件nativeEvent会有我们想要的信息.
  • 什么是合成事件:在React中,事件的处理由其内部自己实现的事件系统完成,触发的事件都叫做 合成事件(SyntheticEvent)
_onScroll(e) {
       // console.log('滚动的时候调用');
        // 不能通过scrollView获取,因为在RN中,滚动的时候,不会给scrollView组件的contentOffset属性赋值,只能通过nativeEvent事件获取
       //  var scrollView = this.refs.scrollView;
       //
       //  console.log(scrollView.props.contentOffset);

        var nativeEvent = e.nativeEvent;

        console.log(nativeEvent.contentOffset);
    }

使用


var arr = require('./res/zhubo.json');
var screenW = Dimensions.get('window').width;

// 引入资源:require
export default class ReactDemo extends Component {



    render() {
        return (

            <View style={{flex:1}}>
                <ScrollView style={{flex:1,backgroundColor:'red'}}

                            // 监听滚动开始
                            onMomentumScrollBegin={this._onMomentumScrollBegin.bind(this)}

                            // 监听滚动结束
                            onMomentumScrollEnd={this._onMomentumScrollEnd.bind(this)}

                            // 监听开始拖拽
                            onScrollBeginDrag={this._onScrollBeginDrag.bind(this)}

                            // 监听结束拖拽
                            onScrollEndDrag={this._onScrollEndDrag.bind(this)}

                            // 监听滚动动画完成
                            onScrollAnimationEnd={this._onScrollAnimationEnd.bind(this)}

                            // 监听滚动的时候
                            onScroll={this._onScroll.bind(this)}

                            // 设置滚动频率,一滚动就监听
                            scrollEventThrottle={1}

                            ref="scrollView"
                >
                    {this.setupChildView()}
                </ScrollView>
            </View>
        );
    }

    // 滚动的时候 会传入一个合成事件

    // 在 React 中, 事件的处理由其内部自己实现的事件系统完成,触发的事件都叫做 合成事件(SyntheticEvent)

    // 通过合成事件能获取原生事件nativeEvent,原生事件nativeEvent会有我们想要的信息.
    _onScrollAnimationEnd (e) {
        console.log('滚动的完成时候调用');

    }
    _onScroll(e) {
       // console.log('滚动的时候调用');
        // 不能通过scrollView获取,因为在RN中,滚动的时候,不会给scrollView组件的contentOffset属性赋值,只能通过nativeEvent事件获取
       //  var scrollView = this.refs.scrollView;
       //
       //  console.log(scrollView.props.contentOffset);

        var nativeEvent = e.nativeEvent;

        console.log(nativeEvent.contentOffset);
    }

    _onScrollBeginDrag(e) {
        console.log('开始拖拽的时候调用');


    }

    _onScrollEndDrag(e) {
        console.log('结束拖拽的时候调用');
    }


    _onMomentumScrollBegin(e) {
        console.log('当一帧滚动开始的时候');
    }

    _onMomentumScrollEnd(e) {
        console.log('当一帧滚动结束的时候');
    }


    setupChildView(){
        var childs = [];
        for (var i = 0;i < arr.length;i++){
            var zhubo = arr[i];
            childs.push(
                <Image key={i} style={{width:screenW,height:300}} source={{uri:zhubo.icon}}/>
            )
        }
        return childs;
    }

}

ReactNative之TabBariOS

  • 目前主流的App,底部都有一个选项条,这个就需要用TabBariOS实现
  • 一行代码,就会底部有条
<TabBarIOS></TabBarIOS>
  • 常用属性
barTintColor string:标签栏的背景颜色。

style:样式

tintColor string:  当前被选中的标签图标的颜色。

unselectedItemTintColor string:  当前没有被选中的标签图标的颜色。仅在iOS 10及以上版本有效

translucent bool: 一个布尔值,决定标签栏是否需要半透明化。

如何添加选项卡

  • TabBarIOS.Item
    • 注意:TabBarIOS.Item必须包装一个View,作为点击tabBar按钮,切换的View
<TabBarIOS.Item title='消息'
                                icon={{uri:'tab_recent_nor'}}
                                badge={10}
                >
                    <View>
                        <Text>消息</Text>
                    </View>
                </TabBarIOS.Item>
  • 常用属性
badge string, number :在图标右上角显示一个红色的气泡。

icon Image.propTypes.source  :给当前标签指定一个自定义的图标。如果定义了systemIcon属性, 这个属性会被忽略。

onPress function :当此标签被选中时调用。你应该修改组件的状态来使得selected={true}。

selected bool :这个属性决定了子视图是否可见。如果你看到一个空白的页面,很可能是没有选中任何一个标签。

selectedIcon Image.propTypes.source :当标签被选中的时候显示的自定义图标。如果定义了systemIcon属性,这个属性会被忽略。如果定义了icon而没定义这个属性,在选中的时候图标会染上蓝色。

systemIcon enum('bookmarks', 'contacts', 'downloads', 'favorites', 'featured', 'history', 'more', 'most-recent', 'most-viewed', 'recents', 'search', 'top-rated') :一些预定义的系统图标。注意如果你使用了此属性,标题和自定义图标都会被覆盖为系统定义的值。

title string :在图标下面显示的标题文字。如果定义了systemIcon属性,这个属性会被忽略

选中按钮,切换界面

  • 只要设置对应的tabBarItem的selected为true,就会自动跳转到对应界面

    • 注意:tabBarItem的selected属性不能写死,可以搞个角标记录当前选中那个角标
  • 监听tabBarItem的点击,修改selected属性

  • 使用

  • 1.订阅角标属性

 constructor(props){
        super(props);
        this.state = {
            selectIndex:0
        }
    }
  • 2.监听tabBar点击,修改选中角标
onPress={()=>{
              this.setState({
              selectIndex:0
               })
             }}
                            
  • 3.select属性根据角标判断
selected={0==this.state.selectIndex}

应用整体代码

render() {
        return (
            <TabBarIOS>
                <TabBarIOS.Item title='消息'
                                icon={{uri:'tab_recent_nor'}}
                                badge={10}
                                onPress={()=>{
                                    this.setState({
                                        selectIndex:0
                                    })
                                }}
                                selected={0==this.state.selectIndex}
                >
                    <View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
                        <Text>消息</Text>
                    </View>
                </TabBarIOS.Item>
            </TabBarIOS>

        )
    }

NavigatorIOS组件

  • 什么是导航,本质就是视图之间界面的跳转,比如首页跳转到详情页。
  • NavigatorIOS作用:用来iOS上界面之间的跳转。

常用属性

barTintColor : 导航条的背景颜色
navigationBarHidden : 为true , 隐藏导航栏。
shadowHidden : 是否隐藏阴影,true/false。
tintColor : 导航栏上按钮的颜色设置。
titleTextColor : 导航栏上字体的颜色 。
translucent : 导航栏是否是半透明的,true/false。

NavigatorIOS使用步骤

  • 1.初始化路由
    • 注意:component,需要传入组件,自定义组件
    • NavigatorIOS上的按钮图片,默认会被渲染成蓝色
    • NavigatorIOS上的按钮,只能放一张图片
    • 注意:导航一定要有尺寸,flex:1,否则看不到子控件
initialRoute:用于初始化路由。其参数对象中的各个属性如下:
 {
  component: function, //加载的视图组件
  title: string, //当前视图的标题
  passPros: object, //传递的数据
  backButtonIcon: Image.propTypes.source, // 后退按钮图标
  backButtonTitle: string, //后退按钮标题
  leftButtonIcon: Image.propTypes.soruce, // 左侧按钮图标
  leftButtonTitle: string, //左侧按钮标题
  onLeftButtonPress: function, //左侧按钮点击事件
  rightButtonIcon: Image.propTypes.soruce, // 右侧按钮图标
  rightButtonTitle: string, //右侧按钮标题
  onRightButtonPress: function, //右侧按钮点击事件
}
  • 使用
<NavigatorIOS
                style={{flex:1,backgroundColor:'blue'}}
                initialRoute={{
                    component: HomeView,
                    title: '首页',
                    leftButtonIcon:{uri'navigationbar_friendattention'},
                }}
            />
  • 2.获取Navigator,只有它才能跳转
    • 只要是导航控制器下的组件,都可以通过props获取
this.props.navigator
  • 3.跳转界面

  • 方法

(1)pust(route):加载一个新的页面(视图或者路由)并且路由到该页面。
(2)pop():返回到上一个页面。
(3)popN(n):一次性返回N个页面。当 N=1 时,相当于 pop() 方法的效果。
(4)replace(route):替换当前的路由。
(5)replacePrevious(route):替换前一个页面的视图并且回退过去。
(6)resetTo(route):取代最顶层的路由并且回退过去。
(7)popToTop():回到最上层视图。
  • 使用
  this.props.navigator.push({
        component:nextView,
        title:'第二页',
        passProps:{name:'xmg'},
    }

Navigator

  • NavigatorIOS弊端:

    • 1.只能用于iOS,不能用于安卓
    • 2.导航条不能自定义
  • Navigator很好解决了上面的问题,RN开发中通常使用它

  • Navigator作用:只提供跳转功能,支持iOS,安卓

    • 注意:导航条需要自定义,需要导航条的界面,自己添加
    • 只要一个控件,包装成Navigator就能获取跳转功能

Navigator导入

  • 在之前的版本可以直接导入
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Dimensions,
    Navigator

} from 'react-native';
  • 但是最近的版本不行,Navigator被移入另外一个库了
  • 直接导入,会报错


 
 
 
 
 
 
 
 
posted @ 2022-05-10 19:53  hanease  阅读(314)  评论(0编辑  收藏  举报