40

ReactNative学习笔记(五)之ListView

 5 years ago
source link: https://mundane799699.github.io/2018/09/08/react-native-note5/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

接上一篇 ReactNative学习笔记(四)之自定义文本组件与Image组件 继续学习ReactNative

ListView

ListView就是列表视图, 一个用来显示垂直滚动的内容列表.它需要两个东西, dataSource, 就是数据源.和renderRow, 用来显示每一行内容用的模板.

首先导入ListView

import {Platform, StyleSheet, Text, View, Image, ImageBackground, ListView} from 'react-native';

然后在App这个类中添加一个constructor方法

export default class App extends Component<Props> {
    constructor(props) {
        super(props)
        let movies = [
            {'title': '肖申克的救赎'},
            {'title': '这个杀手不太冷'},
            {'title': '阿甘正传'},
            {'title': '霸王别姬'},
            {'title': '美丽人生'},
            {'title': '三傻大闹宝莱坞'},
            {'title': '触不可及'},
            {'title': '时空恋旅人'},
            {'title': '我和狗狗的十个约定'},
        ];

        let dataSource = new ListView.DataSource({
            rowHasChanged: (row1, row2) => row1 !== row2
        });

        this.state = {
            movies: dataSource.cloneWithRows(movies)
        };
    }
    ...
}

constructor方法里需要先调用super(props)方法. 我们手工添加一些数据.ListView本身有一种很有效的方法去显示列表的数据, 而且我们要确定重新显示列表内容的时候只重新显示修改过的数据, 所以需要一个对比数据的方法, 也就是rowHasChanged方法.

再去设置组件初始化的状态, 把这个状态作为ListView的数据源.

然后我们再修改App的render方法

export default class App extends Component<Props> {
    ...
    render() {
        return (
            <View
                style={styles.container}>
                <ListView
                    dataSource={this.state.movies}
                    renderRow={
                        movie => <Text style={styles.itemText}>{movie.title}</Text>
                    }
                />
            </View>
        );
    }
}

运行效果:

A3MRraR.png!web

从网络获取数据并重新整理列表的显示

首先需要从网络请求数据, 我们使用的api是这个 https://api.douban.com/v2/movie/top250 , 在App这个类里面写一个fetchData方法

fetchData() {
    fetch(REQUEST_URL)
        .then(response => response.json())
        .then(responseData => {
            this.setState({
                movies: this.state.movies.cloneWithRows(responseData.subjects),
                loaded: true
            })
        })
        .done();
}

对了, App的构造函数的的state中需要加一个loaded变量, 用来标记数据是否已经加载完成.在fetchData方法中, 先将数据转json, 然后我们使用的数据是subjects这部分数据作为数据源

ENFzqyI.png!web

在得到数据之后将state中的loaded属性设置为true.

然后我们修改App的render方法

render() {
    if(!this.state.loaded) {
        return (
            <View style={styles.container}>
                <View style={styles.loading}>
                    <Text>加载中...</Text>
                </View>

            </View>
        );
    }
    return (
        <View style={styles.container}>
            <ListView
                dataSource={this.state.movies}
                renderRow={this.renderMovieList}
            />
        </View>
    );
}

在state为false的时候显示一个加载中的视图, 如果state为true, 那就显示一个ListView, 数据源就是this.state中的movies, 模板就是一个renderMovieList方法

renderMovieList(movie) {
    return (
        <View style={styles.item}>
            <View style={styles.itemImage}>
                <Image
                    source={{uri: movie.images.large}}
                    style={styles.image}
                />
            </View>
            <View style={styles.itemContent}>
                <Text style={styles.itemHeader}>{movie.title}</Text>
                <Text style={styles.itemMeta}>
                    {movie.original_title} ( {movie.year} )
                </Text>
                <Text style={styles.redText}>
                    {movie.rating.average}
                </Text>

            </View>
        </View>
    )
}

这里的样式经过整理了一下, 稍微有些繁琐, 代码如下:

let styles = StyleSheet.create({
    redText: {
        color: '#db2828',
        fontSize: 15,
    },
    itemMeta: {
        fontSize: 16,
        color: 'rgba(0, 0, 0, 0.6)',
        marginBottom: 6,
    },
    itemHeader: {
        fontSize: 18,
        fontFamily: 'Helvetica Neue',
        fontWeight: '300',
        color: '#6435c9',
        marginBottom: 6,
    },
    itemContent: {
        flex: 1,  // todo 这里的flex等于1到底有什么用
        marginLeft: 13,
        marginTop: 6,
    },
    item: {
        flexDirection: 'row',
        borderBottomWidth: 1,
        borderColor: 'rgba(100, 53, 201, 0.1)',
        paddingBottom: 6,
        marginBottom: 6,
        flex: 1, //
    },
    loading: {
        flex: 1,  // todo 这里的flex等于1到底有什么用
        justifyContent: 'center',
        alignItems: 'center',
    },
    ...
});

运行效果:

z6BVRzy.png!web

注意如果把itemContent属性中的flex: 1注释掉, flex属性就会变成默认的stretch

效果如下:

3aeI7fZ.png!web

可以看到文字跑到外面去了, 这是因为, stretch是伸展、张开的意思, 如果文字太长就会将容纳文字的容器的宽度撑大, 也就是Android里的包裹内容的意思, 一撑就撑到屏幕外面去了, 而如果限制容器的宽度为剩余空间的宽度, 文字就会在到控件边缘的时候自动换行了, 不会超出控件的宽度.这点需要注意.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK