1.React Native环境搭建
1)下载node和Homebrew
2)安装watchman和flow
3)安装React Native
npm i react-native-cli -g
react-native init 项目名称
4)ios开发环境需求
xcode7以上版本
5)安卓开发环境需求
安装最新版本的JDK
2.flex布局
根标签和view标签默认开启flex布局,主轴方向默认是竖向,html中默认是横向
flexDirection:主轴方向
'row':横向
'column':竖向,默认值
注意点:
开启flex:wrap,要去关掉flex:1,否则会出现换行bug,看不见第二行以下的内容
3.获取屏幕宽高和屏幕像素
var Dimensions = require('Dimensions');
var {width, height, scale} = Dimensions.get('window');
Dimensions.get('window').width
Dimensions.get('window').height
Dimensions.get('window').scale
4.组件
view:内部只能放标签不能放文本,文本必须放在text标签中
WebView:渲染html代码
source={{html: this.state.detailData}}:html跟字符串,uri跟链接
Image:图片
source={require('./img/icon.png')} //引入资源
resizeMode:内容展示形式,比如contain、cover等,图片的大小不受宽高影响,需要设置resizeMode才行
TextInput:输入框
value:默认值
keyboardType:键盘类型
multiline:是否支持多行输入
password:密码,设置multiline时不起效果
placeholder:占位符
clearButtonMode:清除按钮的出现时机
css属性:
borderWidth: 边框框
borderColor: 边框颜色
textAlign:'center' // 内容居中
TouchableOpacity:让内部元素可以点击和触摸,并且有视觉效果
<TouchableOpacity
activeOpacity={0.5} // 透明度从0.5到1过渡
onPress={this.activeEvent('点击')}
onPressIn={()=>this.activeEvent('按下')}
onPressOut={()=>this.activeEvent('抬起')}
onLongPress={()=>this.activeEvent('长按')}
>
<View style={styles.innerViewStyle}>
<Text ref="event">常用的事件</Text>
</View>
</TouchableOpacity>
ScrollView:内部元素可以滚动,ScrollView必须有一定高度才能正常工作,一般不建议直接给ScrollView设置高度,而是给其父级元素设置固定高度
ScrollView内部其它响应者无法阻止ScrollView本身作为响应者(ScrollView嵌套ScrollView,可能只有外层ScrollView有效果,可以嵌套listView)
horizontal:设置为true时,内部的元素水平排列
showsHorizontalScrollIndicator:设置为false时,隐藏水平滚动条
pagingEnabled:设置为true时,内部元素会有分页效果,每次滚动时会像轮播图一样一次滚动一屏幕
scrollEnabled:设置为true时,内部元素可以滚动
onMomentumScrollEnd:滚动结束时触发回调
scrollResponderScrollTo({x:offsetX, y:0, animated:true}):设置滚动位置,animated开启动画
onScrollBeginDrag:开始拖拽
onScrollEndDrag:停止拖拽
contentInset = {{top: -200}}:渲染是内容相对于ScrollView顶部的偏移位置,ios适用
contentOffset = {{y:200}}:橡皮筋向下拉长的最大范围,ios适用
scrollEnabled:是否可以滚动,true可以,false不可以
ListView:与ScrollView类似,ScrollView的所有属性都可以使用,内部做了一些优化
contentContainerStyle:放ListView的样式
1)只渲染一组数据
// 设置初始值
getInitialState(){
// Wine是个数组,Wine的每一项会放到rowHasChanged中,(r1, r2) => r1 !== r2}过滤掉相同项
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
return{
dataSource: ds.cloneWithRows(Wine)
}
},
<ListView
dataSource={this.state.dataSource} // 数据源
renderRow={this.renderRow}
/>
// 遍历每一项,放回dom放到ListView中
// sectionID组id
// rowID索引
renderRow(rowData,sectionID,rowID){
return()
}
2)渲染多组数据
1.渲染的数据
var Car = {
"data": [
{
"cars": [
{
"icon": "m_180_100.png",
"name": "AC Schnitzer"
},
],
"title": "A"
},
{
"cars": [
{
"icon": "m_180_100.png",
"name": "AC Schnitzer"
},
],
"title": "B"
}
]
}
2.初始化我们要渲染的数据,固定写法
// 初始化函数
getInitialState(){
var getSectionData = (dataBlob, sectionID) => {
return dataBlob[sectionID];
};
var getRowData = (dataBlob, sectionID, rowID) => {
return dataBlob[sectionID + ':' + rowID];
};
return{
dataSource: new ListView.DataSource({
getSectionData: getSectionData, // 获取组中数据
getRowData: getRowData, // 获取行中的数据
rowHasChanged: (r1, r2) => r1 !== r2, // 组中相同行不渲染
sectionHeaderHasChanged:(s1, s2) => s1 !== s2 // 相同组不渲染
})
}
},
3.得到我们需要渲染的数据格式(多组内置吸顶功能,就是展示在可视区的组标题会固定在listView顶部)
componentDidMount(){
// 调用json数据
this.loadDataFromJson();
},
loadDataFromJson(){
// 拿到json数据
var jsonData = Car.data;
// 定义一些变量
var dataBlob = {},
sectionIDs = [],
rowIDs = [],
cars = [];
// 遍历
for(var i=0; i<jsonData.length; i++){
// 1. 把组号放入sectionIDs数组中
sectionIDs.push(i);
// 2.把组中内容放入dataBlob对象中
dataBlob[i] = jsonData[i].title
// 3. 取出该组中所有的车
cars = jsonData[i].cars;
rowIDs[i] = [];
// 4. 遍历所有的车数组
for(var j=0; j<cars.length; j++){
// 把行号放入rowIDs
rowIDs[i].push(j);
// 把每一行中的内容放入dataBlob对象中
dataBlob[i+':'+j] = cars[j];
}
}
// 更新状态
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlob,sectionIDs,rowIDs)
});
},
4.dataBlob,sectionIDs,rowIDs对应的数据格式
dataBlob = {
'sectionID1':第一组标题内容如titleA,
'sectionID2':第二组标题内容如titleB,
...
'sectionID1:rowID1':第一组第一行内容如titleA,
'sectionID1:rowID2':第一组第二行内容如titleA,
...
'sectionID2:rowID1':第二组第一行内容如titleA,
'sectionID2:rowID2':第二组第二行内容如titleA,
...
}
//放每组id
sectionIDs=[sectionID1,sectionID2,...]
//每个数组放每组每行id
rowIDs=[[rowIDs1,rowIDs2,...],[rowIDs1,rowIDs2,...]]
5.页面结构
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow} // 渲染每行的数据有参数rowData(行数据),sectionID(组id),rowID(行id)
renderSectionHeader={this.renderSectionHeader} // 渲染每组的数据有参数sectionData(组数据),sectionID(组id)
/>
TabBarIOS:底部导航,只在ios中有效,项目中会使用第三方库兼容安卓和ios,TabBarIOS.Item每项
<TabBarIOS
barTintColor='orange' //背景颜色
tintColor = 'purple' //选中项颜色
>
{/*第一块*/}
<TabBarIOS.Item
systemIcon="downloads" // 系统提供图标和图标标题
icon="xxx" //自定义图标,systemIcon图标优先级更高
title="张三" //自定义图标标题,systemIcon图标标题优先级更高
badge="3" //图标右上方小红色气泡内容
selected={this.state.selectedTabBarItem == 'home'} // true表示被选中,false相反
onPress = {()=>{this.setState({selectedTabBarItem: 'home'})}} // 点击事件
>
// 选中时展示的内容,View的样式会加到TabBarIOS结构所占的位置
<View style={[styles.commonViewStyle,{backgroundColor:'red'}]}>
<Text>首页</Text>
</View>
</TabBarIOS.Item>
</TabBarIOS>
NavigatorIOS:头部导航,只在ios中有效
<NavigatorIOS
// 初始化路由内容
initialRoute = {
{
component: Home, // 路由
title:'网易', // 标题
passProps:{}, //传递的参数
leftButtonIcon:require('image!navigationbar_friendattention'), //左图标
rightButtonIcon:require('image!navigationbar_pop'), //右图标
tintColor = "orange", //左右图标颜色
}
}
/>
Navigator:Navigator与NavigatorIOS一样都是用来做路由切换的,Navigator在ios和安卓都兼容,NavigatorIOS在加载路由时自带头部导航,Navigator需要自己写
<Navigator
initialRoute={{name:'首页',component:Home}} // 路由信息
configureScene={()=>{
return Navigator.SceneConfigs.PushFromRight; // 路由进来的动画形式,PushFromRight从左到右
}}
renderScene={(route,navigator)=>{ // 渲染路由
let Component = route.component;
return <Component {...route.passProps} navigator={navigator}/>;
}}
/>
5.一份代码在ios和安卓上运行
1)创建一个js文件
import React, { Component } from 'react';
class loginView extends Component {
render(){}
}
const styles = StyleSheet.create({})
module.exports = loginView;
2)index.ios.js和index.android.js文件中
import React, { Component } from 'react';
import {
AppRegistry,
} from 'react-native';
// 引入外部的js文件
var LoginView = require('./loginView');
class BTextInputDemo extends Component {
render() {
return (
<LoginView />
);
}
}
AppRegistry.registerComponent('BTextInputDemo', () => BTextInputDemo);
6.适配
var Dimensions = require('Dimensions');
var {width,height} = Dimensions.get('window');
loginBtnStyle:{
width:width*0.9,
}
7.组件state和prop属性
1)es5定义
import React, { Component } from 'react';
var DTouchabelDemo = React.createClass({
// 定义prop属性
getDefaultProps(){
return{
age: 18
}
},
propTypes:{
age:React.PropTypes.string // 定义prop属性类型
},
// 定义state,this.setState()更新state
getInitialState(){
return{
title:'不透明触摸',
}
},
render() {
return (
<View ref="topView" style={styles.container}>
<View>
<Text>{this.state.title}</Text>
<Text>{this.props.age}</Text>
</View>
</View>
);
},
});
2)es6定义
class BTextInputDemo extends Component {
constructor(props){
super(props)
// 现在可能可以在constructor外面定义
this.state = {
title:'不透明触摸',
}
}
render() {
return (
<View>
// 获取
<Text>{this.state.title}</Text>
</View>
);
}
}
// es6定义prop属性是定义在类外面,现在可能可以定义在类里面了
BTextInputDemo.defaultProps={title:'不透明触摸'}
BTextInputDemo.propTypes={
title:React.PropTypes.string // 定义prop属性类型
},
7.组件prop属性
class BTextInputDemo extends Component {
// prop的值不可以改变
getDefaultProps(){
return{
age: 18
}
},
render() {
return (
<View>
// 获取
<Text>{this.props.age}</Text>
</View>
);
}
}
8.事件
onPress:点击
onPressIn:按下
onPressOut:抬起
onLongPress:长按
9.创建组件
1)es5方式
import React, { Component } from 'react';
var DTouchabelDemo = React.createClass({
// 不可改变的值
getDefaultProps(){
return{
age: 18
}
},
render() {
return (
<View ref="topView" style={styles.container}>
</View>
);
}
});
2)es6方式
import React, { Component } from 'react';
import {
AppRegistry,
} from 'react-native';
// 引入外部的js文件
var LoginView = require('./loginView');
class BTextInputDemo extends Component {
render() {
return (
<LoginView />
);
}
}
AppRegistry.registerComponent('BTextInputDemo', () => BTextInputDemo);
10)获取真实的dom
// 获取this.refs.event
<Text ref="event">常用的事件</Text>
11)使用第三方库
npm i react-timer-mixin
// 引入计时器类库
var TimerMixin = require('react-timer-mixin');
var FScrollViewDemo1 = React.createClass({
// 注册计时器
mixins: [TimerMixin]
})
12)放多个样式
<Text key={i} style={[{fontSize:25},{fontSize:25}]}></Text>
13)事件对象
onAnimationEnd(e){
// e.nativeEvent为事件对象
// 求出水平方向的偏移量
var offSetX = e.nativeEvent.contentOffset.x;
}
14)路由
// 跳转到下一个路由
// 要和Navigator组件一起使用,参数就是Navigator组件的initialRoute
// 使用Navigator父组件将navigator对象传下去,才能继续调用路由api,调用api跳转的路由不需要在向下传navigator对象
<Navigator
initialRoute={{name:'首页',component:Home}} // 路由信息
configureScene={()=>{
return Navigator.SceneConfigs.PushFromRight; // 路由进来的动画形式,PushFromRight从左到右
}}
renderScene={(route,navigator)=>{ // 渲染路由
let Component = route.component;
return <Component {...route.passProps} navigator={navigator}/>;
}}
/>
this.props.navigator.push({
component: NewsDetail, //组件
title: rowData.title, // 标题
passProps:{rowData} // 参数,this.props.rowData拿到参数
})
this.props.navigator.pop():返回
this.props.navigator.replace():替代
15)电商实战
1.使用react-native-tab-navigator库完成兼容ios和安卓的底部导航功能(就是TabBarIOS组件)
2.使用react-native-tab-navigator库和Navigator实现底部路由切换和头部
3.完成更多、我的、首页、商家功能