APP結構探索
我在Github上找到了一個有登陸界面,能從網上獲取新聞信息的開源APP,想來研究一下APP的結構。
附上原網址:我的第一個React Native App
具體來講,就是研究一個復雜功能的APP在編寫時是如何一步一步展開的,包括APP內部邏輯、與UI交互、與服務器交互等。
index.android.js
首先,我找到了index.android.js
文件。文件很短,內容如下:
/**
* News
* author : lufeng
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
var Route = require("./web_modules/route");
AppRegistry.registerComponent('News', () => Route);
可以看到,關鍵代碼只有從"./web_modules/route"
調用的Route
,這也是整個程序的入口。
route.js
順藤摸瓜找到了route.js
,代碼比之前長了一點,如下:
/*
* 頁面路由
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
BackAndroid,
Navigator,
Image,
Text,
View,
StatusBar
} from 'react-native';
var Tab = require("./tab");
var _navigator;
var Routes = React.createClass({
configureScence: function(){
return Navigator.SceneConfigs.FloatFromRight;
},
renderScene: function(route, navigator){
_navigator = navigator;
let Component = route.component;
return <Component route={route} navigator={navigator}/>
},
render: function(){
var renderScene = this.renderSceneAndroid;
var configureScence = this.configureScenceAndroid;
return (
<Navigator
initialRoute={{ name: '主頁', component:Tab }}
configureScene={ this.configureScence }
renderScene={ this.renderScene}
/>
);
}
});
//監聽硬件的back鍵操作
BackAndroid.addEventListener('hardwareBackPress', function() {
if(_navigator == null){
return false;
}
if(_navigator.getCurrentRoutes().length === 1){
return false;
}
_navigator.pop();
return true;
});
module.exports = Routes;
route.js
文件里的結構仍然很清晰,主要有三部分:
- 注冊了名為
Routes
的組件。 - 監聽
back
鍵操作並進行相應處理。 - 導出
Routes
。
React
允許將代碼封裝成組件(component
),然后像插入普通 HTML
標簽一樣,在網頁中插入這個組件。React.createClass
方法就用於生成一個組件類。
Navigator
這里涉及到Navigator
的一些知識:
Navigator
即導航器,通過導航器我們可以實現應用在不同的頁面之間進行跳轉。
導航器會建立一個路由棧,用來彈出,推入或者替換路由狀態。該路由棧相當於Android
原生中使用的任務棧。
renderScene
renderScene
方法就相當於我們使用的startActivity
方法了,我們可以在該方法中設置需要渲染的場景(跳轉的頁面),該方法接收兩個參數(必要參數),route
和navigator
,其中route
就是路由的頁面,navigator
是一個Navigator
對象,因為Navigator
對象擁有pop
,push
,jump
等方法,我們需要導航器對象來實現頁面的跳轉。而路由route
需要我們提前進行配置。
configureScene
該方法用來設置指定路由對象的配置信息,從而改變場景的動畫或者手勢。如:
configureScene={(route) => {
//跳轉的動畫
return Navigator.SceneConfigs.VerticalDownSwipeJump;
}}
initialRoute
用來設置默認頁面,也就是啟動APP看到的第一個頁面,需要兩個參數,name
和component
。
可以看到,這里設置的默認頁面來自Tab
。而Tab
來自"./tab"
,我們打開tab.js
,重頭戲好像來了!!因為我看到了很多寫頁面的代碼。。。
tab.js
先貼代碼:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Image,
Text,
View,
StatusBar
} from 'react-native';
import TabNavigator from 'react-native-tab-navigator'; //引入tabbar支持包
const TabNavigatorItem =TabNavigator.Item;
const TAB_ITEM = [{
code : 'Home',
name : '首頁',
icon_n : require('./images/tab/tab_messagecenter_n.png'),
icon_p : require('./images/tab/tab_messagecenter_p.png'),
contentView : require('./tab_home')
},{
code : 'Suggest',
name : '推薦',
icon_n : require('./images/tab/tab_contact_n.png'),
icon_p : require('./images/tab/tab_contact_p.png'),
contentView : require('./tab_suggest')
},{
code : 'Find',
name : '發現',
icon_n : require('./images/tab/tab_discovery_n.png'),
icon_p : require('./images/tab/tab_discovery_p.png'),
contentView : require('./tab_find')
},{
code : 'Mine',
name : '我的',
icon_n : require('./images/tab/tab_myself_n.png'),
icon_p : require('./images/tab/tab_myself_p.png'),
contentView : require('./tab_mine')
}];
class Tab extends Component {
constructor(props){
super(props);
this.state = {
items : TAB_ITEM,
selectedTab : TAB_ITEM[0].code
}
}
/*
* tab點擊方法
*/
onPress(tabCode){
if(tabCode){
this.setState({
selectedTab : tabCode
});
}
}
/*
*渲染每項
*/
renderTabView(options){
var tabNomal = options.icon_n;
var tabPress = options.icon_p;
return(
<TabNavigatorItem
key={options.code}
title={options.name}
renderIcon={()=><Image style={styles.tabIcon} source={tabNomal}/>}
renderSelectedIcon={()=><Image style={styles.tabIcon} source={tabPress}/>}
selected={this.state.selectedTab === options.code}
selectedTitleStyle={{color:'#333333'}}
onPress={()=>this.onPress(options.code)}
renderBadge= {()=>options.badgeNum?<View style={styles.badgeView}>
<Text style={styles.badgeText}>options.badgeNum</Text>
</View>:null}>
<options.contentView route={this.props.route} navigator={this.props.navigator}/>
</TabNavigatorItem>
);
}
render() {
var items = [];
for (var i=0; i< this.state.items.length; i++) {
items.push(this.renderTabView(this.state.items[i]));
}
return (
<View style={styles.container}>
<TabNavigator tabBarStyle={styles.tab}>
{items}
</TabNavigator>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
tab:{
height: 55,
alignItems:'center',
backgroundColor:'#f4f5f6',
},
tabIcon:{
width:25,
height:25,
},
badgeView:{
width:22,
height:14 ,
backgroundColor:'#f85959',
borderWidth:1,
marginLeft:10,
marginTop:3,
borderColor:'#FFF',
alignItems:'center',
justifyContent:'center',
borderRadius:8,
},
badgeText:{
color:'#fff',
fontSize:8,
}
});
module.exports = Tab;
這里看的比較費力。。先是創建了一個名為tab
的類,然后將界面底部的所有項的信息作為TAB_ITEM
傳到tab
的constructor
中。接着是onPress()
方法作為tab
的點擊方法,后面的renderTabView
方法的注釋中標明了是渲染每項,其中又調用了TabNavigatorItem
組件,這個組件來自Github
。最后的render()
里就是加載了整個布局。
TabNavigator
Tab組件(底部導航):react-native-tab-navigator
https://github.com/exponentjs/react-native-tab-navigator
TAB_ITEM
這里以tab_home.js
為例,其他的類似。核心代碼如下:
class Tab_Home extends Component {
constructor(props){
super(props);
}
showActionSheet(){
this.ActionSheet.show();
}
render() {
return (
<View style={{flex:1,justifyContent:'flex-start',alignItems:'center', bottom : 5}}>
<View style={styles.header}>
<View style={styles.header_btn}></View>
<View style={styles.header_center}>
<Text style={styles.header_title}>新聞網</Text>
</View>
<TouchableOpacity onPress={this.showActionSheet.bind(this)}>
<Image source={require('./images/header/more.png')} style={styles.header_icon,styles.header_btn}/>
</TouchableOpacity>
</View>
<Swiper style={styles.wrapper} showsButtons={false} autoplay={false} height={200} showsPagination={true}>
<Image style={styles.slide} source={ require('./images/demo/slideshow1.jpg') }></Image>
<Image style={styles.slide} source={ require('./images/demo/slideshow2.jpg') }></Image>
<Image style={styles.slide} source={ require('./images/demo/slideshow3.jpg') }></Image>
<Image style={styles.slide} source={ require('./images/demo/slideshow4.jpg') }></Image>
<Image style={styles.slide} source={ require('./images/demo/slideshow5.jpg') }></Image>
</Swiper>
<List dataNums="6" route={this.props.route} navigator={this.props.navigator}/>
<ActionSheet
ref={(o) => this.ActionSheet = o}
title="進入新聞推薦分類!"
options={buttons}
cancelButtonIndex={0}
destructiveButtonIndex={1}
/>
</View>
);
}
}
這個類主要是首頁的布局,涉及到多種組件。其中比較值得一提的是TouchableOpacity
、Swiper
和ActionSheet
。
TouchableOpacity
本組件用於封裝視圖,使其可以正確響應觸摸操作。當按下的時候,封裝的視圖的不透明度會降低。這個過程並不會真正改變視圖層級,大部分情況下很容易添加到應用中而不會帶來一些奇怪的副作用。(譯注:此組件與TouchableHighlight
的區別在於並沒有額外的顏色變化,更適於一般場景)
這里此組件的onPress()
事件的處理是彈出ActionSheet
菜單。
Swiper
用於組件(常見的是圖片)的輪播。
官方文檔:https://github.com/leecade/react-native-swiper
ActionSheet
從頁面底部彈出菜單,擁有標題等屬性,常用於應用的退出。本例是選擇欄目用。(雖然沒有實現具體的功能)
官方文檔:https://github.com/beefe/react-native-actionsheet
今天就先到這啦。明天繼續看~~
感謝以下幾篇博客: