ReactNative 仿美团项目
今天公司iOS项目完成了,在闲暇时候回顾一下上半年写过的ReactNative 仿美团项目,感觉好久好久没有再动过ReactNative混合模式了,对于ReactNative中ES6很是熟练,在这段时间几乎每天都用Flex ES6语法糖来写手机端页面,对于web前端手机页面也有了一定的了解,为了ReactNative项目能够更加熟练的编写,也积累了很多前端知识,学习永无止境,但是实战项目是使自己增长最快的方法,今天为大家分享ReactNative 仿美团项目,和大家一起学习。
一、第一部分直接上代码,先了解下项目结构
1、仿美团项目:设置标签栏和导航栏
导航栏和标签栏.png核心代码:
JavaScript
var LBRNMain = React.createClass({
getInitialState(){
return {
selectedTab:'Home'
}
},
render() {
return (
<TabNavigator>
{this.childNavigetor('首页','Home','icon_tabbar_homepage','icon_tabbar_homepage_selected','Home',LBRNHome)}
{this.childNavigetor('商家','EB','icon_tabbar_merchant_normal','icon_tabbar_merchant_selected','EB',LBRNEB)}
{this.childNavigetor('我的','Mine','icon_tabbar_mine','icon_tabbar_mine_selected','Mine',LBRNMine)}
{this.childNavigetor('更多','More','icon_tabbar_misc','icon_tabbar_misc_selected','More',LBRNMore)}
</TabNavigator>
);
},
childNavigetor(title,tabName,normalImage, selectedImage,componentName,component){
return (
<TabNavigator.Item
selectedTitleStyle={styles.selectedTextStyle}
selected={this.state.selectedTab === tabName}
title={title}
renderIcon={() => <Image source={{uri:normalImage}} style={styles.iconStyle} />}
renderSelectedIcon={() => <Image source={{uri:selectedImage}} style={styles.iconStyle} />}
onPress={() => this.setState({ selectedTab: componentName })}>
<Navigator
initialRoute={{ name: componentName, component: component }}
configureScene={(route) => {
return Navigator.SceneConfigs.PushFromRight;
}}
renderScene={(route, navigator) => {
let Component = route.component;
return <Component {...route.passProps} navigator={navigator} />
}}
/>
</TabNavigator.Item>
)
}
})
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
iconStyle:{
width:28,
height:28
},
selectedTextStyle:{
color:'#fb6320'
}
});
module.exports = LBRNMain;
2、 加载启动图片
核心代码:
JavaScript
var LBRNLaunchImage = React.createClass({
render(){
return(
<Image source={{uri:'welcome.png'}} style={styles.container} />
)
},
//增加定时器、请求网络数据
componentDidMount(){
setTimeout(()=>{
this.props.navigator.replace({
component:LBRNMain
})
},1000)
}
})
const styles=StyleSheet.create({
container:{
flex:1
}
})
module.exports=LBRNLaunchImage;
3、更多模块实现
① 错误解决
解决办法:cd进入项目根目录执行如下安装命令
react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/
② 正确截图
zongduan.png③ 错误截图
错误截图.png4、导入文件方式
第一种方式
import SmallMiddleView from './SmallMiddleView.js';
第二种方式
SmallMiddleView = require('SmallMiddleView');
5、我的模块
我的界面.png核心代码:
JavaScript
var width = require('Dimensions').get('window').width;
var LBRNMineCell = require('./LBRNMineCell');
var LBRNMine = React.createClass({
render(){
return (
<View style={styles.container}>
<View style={styles.backgroundStyle}>
</View>
<View style={{position:'absolute',marginTop:-300}}>
<ScrollView>
<View style={{flexDirection:'row',justifyContent:'space-between',alignItems:'center',marginTop:30}}>
<View style={{flexDirection:'row',alignItems:'center',backgroundColor:'#FB6320'}}>
<Image source={{uri:'wm'}} style={{width: 80,height:80,borderRadius:40}}/>
<Text style={{color:'red',fontSize:18}}>LBRNMeiTuan</Text>
<Image source={{uri:'avatar_vip'}} style={{width: 20,height:20,marginLeft:5}}/>
</View>
<TouchableOpacity>
<Image source={{uri:'icon_cell_rightarrow'}} style={{width: 20,height:20,marginRight:10}}/>
</TouchableOpacity>
</View>
{/*美团劵、评价、收藏*/}
<View style={{height:60,width:width,backgroundColor:'#FC8135',flexDirection:'row',marginTop: 20}}>
{this.renderViewMethod('500','美团劵')}
<View style={{backgroundColor:'#ddd',height:56,marginTop:2,width:0.5}}></View>
{this.renderViewMethod('500','评价')}
<View style={{backgroundColor:'#ddd',height:56,marginTop:2,width:0.5}}></View>
{this.renderViewMethod('500','收藏')}
</View>
{/*待付款、待使用、待评价、退货/售后*/}
<View style={{height:80,width:width,backgroundColor:'white',flexDirection:'row'}}>
{this.renderImageMethod('order1','待付款')}
{this.renderImageMethod('order2','待使用')}
{this.renderImageMethod('order3','待评价')}
{this.renderImageMethod('order4','退货/售后')}
</View>
<View style={{height:20,width:width,backgroundColor: '#DCDCDB'}}>
</View>
<View>
<LBRNMineCell
title='我的钱包'
iconLeftString='pay'
arrowString='icon_cell_rightarrow'
// rightImageString=''
rightText='帐户余额:¥8452'
/>
<LBRNMineCell
title='抵用券'
iconLeftString='card'
arrowString='icon_cell_rightarrow'
rightImageString='icon_hot'
// rightText=''
/>
</View>
<View style={{height:20,width:width,backgroundColor: '#DCDCDB'}}>
</View>
<View >
<LBRNMineCell
title='积分商城'
iconLeftString='card'
arrowString='icon_cell_rightarrow'
rightImageString='icon_hot'
// rightText=''
/>
</View>
<View style={{height:20,width:width,backgroundColor: '#DCDCDB'}}>
</View>
<View>
<LBRNMineCell
title='今日推荐'
iconLeftString='like'
arrowString='icon_cell_rightarrow'
rightImageString='icon_hot'
// rightText=''
/>
</View>
<View style={{height:20,width:width,backgroundColor: '#DCDCDB'}}>
</View>
<View>
<LBRNMineCell
title='我要合作'
iconLeftString='new_friend'
arrowString='icon_cell_rightarrow'
// rightImageString='icon_hot'
rightText='轻松开店,招财进宝'
/>
</View>
<View style={{height:100,width:width,backgroundColor: '#DCDCDB'}}>
</View>
</ScrollView>
</View>
</View>
);
},
renderViewMethod(count,name){
return(
<View style={{justifyContent:'center',width:width/3.0,alignItems:'center'}}>
<Text style={{color:'white'}}>{count}</Text>
<Text style={{color:'white',marginTop:7}}>{name}</Text>
</View>
)
},
renderImageMethod(imageString,name){
return(
<View style={{justifyContent:'center',width:width/4.0,alignItems:'center'}}>
<Image source={{uri:imageString}} style={{width:40,height:30}}/>
<Text style={{color:'#bbb',marginTop:7}}>{name}</Text>
</View>
)
}
}
)
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#DCDCDB',
},
backgroundStyle:{
width:width,
height:300,
backgroundColor:'#FB6320',
position:'absolute',
top:0
}
});
module.exports = LBRNMine;
6、商家模块
商家.png核心代码:
JavaScript
var width = require('Dimensions').get('window').width;
var LBRNEB = React.createClass({
getDefaultProps(){
return{
urlString: 'http://i.meituan.com/topic/mingdian?ci=1&f=iphone&msid=48E2B810-805D-4821-9CDD-D5C9E01BC98A2015-07-02-16-25124&token=p19ukJltGhla4y5Jryb1jgCdKjsAAAAAsgAAADHFD3UYGxaY2FlFPQXQj2wCyCrhhn7VVB-KpG_U3-clHlvsLM8JRrnZK35y8UU3DQ&userid=10086&utm_campaign=AgroupBgroupD100Fab_chunceshishuju__a__a___b1junglehomepagecatesort__b__leftflow___ab_gxhceshi__nostrategy__leftflow___ab_gxhceshi0202__b__a___ab_pindaochangsha__a__leftflow___ab_xinkeceshi__b__leftflow___ab_gxtest__gd__leftflow___ab_waimaiwending__a__a___ab_gxh_82__nostrategy__leftflow___i_group_5_2_deallist_poitype__d__d___ab_b_food_57_purepoilist_extinfo__a__a___ab_pindaoshenyang__a__leftflow___ab_pindaoquxincelue0630__b__b1___a20141120nanning__m1__leftflow___ab_i_group_5_3_poidetaildeallist__a__b___ab_waimaizhanshi__b__b1___ab_i_group_5_5_onsite__b__b___ab_i_group_5_6_searchkuang__a__leftflowGhomepage_bargainmiddle_30311731&utm_content=4B8C0B46F5B0527D55EA292904FD7E12E48FB7BEA8DF50BFE7828AF7F20BB08D&utm_medium=iphone&utm_source=AppStore&utm_term=5.7&uuid=4B8C0B46F5B0527D55EA292904FD7E12E48FB7BEA8DF50BFE7828AF7F20BB08D&version_name=5.7&lat=23.12005&lng=113.3076'
}
},
getInitialState(){
return{
urlString: 'http://i.meituan.com/topic/mingdian?ci=1&f=iphone&msid=48E2B810-805D-4821-9CDD-D5C9E01BC98A2015-07-02-16-25124&token=p19ukJltGhla4y5Jryb1jgCdKjsAAAAAsgAAADHFD3UYGxaY2FlFPQXQj2wCyCrhhn7VVB-KpG_U3-clHlvsLM8JRrnZK35y8UU3DQ&userid=10086&utm_campaign=AgroupBgroupD100Fab_chunceshishuju__a__a___b1junglehomepagecatesort__b__leftflow___ab_gxhceshi__nostrategy__leftflow___ab_gxhceshi0202__b__a___ab_pindaochangsha__a__leftflow___ab_xinkeceshi__b__leftflow___ab_gxtest__gd__leftflow___ab_waimaiwending__a__a___ab_gxh_82__nostrategy__leftflow___i_group_5_2_deallist_poitype__d__d___ab_b_food_57_purepoilist_extinfo__a__a___ab_pindaoshenyang__a__leftflow___ab_pindaoquxincelue0630__b__b1___a20141120nanning__m1__leftflow___ab_i_group_5_3_poidetaildeallist__a__b___ab_waimaizhanshi__b__b1___ab_i_group_5_5_onsite__b__b___ab_i_group_5_6_searchkuang__a__leftflowGhomepage_bargainmiddle_30311731&utm_content=4B8C0B46F5B0527D55EA292904FD7E12E48FB7BEA8DF50BFE7828AF7F20BB08D&utm_medium=iphone&utm_source=AppStore&utm_term=5.7&uuid=4B8C0B46F5B0527D55EA292904FD7E12E48FB7BEA8DF50BFE7828AF7F20BB08D&version_name=5.7&lat=23.12005&lng=113.3076'
}
},
render(){
return (
<View style={styles.container}>
{this.renderNavbarMethod()}
{this.webViewMethod()}
</View>
);
},
renderNavbarMethod(){
return(
<View style={styles.ebStyleStyle}>
<TouchableOpacity style={{marginTop:20,marginLeft:10}}>
<Image source={{url:'icon_shop_local'}} style={styles.toolBarIcon}/>
</TouchableOpacity>
<View >
<Text style={{color: 'white',fontSize:18,marginTop:28,fontWeight:'bold'}}>商家</Text>
</View>
<TouchableOpacity style={{marginTop:20,marginRight:10}}>
<Image source={{url:'icon_shop_search'}} style={styles.toolBarIcon}/>
</TouchableOpacity>
</View>
)
},
webViewMethod(){
return(
<WebView
automaticallyAdjustContentInsets={true}
source={{uri: this.state.urlString}}
javaScriptEnabled={true}
domStorageEnabled={true}
decelerationRate="normal"
startInLoadingState={true}
/>
)
}
}
)
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
ebStyleStyle:{
width:width,
height:Platform.OS === 'ios' ? 64 : 44,
backgroundColor:'#FB6320',
flexDirection:'row',
justifyContent:'space-between',
},
toolBarIcon:{
width:30,
height:30,
}
});
module.exports = LBRNEB;
7、首页上部视图
topView.png核心代码:
JavaScript
// 当滚动动画结束之后调用此回调
onScrollAnimationEnd(event){
// Math.floor(x)获取不大于x的最大整数
var page = Math.floor(event.nativeEvent.contentOffset.x/width);
console.log(page);
this.setState({
currentPage:page
})
},
renderScrollViewMethod(){
var dataCount = homeTopMenuData;
var array = [];
for (var i=0;i<dataCount.length;i++){
array.push(
<LBRNHomeTopListView
dataArray={homeTopMenuData[i]}
key={i}
/>
)
}
return array;
},
indicatorMethod(){
var colorsArray = [], bullColor;
//• 圆点
for(var i=0;i<2;i++){
bullColor = i == this.state.currentPage ? 'orange' : '#bbb';
colorsArray.push(
<Text style={[{fontSize:22,paddingRight:3}, style={color:bullColor}]} key={i}>•</Text>
)
}
return colorsArray;
}
var LBRNHomeTopListView = React.createClass({
getDefaultProps(){
return{
dataArray:[]
}
},
getInitialState(){
let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
return{
dataSource : ds.cloneWithRows(this.props.dataArray)
}
},
render(){
return(
<View>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
contentContainerStyle={styles.contentStyle}
scrollEnabled={false}//设置ListView不滑动
/>
</View>
)
},
//cell
renderRow(rowData){
return(
<View style={{width:imageWH,height:imageWH,justifyContent:'center',alignItems:'center'}}>
<Image source={{uri:rowData.image}} style={{width:imageWH - 20,height:imageWH - 20}}/>
<Text>{rowData.title}</Text>
</View>
)
}
})
注意事项:
JavaScript
// 底部如果是ScrollView,那么根节点就是ScrollView,不要把根节点设置为View
<ScrollView>
<LBRNHomeTopView />
</ScrollView>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
contentContainerStyle={styles.contentStyle}
scrollEnabled={false}
/>
// 要使ListView换行,要设置ListView宽度
contentStyle:{
flexDirection:'row',
flexWrap:'wrap',
width:width
}
8、首页中间组件以及购物中心组件
购物中心.png核心代码:
JavaScript
var LBRNHomeShopCenterView = React.createClass({
getDefaultProps(){
return{
dataArray:[]
}
},
getInitialState(){
let dataSource = new ListView.DataSource({rowHasChanged:(row1,row2) => row1 !== row2});
return{
dataSource:dataSource.cloneWithRows(this.props.dataArray)
}
},
render(){
return(
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderCenterRow}
contentContainerStyle={styles.contentStyle}
/>
)
},
//cell
renderCenterRow(rowData,sectionID,rowID){
var imageMarginRight = 0;
if (this.props.dataArray.length-1 == rowID){
imageMarginRight = 10;
}
return(
<View >
<View style={styles.container}>
<Image source={{uri:rowData.img}} style={{width:130,height:97,borderRadius:10,marginRight:imageMarginRight}}/>
<Text style={{color:'white',position:'absolute',bottom:20,backgroundColor:'red',fontSize:16}}>{rowData.showtext.text}</Text>
</View>
<View >
<Text style={{fontSize:14,fontWeight:'bold', color:'#bbb',marginTop:5,marginLeft:10,marginBottom:5}}>{rowData.name}</Text>
</View>
</View>
)
}
})
const styles = StyleSheet.create({
container:{
backgroundColor:'white',
marginLeft:10
},
contentStyle:{
flexDirection:'row',
}
})
module.exports=LBRNHomeShopCenterView;
9、购物中心详情
购物中心详情页面使用了回调函数、逆向传值和正向传值
核心代码:
JavaScript
getDefaultProps(){
return{
dataArray:[],
popToHome:null
}
},
urlMethod(url){
///处理url
var httpUrl=url.replace('imeituan://www.meituan.com/web/?url=','');
if (httpUrl == null)return;
this.props.popToHome(httpUrl);
//AlertIOS.alert(httpUrl);
}
<LBRNHomeShopCenterView
dataArray={homeShopCenter.data}
popToHome={(url)=>this.popToHome(url)}///回调函数
/>
<WebView
automaticallyAdjustContentInsets={true}
source={{uri: this.state.detailUrl}}
javaScriptEnabled={true}
domStorageEnabled={true}
decelerationRate="normal"
startInLoadingState={true}
/>
10、热门中心模块
热门中心模块和购物中心类似
核心代码:
var width = require('Dimensions').get('window').width;
var widthView;
var LBRNHomeHotView = React.createClass({
getDefaultProps(){
return{
data:[],
popToHot:null
}
},
getInitialState(){
let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
if(this.props.data.length == 2){
widthView = width/2.0;
}else {
widthView = width/4;
}
return{
dataSource : ds.cloneWithRows(this.props.data)
}
},
render(){
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
contentContainerStyle={styles.contentStyle}
/>
);
},
renderRow(rowData){
return(
<View>
<TouchableOpacity activeOpacity={0.5} onPress={()=>this.pressMethod(rowData.target)} style={[styles.touchableStyle,{borderRightWidth: 0.5,borderTopWidth: 0.5,borderRightColor:'#bbb',borderTopColor:'#bbb',width:widthView}]}>
<Text style={{fontSize:14,color:'black',marginBottom:5}}>{rowData.title}</Text>
<Text style={{fontSize:10,color:'orange',marginBottom:5}}>{rowData.subTitle}</Text>
<Image source={{uri:rowData.hotImage}} style={{width:50,height:50,borderRadius:25}}/>
</TouchableOpacity>
</View>
)
},
pressMethod(target){
if (target.length == 0)return;
this.props.popToHot(target.replace('imeituan://www.meituan.com/web?url=',''));
}
}
)
const styles = StyleSheet.create({
contentStyle:{
flexDirection:'row'
},
touchableStyle:{
justifyContent:'center',
alignItems:'center',
paddingTop:10,
paddingBottom:10
}
});
module.exports = LBRNHomeHotView;
11、猜你喜欢
猜你喜欢使用了回调函数、逆向传值和正向传值
核心代码:
dataMethod(){
fetch(this.props.api_url)
.then((responder)=>responder.json())
.then(responderData=>{
this.setState({dataSource:this.state.dataSource.cloneWithRows(responderData.data)})
})
.catch((error) => {
this.setState({dataSource:this.state.dataSource.cloneWithRows(homeBottomData.data)})
});
}
dealWithUrl(url){
if (url.search('w.h') == -1){
return url.replace('.webp','');
}else {
return url.replace('w.h','120.90')
}
}
二、第二部分项目运行
在github上面下载代码直接使用命令行git clone https://github.com/lb2281075105/LBRNMeiTuan.git
,直接运行可能显示错误,需要执行下面的命令才能运行:
1、首先:【npm install】
2、其次:【react-native-link】
3、最后:【react-native run-ios】
4、如果有什么问题可以github直接issue给我,或者留言给我,一起学习,一起讨论
三、第三部分学习心得
学习ReactNative开发,感觉编写代码心里很舒畅,用JSX语句开发App,很爽快,自学ReactNative,有时间看看ReactNative中文网站资源,就拿美团
作为练手项目,也是第一个在github上面开源项目,接下来也会深入学习,写更多的开源项目来提升自己的能力,也会不断更新笔记。