前言
眼看很多公司都开始尝试使用ReactNative,达到跨平台开发,最近也写了很多文章,希望让更多想了解的同学快速上手ReactNative.
如果喜欢我的文章,可以关注我微博:袁峥Seemygo
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
}
});