從本章開始我們將一起來開發(fā)一個(gè)簡(jiǎn)單的微信精選列表應(yīng)用按厘。主要包括兩個(gè)界面,主界面是微信精選列表钱慢,點(diǎn)擊每一條信息可以進(jìn)入到詳細(xì)信息展示的界面逮京。通過這個(gè)應(yīng)用我們將學(xué)習(xí)如何從網(wǎng)絡(luò)獲取數(shù)據(jù)、怎樣對(duì)UI組件進(jìn)行布局及設(shè)置樣式束莫、以及常用UI組件的使用懒棉。
可以在React Native和React的官方網(wǎng)站查看到更多ReactNative及React的相關(guān)內(nèi)容。對(duì)應(yīng)的中文社區(qū):React Native與React览绿。
項(xiàng)目完整源碼
項(xiàng)目的完整源碼下載地址策严。
注意
一、教程以開放IOS應(yīng)用為基礎(chǔ)講解饿敲。
二妻导、涉及到的原生代碼采用Objective-C語言完成。
微信精選
單條內(nèi)容
可以將微信精選列表看做一個(gè)tableview怀各,那么每一條內(nèi)容就是一個(gè)cell倔韭。我們將從搭建單條內(nèi)容的界面開始講解。先看一下最終完成的樣子:
這個(gè)界面主要包括文本和圖片瓢对,采用文本堆疊在圖片之上的布局方式寿酌。
開發(fā)React Native應(yīng)用主要需要JavaScript、CSS和JSX三種技術(shù)硕蛹。其中JavaScript負(fù)責(zé)業(yè)務(wù)邏輯醇疼;CSS負(fù)責(zé)UI樣式和布局;JSX將UI組件封裝為標(biāo)簽語言妓美,使我們可以通過JSX標(biāo)簽構(gòu)建UI。React Native的這種開發(fā)模式借鑒了傳統(tǒng)的web開發(fā)鲤孵。這樣做主要的優(yōu)勢(shì)是可以將內(nèi)容和表現(xiàn)分離壶栋,JSX負(fù)責(zé)內(nèi)容,CSS負(fù)責(zé)表現(xiàn)普监。代碼邏輯更加清晰贵试。
JSX
相信大部分同學(xué)對(duì)于JavaScript和CSS肯定不陌生。如果沒有使用React的經(jīng)驗(yàn)可能不了解JSX是什么凯正。那么就來說一說JSX毙玻,先來看一段代碼:
<View>
<Text>
JSX,JavaScript語法擴(kuò)展
</Text>
</View>
上面就是一段JSX代碼廊散。
每一個(gè)UI閉合的標(biāo)簽都稱之為組件,<View/>
組件可以認(rèn)為代表UIView
桑滩,<Text/>
組件可以認(rèn)為代表UILabel
。ReactNative就是通過這些JSX組件與原生UI一一對(duì)應(yīng)起來的允睹。
JSX运准,本質(zhì)是JavaScript的語法擴(kuò)展幌氮。React在運(yùn)行期將JSX轉(zhuǎn)換為JavaScript代碼。
JSX以<
起始胁澳,以/>
結(jié)束的閉合標(biāo)簽该互。JSX級(jí)能夠解析HTML標(biāo)簽,也能夠解析React組件韭畸。規(guī)定HTML標(biāo)簽必須以小寫字母開頭表示宇智,React組件以大寫字母開頭。
JSX胰丁,在構(gòu)建ReactNative應(yīng)用并不是必須的随橘,也可以直接通過JavaScript創(chuàng)建UI組件。但是使用JSX能夠讓我們的代碼更加直觀清晰隘马,并且JSX支持與JavaScript混編太防,這也賦予其非常大的靈活性。
Flexbox彈性盒
搭建UI界面必須解決的一個(gè)問題就是屏幕適配的問題酸员。在IOS開發(fā)中我們使用autolayout進(jìn)行布局蜒车,ReactNative也為我們提供了它解決布局問題的技術(shù)——Flexbox彈性盒。Flexbox本身是CSS3的技術(shù)標(biāo)準(zhǔn)幔嗦,ReactNative因?yàn)槭褂肅SS進(jìn)行布局和樣式設(shè)置酿愧,所以也就順理成章的使用Flexbox彈性盒技術(shù)。
可以通過下面的代碼在組件中聲明樣式:
<View style = {styles.container}>
<Text style = {styles.content}>
JSX邀泉,JavaScript語法擴(kuò)展
</Text>
</View>
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
ReactNative并沒有支持全部的CSS屬性嬉挡,ReactNative支持的CSS屬性可以看這里。關(guān)于Flexbox彈性盒詳細(xì)的屬性可以查看這里汇恤。
關(guān)于Flexbox的教程推薦這兩篇文章:Flex 布局教程:語法篇庞钢、Flex 布局教程:實(shí)例篇。
編寫單條內(nèi)容界面
了解了JSX與Flexbox之后因谎,我們就可以正式開始編寫單條內(nèi)容的界面基括。下面是完整的代碼:
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
} from 'react-native'
class ReactNativeLearn extends Component {
render() {
return (
<View style={styles.container}>
<Image
style={{
width: 300,
height: 200,
}}
resizeMode={"contain"}
source={{uri:'http://facebook.github.io/react/img/logo_og.png'}}
/>
<Text
style={{
color: 'black',
fontSize: 16,
fontWeight: 'normal',
fontFamily: 'Helvetica Neue',
}}>
新聞內(nèi)容
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
AppRegistry.registerComponent('ReactNativeLearn', () => ReactNativeLearn);
啟動(dòng)Xcode,運(yùn)行工程:
可以看到我們已經(jīng)實(shí)現(xiàn)簡(jiǎn)單的上下布局的圖片和文本财岔。
現(xiàn)在我們來分析一下上面的代碼风皿。首先是'use strict'
,這是JavaScript的嚴(yán)格模式匠璧,在嚴(yán)格模式下一些可能會(huì)帶來安全隱患的JavaScript語言特性將被禁用(ES6標(biāo)準(zhǔn)Module默認(rèn)采用嚴(yán)格模式)桐款。然后是import React, { Component } from 'react'
這行代碼的意思是將React庫的Component組件導(dǎo)入到當(dāng)前作用域中。這是采用ES6標(biāo)準(zhǔn)的寫法夷恍。之后的代碼就是我們創(chuàng)建界面的主要代碼魔眨。我們可以看到render()
函數(shù),render是渲染的意思,我們?cè)趓ender函數(shù)中創(chuàng)建我們的UI組件渲染到屏幕冰沙。
關(guān)于組件的詳細(xì)信息可以在ReactNative官網(wǎng)找到侨艾。AppRegistry.registerComponent('ReactNativeLearn', () => ReactNativeLearn)
,表示將ReactNativeLearn組件作為整個(gè)ReactNative應(yīng)用的入口拓挥。ReactNativeLearn的名稱與AppDelegate中的moduleName對(duì)應(yīng):
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"ReactNativeLearn"
initialProperties:nil
launchOptions:launchOptions];
現(xiàn)在我們可以通過修改組件的style屬性和組件的包裹方式唠梨,實(shí)現(xiàn)我們想要的最終效果。代碼如下:
class ReactNativeLearn extends Component {
render() {
return (
<View style={styles.container}>
<Image
style={{
width: Dimensions.get('window').width - 40,
height: 300,
marginLeft: 20,
marginRight: 20,
marginTop: 10,
marginBottom: 10,
borderColor: "rgb(0,0,0)",
borderWidth: 2,
borderRadius: 8,
justifyContent: 'flex-end',
}}
resizeMode={"stretch"}
source={{uri:'http://facebook.github.io/react/img/logo_og.png'}}
>
<View
style={{
flex: 0,
justifyContent: 'flex-start',
alignItems: 'stretch',
backgroundColor: "rgba(0,0,0,0.4)",
}}>
<Text
style={{
color: "rgb(255,255,255)",
fontSize: 20,
fontWeight: 'normal',
fontFamily: 'Helvetica Neue',
marginLeft: 10,
marginRight: 10,
marginTop: 10,
marginBottom: 10,
}}
numberOfLines={3}
>
A React component for displaying different types of images, including network images, static resources, temporary local images, and images from local disk, such as the camera roll.
</Text>
</View>
</Image>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#F5FCFF',
},
});
最終效果:
我們不應(yīng)該將全部代碼都寫在index.ios.js文件侥啤,而是應(yīng)該分成不同的模塊当叭,方便管理。我們新建一個(gè)名為newsCell的js文件盖灸,將代碼復(fù)制到該文件當(dāng)中蚁鳖。最后將newsCell聲明為一個(gè)可以被其他模塊引用的組件:
export default NewsCell;
微信精選列表
完成單條內(nèi)容的UI搭建,現(xiàn)在我們就來實(shí)現(xiàn)列表的界面赁炎。在React Native應(yīng)用中列表通常使用ListView組件醉箕。ListView的功能類似于UITableView。先實(shí)現(xiàn)最基本的ListView徙垫,代碼如下:
class ReactNativeLearn extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
})
};
this.renderRow = this.renderRow.bind(this);
}
componentDidMount() {
this.fetchNewsData();
}
// 設(shè)置數(shù)據(jù)
fetchNewsData() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows([{'content':'ReactNativeLearn ListView'},
{'content':'ReactNativeLearn ListView'},
{'content':'ReactNativeLearn ListView'},
{'content':'ReactNativeLearn ListView'},
{'content':'ReactNativeLearn ListView'},
{'content':'ReactNativeLearn ListView'},
{'content':'ReactNativeLearn ListView'},
{'content':'ReactNativeLearn ListView'},
{'content':'ReactNativeLearn ListView'},
{'content':'ReactNativeLearn ListView'}])
});
}
// 渲染cell
renderRow(rowData) {
return (
<View
style={{
height: 80,
justifyContent: 'flex-start',
alignItems: 'stretch',
backgroundColor: "rgba(74,144,226,1)",
}}>
<Text
style={{
color: 'black',
fontSize: 28,
fontWeight: 'normal',
fontFamily: 'Helvetica Neue',
}}>
{rowData.content}
</Text>
</View>
);
}
// 渲染cell的分割
renderSeparator(
sectionID,
rowID
) {
return (
<View key = {'cell_'+ sectionID + '_' + rowID} style = {styles.rowSeparator}/>
);
}
render() {
return (
<View style={styles.container}>
<ListView
style = {styles.listContainer}
dataSource = {this.state.dataSource}
renderRow = {this.renderRow}
renderSeparator = {this.renderSeparator}
enableEmptySections={true}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#F5FCFF',
},
listContainer: {
flex:1,
marginTop: 64,
backgroundColor: 'rgb(237, 240, 235)',
},
rowSeparator: {
backgroundColor: 'rgb(223, 218, 223)',
height: 1,
},
});
最終效果:
現(xiàn)在我們已經(jīng)完成一個(gè)最簡(jiǎn)單的ListView讥裤。可以看到我們?yōu)長(zhǎng)istView組件設(shè)置了三個(gè)屬性:dataSource姻报,renderRow和renderSeparator己英。dataSource是ListView的數(shù)據(jù)源類似于UITableView的數(shù)據(jù)源方法,這里我們手動(dòng)設(shè)置了一個(gè)數(shù)組吴旋;renderRow接收一個(gè)渲染cell的函數(shù)回調(diào)损肛,類似于UITableView的cellForRowAtIndexPath
∪偕可以在這個(gè)函數(shù)里我們可以創(chuàng)建cell的UI界面治拿,即我們之前創(chuàng)建的單條內(nèi)容界面。還有一個(gè)renderSeparator笆焰,可以用于創(chuàng)建分割cell的UI劫谅。
目前為止ListView的每一個(gè)cell的數(shù)據(jù)都是我們手動(dòng)設(shè)置的。實(shí)際開發(fā)中仙辟,這些數(shù)據(jù)往往來自于服務(wù)器同波。下面我們來實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求鳄梅,代碼如下:
fetchNewsData() {
fetch(newsULR,{
headers: {
"apikey": "f589f2834aeab120eef2e750e4fb1dfb"
}
}).then((response) => response.json())
.catch((error) => {
console.error('error request!');
})
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.newslist)
});
})
.done();
}
React Native使用Fetch進(jìn)行網(wǎng)絡(luò)請(qǐng)求叠国。Fetch是新的異步網(wǎng)絡(luò)請(qǐng)求標(biāo)準(zhǔn),相較于AJAX提供更強(qiáng)大高效的網(wǎng)絡(luò)請(qǐng)求API戴尸。關(guān)于ReactNative的網(wǎng)絡(luò)內(nèi)容可以看這里粟焊。
我們已經(jīng)實(shí)現(xiàn)ListView的基本功能,現(xiàn)在需要將之前單條新聞的界面newsCell作為組件,添加到ListView中项棠,實(shí)現(xiàn)完整的列表悲雳。引入newsCell的方式與引入ReactNative原生組件的方式相同,都是通過import進(jìn)行導(dǎo)入香追。
import NewsCell from './News/newsCell';
我們可以通過下面的方式向NewsCell傳遞數(shù)據(jù)或者回調(diào)函數(shù):
renderRow(rowData) {
return (
<NewsCell newsData={rowData}/>
);
}
然后在NewsCell組件可以通過如下的方式獲得數(shù)據(jù):
{this.props.newsData.title}
到此我們完成完整的微信精選列表合瓢。最終列表界面:
像newsCell一樣,我們把列表也封裝為一個(gè)單獨(dú)的組件透典,命名為newsMain晴楔,然后直接在index.ios.js文件導(dǎo)入即可。