RN实战开发

08-首页

2021-08-25  本文已影响0人  Right01

学习内容

根据网络数据结构,定义数据模型

//header icon 模型
export interface HeaderItemIconModel 
{
    title: string,
    coverPath: string,
    darkCoverPath: string,
    properties: HeaderItemIconUrlModel,
}
//header Icon 模型 的 properties 模型
export interface HeaderItemIconUrlModel
{
    uri: string,
}

类目Icon组件封装

interface IProps {
    iconList: HeaderItemIconModel[],
}
class Icon extends React.Component<IProps> {
    renderItem = ({ item }: { item: HeaderItemIconModel }, parallaxProps?: AdditionalParallaxProps) => {
        return (
            <View style={styles.item}>
                <Image source={{uri: item.coverPath}} style={styles.itemImage}/>
                <Text style={styles.itemTitle} numberOfLines={1}>
                    {item.title}
                </Text>
            </View>
        );
    }
    render() {
        const { iconList } = this.props;
        if (iconList != null && iconList.length > 0) {
            return (
                <View style={styles.container}>
                    {/* <Text>{JSON.stringify(iconList)}</Text> */}
                    <FlatList
                        data={iconList}
                        renderItem={this.renderItem}
                        numColumns={5}
                    />
                </View>
            );
        }
        else {
            return null;
        }
    }
}

const styles = StyleSheet.create({
    container: {
        // backgroundColor: '#fff',
        borderRadius: 8,
        marginTop: 5,
        margin: 16,
    },
    item: {
        flex : 1,
        marginHorizontal: 5,
        marginVertical: 6,
    },
    itemImage: {
        width: '100%',
        height: 70,
        borderRadius: 50,
        marginBottom: 5,
    },
    itemTitle: {
        textAlign: 'center',
        fontSize: 14,
        color: '#333333',
        marginBottom: 5,
    }
});
export default Icon;
render() {
        ...
        var icons: HeaderItemIconModel[] = [];
        header.forEach(element => {
            ...
            else if (element.item.moduleType == 'square' && element.item.list.length > 0) {
                //广告Icon
                icons = element.item.list;
            }
        });
        return (
            <View>
                ...
                <Icon iconList={icons}/>
            </View>
        );
}

ps: 当然样式上实现了,但是点击事件都没有做

import React from "react";
import { TouchableOpacity, TouchableOpacityProps } from "react-native";
const Touchable: React.FC<TouchableOpacityProps>= (props) => (
    <TouchableOpacity activeOpacity={0.8} {...props} />
)
export default Touchable;
return (
    <Touchable
        style={styles.item}
        onPress={() => {
              Alert.alert(item.properties.uri);
        }}
    >
         ...
    </Touchable>
);

底部猜你喜欢模块

interface IProps {
    data: HomeBodyItemModel;
}

class GuessItem extends React.Component<IProps> {
    render() {
        const { data } = this.props;
        // console.log("_________:", data.intro);
        return (
            <Touchable
                style={styles.container}
                onPress={() => {
                    Alert.alert(data.title);
                }}
            >
                <Image source={{ uri: data.coverPath }} style={styles.image} />
                <View>
                    <Text style={styles.title}>{data.title}</Text>
                    <Text style={styles.subTitle} numberOfLines={1}>{data.intro}</Text>
                    <View style={styles.playContainer}>
                        <IconBofang2 color='#999999' />
                        <Text style={styles.playNum}>{numberChange(data.playsCounts)}</Text>
                    </View>
                </View>
                <IconClose color='#999999' style={styles.close} />
            </Touchable>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flexDirection: 'row',
        marginLeft: 15,
        marginRight: 15,
        marginBottom: 10,
        // backgroundColor: '#ffffff',
    },
    image: {
        width: 80,
        height: 80,
        borderRadius: 12,
        backgroundColor: '#dedede',
    },
    title: {
        marginTop: 10,
        marginLeft: 10,
        fontSize: 16,
        fontWeight: 'bold',
    },
    subTitle: {
        marginLeft: 10,
        marginTop: 10,
        marginRight: 5,
        width: viewPortWidth - 165,
    },
    playContainer: {
        flexDirection: 'row',
        marginLeft: 6,
        // backgroundColor: 'red',
        marginTop: 10,
    },
    playNum: {
        marginLeft: 2,
        color: '#999999',
    },
    close: {
        marginTop: 10,
    }
});
export default GuessItem;
renderItem = ({ item }: { item: HomeBodyModel }, parallaxProps?: AdditionalParallaxProps) => {
        return (
            <GuessItem data={item.item} />
        );
}

