React Native (一):基礎(chǔ)
React Native (二):StatusBar 、 NavigationBar 與 TabBar
React Native (三):自定義視圖
React Native (四):加載新聞列表
React Native (五):上下拉刷新加載
React Native (六):加載所有分類與詳情頁
1.標(biāo)簽與內(nèi)容頁聯(lián)動(dòng)
上一節(jié)做到了點(diǎn)擊標(biāo)簽自動(dòng)移動(dòng)洼滚,還差跟下面的視圖進(jìn)行聯(lián)動(dòng)。
首先創(chuàng)建 NewsList.js
:
import React from 'react'
import {
View,
Text,
ListView,
Image,
StyleSheet,
Dimensions
} from 'react-native'
const {width, height} = Dimensions.get('window')
export default class NewsList extends React.Component {
render() {
const {style} = this.props
return (
<View style={[styles.view,style]}>
</View>
)
}
}
const styles = StyleSheet.create({
view: {
flex: 1,
backgroundColor:'red'
}
})
然后在 Home.js
引入宵蕉,再加入 ScrollView
,現(xiàn)在 Home.js
的 redner()
是這樣子的,這里加入的 ScrollView
我們?cè)诤笪闹蟹Q為 NewsScrollView
render() {
return (
<View style={styles.view}>
<NavigationBar
title="首頁"
unLeftImage={true}
/>
<SegmentedView
ref="SegmentedView"
list={this.state.list}
style={{height: 30}}
/>
<ScrollView
style={styles.view}
ref="ScrollView"
horizontal={true}
showsHorizontalScrollIndicator={false}
pagingEnabled={true}
>
{ this._getNewsLists()}
</ScrollView>
</View>
)
}
_getNewsLists()
方法:
_getNewsLists() {
let lists = []
if (this.state.list) {
for (let index in this.state.list) {
let dic = this.state.list[index]
lists.push(
<NewsList
key={index}
style={{backgroundColor:'#' + this._getColor('',0), width: width, height: height - 49 - 64 - 30}}
dic={dic}
/>
)
}
}
return lists
}
_getColor(color, index) {
index ++
if (index == 7) {
return color
}
color = color + '0123456789abcdef'[Math.floor(Math.random()*16)]
return this._getColor(color, index)
}
根據(jù)返回的數(shù)據(jù)創(chuàng)建對(duì)應(yīng)數(shù)量的視圖候醒,給隨機(jī)顏色方便看效果为牍。
先設(shè)置滑動(dòng) NewsScrollView
讓標(biāo)簽跟著移動(dòng)哼绑。
我們把 SegmentedView
中 items.push
中的 onPress
方法的實(shí)現(xiàn)單獨(dú)寫到一個(gè)方法里,然后在這里調(diào)用:
_moveTo(index) {
const { list } = this.props //獲取到 傳入的數(shù)組
this.state.selectItem && this.state.selectItem._unSelect()
this.state.selectItem = this.refs[index]
if (list.length > maxItem) {
let meiosis = parseInt(maxItem / 2)
this.refs.ScrollView.scrollTo({x: (index - meiosis < 0 ? 0 : index - meiosis > list.length - maxItem ? list.length - maxItem : index - meiosis ) * this.state.itemWidth, y: 0, animated: true})
}
}
這里會(huì)發(fā)現(xiàn)我們給 this.state
加了一個(gè) itemWidth
碉咆,原來我們獲取 itemWidth
是在 _getItems()
中計(jì)算的抖韩,但是在渲染的過程中無法調(diào)用 setState()
,我們把計(jì)算 itemWidth
的方法移動(dòng)到 :
componentWillReceiveProps(props) {
const { list } = props //獲取到 傳入的數(shù)組
if (!list || list.length == 0) return
// 計(jì)算每個(gè)標(biāo)簽的寬度
let itemWidth = width / list.length
if (list.length > maxItem) {
itemWidth = width / maxItem
}
this.setState({
itemWidth
})
}
componentWillReceiveProps(props)
方法會(huì)在屬性更新后調(diào)用疫铜,參數(shù) props
是新的屬性茂浮。
現(xiàn)在運(yùn)行會(huì)發(fā)現(xiàn)點(diǎn)擊標(biāo)簽可以正常改變標(biāo)簽的狀態(tài),然而拖動(dòng) NewsScrollView
只會(huì)讓上一個(gè)選中的變?yōu)槲催x中壳咕,新的標(biāo)簽并沒有變?yōu)檫x中励稳,這是因?yàn)檫x中狀態(tài)只在標(biāo)簽被點(diǎn)擊的時(shí)候進(jìn)行了設(shè)置,我們需要給 Item
添加一個(gè)選中的方法 :
_select() {
this.setState({
isSelect: true
})
}
然后在 _moveTo(index)
進(jìn)行調(diào)用:
this.state.selectItem && this.state.selectItem._unSelect()
this.state.selectItem = this.refs[index]
this.state.selectItem._select()
現(xiàn)在運(yùn)行滑動(dòng) NewsScrollView
上面的 SegmentedView
可以正常運(yùn)行了囱井。
最后設(shè)置點(diǎn)擊標(biāo)簽可以讓 NewsScrollView
滑動(dòng)到對(duì)應(yīng)的位置驹尼,我們需要給 SegmentedView
加入一個(gè)回調(diào)函數(shù),在標(biāo)簽被點(diǎn)擊的時(shí)候調(diào)用返回點(diǎn)擊的 index
<SegmentedView
ref="SegmentedView"
list={this.state.list}
style={{height: 30}}
selectItem={(index) => {
this.refs.ScrollView.scrollTo({x: width * index, y: 0, animated: true})
}}
/>
在 SegmentedView
進(jìn)行調(diào)用:
_getItems() {
const { list, selectItem } = this.props //獲取到 傳入的數(shù)組
if (!list || list.length == 0) return []
let items = []
for (let index in list) {
let dic = list[index]
items.push(
<Item
ref={index}
key={index}
isSelect={index == 0}
itemHeight={this.state.itemHeight}
itemWidth={this.state.itemWidth}
dic={dic}
onPress={() => {
this._moveTo(index)
selectItem && selectItem(index)
}}
/>
)
}
return items
}
2.加載新聞列表第一頁數(shù)據(jù)
在 Home.js
中已經(jīng)給 NewsList
傳入了數(shù)據(jù)庞呕,我們?cè)俳o傳入一個(gè)參數(shù)識(shí)別是否是第一頁新翎,初始只加載第一頁的數(shù)據(jù)程帕,也方便調(diào)試:
_getNewsLists() {
let lists = []
if (this.state.list) {
for (let index in this.state.list) {
let dic = this.state.list[index]
lists.push(
<NewsList
key={index}
style={{backgroundColor:'white'}}
dic={dic}
isRequest={index == 0}
/>
)
}
}
return lists
}
然后去 NewsList.js
進(jìn)行請(qǐng)求數(shù)據(jù):
// 構(gòu)造
constructor(props) {
super(props);
// 初始狀態(tài)
this.state = {
page: 1,
rn: 1,
};
}
componentDidMount() {
if (!this.props.isRequest) return
this._onRefresh()
}
_onRefresh(page) {
if (this.props.dic) {
let url = 'http://api.iapple123.com/newspush/list/index.html?clientid=1114283782&v=1.1&type='
+ this.props.dic.NameEN
+ '&startkey=&newkey=&index='
+ (page ? page : this.state.page)
+ '&size=20&ime=6271F554-7B2F-45DE-887E-4A336F64DEE6&apptypeid=ZJZYIOS1114283782&rn='
+ this.state.rn
LOG('url=》', url)
fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
})
.then((res) => {
res.json()
.then((json) => {
LOG('GET SUCCESSED then =>', url, json)
})
})
.catch((e) => {
LOG('GET ERROR then =>', url, e)
})
})
.catch((error) => {
LOG('GET ERROR=>', url, '==>', error)
})
}
}
請(qǐng)求到數(shù)據(jù)后我們需要用 ListView
(官方文檔) 來顯示, 所以導(dǎo)入 ListView
地啰,然后去 render()
加入:
render() {
const {style} = this.props
return (
<View style={[styles.view,style]}>
<ListView
style={{flex:1}}
dataSource={this.state.dataSource} //設(shè)置數(shù)據(jù)源
renderRow={this.renderRow} //設(shè)置cell
/>
</View>
)
}
然后加入 dataSource
和 renderRow
:
// 構(gòu)造
constructor(props) {
super(props);
var getRowData = (dataBlob, sectionID, rowID) => {
return dataBlob[sectionID][rowID]
};
// 初始狀態(tài)
this.state = {
page: 1,
rn: 1,
dataSource: new ListView.DataSource({
getRowData: getRowData,
rowHasChanged: (r1, r2) => r1 !== r2,
}),
};
this.renderRow = this.renderRow.bind(this)
}
renderRow(rowData, rowID, highlightRow) {
return (
<View />
)
}
我們要做的界面是這個(gè)樣子
從上圖可以看出來新聞分為 3 種樣式愁拭,輪播圖、有一張圖片的和二亏吝、三張圖片的岭埠。
接下來開始解析數(shù)據(jù),解析完 json
數(shù)據(jù)發(fā)現(xiàn)只有一個(gè)數(shù)組蔚鸥,輪播圖是取了前四個(gè)惜论,剩下的根據(jù) ImagesList
里圖片的個(gè)數(shù)來判斷,
去 .then((json) => {
加入
let list = json.NewsList
let swipers = []
let news = []
for (let index in list) {
let dic = list[index]
index < 4 ? swipers.push(dic) : news.push(dic)
}
news.splice(0, 0, swipers)
this.setState({
dataSource: this.state.dataSource.cloneWithRows(news)
})
現(xiàn)在 news
的數(shù)據(jù)結(jié)構(gòu)為:
[
[
{},
{}
],
{},
{}
}
然后去 renderRow
處理數(shù)據(jù)
如果是數(shù)組止喷,那么返回輪播圖:
if (Object.prototype.toString.call(rowData) === '[object Array]') {
return (
<CarousePicture
index={2}
ref="ScrollView"
rowData={rowData}
style={{width, height: 200}}
touchIn={this.props.touchIn}
>
</CarousePicture>
)
}
這里的輪播圖本來用的 Swiper
馆类,但是在 Android
上有很多 BUG,我只好自己寫了一個(gè)弹谁,但是在 Android
上的體驗(yàn)差強(qiáng)人意乾巧,源碼在這里,把文件導(dǎo)入項(xiàng)目即可预愤。
具體的可以看這里
touchIn
是由于在 Andoird
上兩個(gè) ScrollView
重疊時(shí)沟于,處于頂部的 ScrollView
滑動(dòng)事件不會(huì)響應(yīng),因?yàn)榈撞康?ScrollView
進(jìn)行了響應(yīng)并攔截了事件植康,我們需要在手指接觸到輪播圖的時(shí)候禁用底部 ScrollView
的滑動(dòng)屬性社裆,再手指離開的時(shí)候再進(jìn)行恢復(fù),所以還需要去 Home.js
加入:
_getNewsLists() {
let lists = []
if (this.state.list) {
for (let index in this.state.list) {
let dic = this.state.list[index]
lists.push(
<NewsList
key={index}
style={{backgroundColor:'white', width: width, height: height - 64 - 49 - 30}}
dic={dic}
isRequest={index == 0}
touchIn={(scrollEnabled) => {
this.refs.ScrollView.setNativeProps({scrollEnabled: !scrollEnabled})
}}
/>
)
}
}
return lists
}
然后根據(jù) ImagesList
的個(gè)數(shù)來區(qū)分:
let imagesList = rowData.ImagesList
if (imagesList && imagesList.length == 1) {
return (
<TouchableOpacity style={{width, backgroundColor:'white'}}>
<View
style={{width, backgroundColor:'white', flexDirection:'row', justifyContent:'space-between', flex:1}}>
<Image
resizeMode="cover"
style={{marginTop: 10, marginBottom:10, marginLeft: 10, width: 80, height: 80, backgroundColor:'#EEEEEE'}}
source={{uri:imagesList[0].ImgPath}}
/>
<View
style={{ marginRight: 10,backgroundColor:'white', marginTop: 10, height: 80, width: width - 110}}
>
<Text>{rowData.Title}</Text>
<View style={{flex:1, flexDirection: 'row', justifyContent: 'space-between'}}>
<Text style={{marginTop:10, fontSize: 13, color: '#999999'}}>{rowData.Source}</Text>
<Text style={{marginRight:0,marginTop:10,fontSize: 13, color: '#999999'}}>{rowData.PublishTime}</Text>
</View>
</View>
</View>
<View style={{width, height:1, backgroundColor: '#EEEEEE'}}></View>
</TouchableOpacity>
)
}
let images = []
for (let index in imagesList) {
let dic = imagesList[index]
images.push(
<Image
resizeMode="cover"
key={index}
style={{marginRight: 10, marginLeft: index == 0 ? 10 : 0, marginTop:10, marginBottom: 10,flex:1, height: 90}}
source={{uri:dic.ImgPath}}
/>
)
}
return (
<TouchableOpacity style={{width, backgroundColor:'white'}}>
<View style={{width,backgroundColor:'white'}}>
<Text style={{marginLeft: 10, marginTop: 10}}>{rowData.Title}</Text>
</View>
<View style={{flexDirection:'row'}}>
{images}
</View>
<View style={{flex:1, flexDirection: 'row', justifyContent: 'space-between'}}>
<Text style={{marginLeft: 10, marginBottom: 10,fontSize: 13, color: '#999999'}}>{rowData.Source}</Text>
<Text style={{marginRight:10,fontSize: 13, marginBottom: 10,color: '#999999'}}>{rowData.PublishTime}</Text>
</View>
<View style={{width, height:1, backgroundColor: '#EEEEEE'}}></View>
</TouchableOpacity>
)
我這里的 style
沒有進(jìn)行整理向图,所以看著比較亂,正式開發(fā)中應(yīng)該整理到 styles
里标沪,看起來就簡(jiǎn)潔多了榄攀。
現(xiàn)在運(yùn)行就可以顯示第一頁的數(shù)據(jù)了。
下篇文章處理上下拉刷新加載金句。