React Native (四):加载新闻列表

React Native (一):基础
React Native (二):StatusBar 、 NavigationBar 与 TabBar
React Native (三):自定义视图
React Native (四):加载新闻列表
React Native (五):上下拉刷新加载
React Native (六):加载所有分类与详情页

1.标签与内容页联动

上一节做到了点击标签自动移动,还差跟下面的视图进行联动。

首先创建 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 (
            

            
        )
    }
}

const styles = StyleSheet.create({
    view: {
        flex: 1,
        backgroundColor:'red'
    }
})

然后在 Home.js 引入,再加入 ScrollView ,现在 Home.jsredner() 是这样子的,这里加入的 ScrollView 我们在后文中称为 NewsScrollView

render() {
        return (
            
                

                

                
                    { this._getNewsLists()}
                
           
        )
    }


_getNewsLists() 方法:

_getNewsLists() {
    let lists = []
    if (this.state.list) {
        for (let index in this.state.list) {
            let dic = this.state.list[index]
            lists.push(
                
            )
        }
    }

    return lists
}

_getColor(color, index) {

    index ++

    if (index == 7) {
        return color
    }

    color = color + '0123456789abcdef'[Math.floor(Math.random()*16)]
    return  this._getColor(color, index)
}

根据返回的数据创建对应数量的视图,给随机颜色方便看效果。

先设置滑动 NewsScrollView 让标签跟着移动。

我们把 SegmentedViewitems.push 中的 onPress 方法的实现单独写到一个方法里,然后在这里调用:

_moveTo(index) {
    const { list } = this.props  //获取到 传入的数组

    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})
    }
}

这里会发现我们给 this.state 加了一个 itemWidth ,原来我们获取 itemWidth 是在 _getItems() 中计算的,但是在渲染的过程中无法调用 setState() ,我们把计算 itemWidth 的方法移动到 :

componentWillReceiveProps(props) {
    const { list } = props  //获取到 传入的数组
    if (!list || list.length == 0) return

    // 计算每个标签的宽度
    let itemWidth = width / list.length

    if (list.length > maxItem) {
        itemWidth = width / maxItem
    }

    this.setState({
        itemWidth
    })
}
    

componentWillReceiveProps(props) 方法会在属性更新后调用,参数 props 是新的属性。

现在运行会发现点击标签可以正常改变标签的状态,然而拖动 NewsScrollView 只会让上一个选中的变为未选中,新的标签并没有变为选中,这是因为选中状态只在标签被点击的时候进行了设置,我们需要给 Item 添加一个选中的方法 :

 _select() {
    this.setState({
        isSelect: true
    })
}

然后在 _moveTo(index) 进行调用:

this.state.selectItem && this.state.selectItem._unSelect()
this.state.selectItem = this.refs[index]
this.state.selectItem._select()

现在运行滑动 NewsScrollView 上面的 SegmentedView 可以正常运行了。

最后设置点击标签可以让 NewsScrollView 滑动到对应的位置,我们需要给 SegmentedView 加入一个回调函数,在标签被点击的时候调用返回点击的 index

 {
        this.refs.ScrollView.scrollTo({x: width * index, y: 0, animated: true})
    }}
/>

SegmentedView 进行调用:

_getItems() {
    const { list, selectItem } = this.props  //获取到 传入的数组

    if (!list || list.length == 0) return []

    let items = []
    for (let index in list) {
        let dic = list[index]
        items.push(
             {
                    this._moveTo(index)
                    selectItem && selectItem(index)
                }}
            />
        )
    }



    return items
}

2.加载新闻列表第一页数据

Home.js 中已经给 NewsList 传入了数据,我们再给传入一个参数识别是否是第一页,初始只加载第一页的数据,也方便调试:

_getNewsLists() {
    let lists = []
    if (this.state.list) {
        for (let index in this.state.list) {
            let dic = this.state.list[index]
            lists.push(
                
            )
        }
    }

    return lists
}

然后去 NewsList.js 进行请求数据:


 // 构造
constructor(props) {
    super(props);
    // 初始状态
    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)
            })
    }
}

请求到数据后我们需要用 ListView (官方文档) 来显示, 所以导入 ListView ,然后去 render() 加入:

render() {
    const {style} = this.props
    return (
        
            
        
    )
}

然后加入 dataSourcerenderRow:

// 构造
constructor(props) {
    super(props);

    var getRowData = (dataBlob, sectionID, rowID) => {

        return dataBlob[sectionID][rowID]
    };
    // 初始状态
    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 (
        
    )
}

我们要做的界面是这个样子

React Native (四):加载新闻列表_第1张图片

从上图可以看出来新闻分为 3 种样式,轮播图、有一张图片的和二、三张图片的。

接下来开始解析数据,解析完 json 数据发现只有一个数组,轮播图是取了前四个,剩下的根据 ImagesList 里图片的个数来判断,

.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)
})

现在 news 的数据结构为:

[
    [
        {},
        {}
    ],
    
    {},
    {}
}

然后去 renderRow 处理数据

如果是数组,那么返回轮播图:

if (Object.prototype.toString.call(rowData) === '[object Array]') {
    return (
        
        
    )
}

这里的轮播图本来用的 Swiper,但是在 Android 上有很多 BUG,我只好自己写了一个,但是在 Android 上的体验差强人意,源码在这里,把文件导入项目即可。

具体的可以看这里

touchIn 是由于在 Andoird 上两个 ScrollView 重叠时,处于顶部的 ScrollView 滑动事件不会响应,因为底部的 ScrollView 进行了响应并拦截了事件,我们需要在手指接触到轮播图的时候禁用底部 ScrollView 的滑动属性,再手指离开的时候再进行恢复,所以还需要去 Home.js 加入:

 _getNewsLists() {
    let lists = []
    if (this.state.list) {
        for (let index in this.state.list) {
            let dic = this.state.list[index]
            lists.push(
                 {
                        this.refs.ScrollView.setNativeProps({scrollEnabled: !scrollEnabled})
                    }}
                />
            )
        }
    }
  return lists
}

然后根据 ImagesList 的个数来区分:

let imagesList = rowData.ImagesList

if (imagesList && imagesList.length == 1) {
    return (
        
            
                

                
                    {rowData.Title}
                    
                        {rowData.Source}
                        {rowData.PublishTime}
                    
                
            
            
        
    )
}

let images = []

for (let index in imagesList) {
    let dic = imagesList[index]
    images.push(
        
    )
}

return (
    

        
            {rowData.Title}
        
        
            {images}
        
        
            {rowData.Source}
            {rowData.PublishTime}
        
        
    
)

我这里的 style 没有进行整理,所以看着比较乱,正式开发中应该整理到 styles 里,看起来就简洁多了。

现在运行就可以显示第一页的数据了。

下篇文章处理上下拉刷新加载。

你可能感兴趣的:(React Native (四):加载新闻列表)