全屏滚动

  get header() {
        const { header, body } = this.props;
        var banner: HeaerItemBannerModel[] = [];
        var icons: HeaderItemIconModel[] = [];

        header.forEach(element => {

            if (element.item.moduleType == 'focus' && element.item.list.length > 0) {
                //banner广告
                element.item.list.forEach((ele: HeaerItemBanner) => {
                    banner = ele.data;
                });
            }
            else if (element.item.moduleType == 'square' && element.item.list.length > 0) {
                //广告Icon
                icons = element.item.list;
            }

        });

        return (
            <View>
                <Banner banner={banner} />
                <Icon iconList={icons} />
                {/* <GuessTitle guessData={body} /> */}
            </View>
        );
    }

    get footer() {
        return (
            <View>
                <Text>加载中...</Text>
            </View>
        );
    }

    get empty() {
        return (
            <View>
                <Text style={styles.textStyle}>正在加载中...</Text>
            </View>
        );
    }

        return (

            <FlatList
                ListHeaderComponent={this.header}
                ListFooterComponent={this.footer}
                numColumns={1}
                ListEmptyComponent={this.empty}

                data={body} renderItem={this.renderItem}
            >

            </FlatList>
        );
image.png

上拉加载更多

修改src/pages/Home/Home.tsx

onEndReached={this.onEndReached}
onEndReachedThreshold={0.2}  //比例:距离内容底部多远开始掉接口
//加载更多
    onEndReached = () => {
        console.log("加载更多");
        const { dispatch, loading, hasMore } = this.props
        if (loading || !hasMore) return;
        dispatch({
            type: "home/fetchGuess",
            payload: {
                loadMore: true,
            },
        });
    }

修改src/models/http.ts,添加接口调用

      *fetchGuess({ callback, payload }, { call, put, select }) {
            const home = yield select((state: RootState) => state.home);

            let page = 1;
            if (payload && payload.loadMore) {
                page = home.pagenation.current + 1;
            }

            const { data, pagenation } = yield call(axios.get, GUESS_PAGE_URL, {
                params: {
                    page,
                }
            });

            let newData = data;
            if (payload && payload.loadMore) {
                newData = home.body.concat(newData);
            }
            console.log("___________数据个数:", newData.length);
            yield put({
                type: 'setState',
                payload: {
                    body: newData,
                    pagenation: {
                        current: pagenation.current,
                        total: pagenation.total,
                        hasMore: newData.length < pagenation.total,
                    }
                }
            });
            if (typeof callback == 'function') {
                callback();
            }
        },
    },

下拉刷新

修改src/pages/Home/Home.tsx


interface IState {
    refreshing: boolean;
}

/**
 * 首页类
 */
class Home extends React.Component<IProps, IState> {

    state = {
        refreshing: false,
    };
    ...
    //下拉加载更多
    onRefresh = () => {
        console.log("下拉刷新");
        //1.修改刷新状态为 true
        this.setState({
            refreshing: true,
        });

        //2.获取数据
        const { dispatch } = this.props;
        dispatch({
            type: "home/fetchHome",
            //3.修改刷新状态为false
            callback: () => {
                this.setState({
                    refreshing: false,
                })
            }
        });
    }
    ...
   
    render() {
        const { body } = this.props;
        const { refreshing } = this.state;
        return (
            <FlatList
               ...
                onRefresh={this.onRefresh}
                refreshing={refreshing}
                ...
            >
            </FlatList>
        );
    }
}

底部视图处理

    get footer() {
        const { body, hasMore, loading } = this.props;
        if (!hasMore) {
            return (
                <View style={styles.footerView}>
                    <Text style={styles.footerStyle}>--- 我是有底线的 ---</Text>
                </View>
            );
        }
        if (loading && hasMore && body.length > 0) {
            return (
                <View style={styles.footerView}>
                    <Text style={styles.footerLoadingStyle}>正在加载中...</Text>
                </View>
            );
        }
    }

赋值到 FlatList 的 ListFooterComponent

导航栏顶部自定义标签

首页沉浸式头部

封装顶部top导航栏

导航栏布局

导航栏背景渐变

ps:一步一个脚印👣,up~

上一篇 下一篇

猜你喜欢

热点阅读