11-4 频道页面tab标签

26 阅读2分钟

在这一章中,我们将完成频道页面的标签组件,分别显示简介和节目列表。以下是实现步骤:

一、创建标签组件

  1. 安装 react-native-tab-view

    由于我们已经使用了 react-native-tab-view 作为顶部标签导航器的依赖库,因此无需再次安装,可以直接使用。

  2. 创建 Tab 组件

    Album 文件夹中创建 Tab.tsx 文件:

    import React from 'react';
    import { View, Text, StyleSheet } from 'react-native';
    import { TabView, SceneRendererProps } from 'react-native-tab-view';
    
    interface IRoute {
      key: string;
      title: string;
    }
    
    interface IState {
      routes: IRoute[];
      index: number;
    }
    
    class Tab extends React.Component {
      state: IState = {
        routes: [
          { key: 'introduction', title: '简介' },
          { key: 'albums', title: '节目' },
        ],
        index: 1,
      };
    
      onIndexChange = (index: number) => {
        this.setState({ index });
      };
    
      renderScene = ({ route }: { route: IRoute }) => {
        switch (route.key) {
          case 'introduction':
            return <Introduction />;
          case 'albums':
            return <List />;
        }
      };
    
      renderTabBar = (props: SceneRendererProps & { navigationState: IState }) => (
        <TabBar
          {...props}
          scrollEnabled
          tabStyle={styles.tabStyle}
          labelStyle={styles.label}
          style={styles.tabbar}
          indicatorStyle={styles.indicator}
        />
      );
    
      render() {
        return (
          <TabView
            navigationState={this.state}
            renderScene={this.renderScene}
            renderTabBar={this.renderTabBar}
            onIndexChange={this.onIndexChange}
          />
        );
      }
    }
    
    const styles = StyleSheet.create({
      tabStyle: {
        width: 80,
      },
      label: {
        color: '#333',
      },
      tabbar: {
        backgroundColor: '#fff',
        ...Platform.select({
          android: {
            elevation: 0,
            borderBottomWidth: StyleSheet.hairlineWidth,
            borderBottomColor: '#e3e3e3',
          },
        }),
      },
      indicator: {
        backgroundColor: '#eb6d48',
        borderColor: '#fff',
        borderLeftWidth: 15,
        borderRightWidth: 15,
      },
    });
    
    export default Tab;
    

二、创建简介组件

  1. 创建 Introduction 组件

    Album 文件夹中创建 Introduction.tsx 文件:

    import React from 'react';
    import { View, Text, StyleSheet, ScrollView } from 'react-native';
    import { connect, ConnectedProps } from 'react-redux';
    import { RootState } from '@/models/index';
    
    const mapStateToProps = ({ album }: RootState) => ({
      introduction: album.introduction,
    });
    
    const connector = connect(mapStateToProps);
    
    type ModelState = ConnectedProps<typeof connector>;
    
    class Introduction extends React.Component<ModelState> {
      render() {
        const { introduction } = this.props;
        return (
          <ScrollView style={styles.container}>
            <Text>{introduction}</Text>
          </ScrollView>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        padding: 10,
      },
    });
    
    export default connector(Introduction);
    

三、创建节目列表组件

  1. 创建 List 组件

    Album 文件夹中创建 List 文件夹,并在其中创建 index.tsx 文件:

    import React from 'react';
    import { FlatList, StyleSheet } from 'react-native';
    import { connect, ConnectedProps } from 'react-redux';
    import Item from './Item';
    import { IAlbum } from '@/models/album';
    import { RootState } from '@/models/index';
    
    const mapStateToProps = ({ album }: RootState) => ({
      list: album.list,
    });
    
    const connector = connect(mapStateToProps);
    
    type ModelState = ConnectedProps<typeof connector>;
    
    interface IProps extends ModelState {
      onItemPress: (item: IAlbum, index: number) => void;
    }
    
    class List extends React.Component<IProps> {
      renderItem = ({ item, index }: ListRenderItemInfo<IAlbum>) => {
        return <Item data={item} index={index} onPress={this.onPress} />;
      };
    
      onPress = (item: IAlbum, index: number) => {
        const { onItemPress } = this.props;
        onItemPress(item, index);
      };
    
      keyExtractor = (item: IAlbum) => item.id;
    
      render() {
        const { list } = this.props;
        return (
          <FlatList
            style={styles.container}
            data={list}
            renderItem={this.renderItem}
            keyExtractor={this.keyExtractor}
          />
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
      },
    });
    
    export default connector(List);
    
  2. 创建 Item 组件

    Album/List 文件夹中创建 Item.tsx 文件:

    import React from 'react';
    import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
    import Icon from '@/assets/iconfont/index';
    import { IAlbum } from '@/models/album';
    
    export interface IProps {
      data: IAlbum;
      index: number;
      onPress: (item: IAlbum, index: number) => void;
    }
    
    class Item extends React.Component<IProps> {
      onPress = () => {
        const { onPress, data, index } = this.props;
        onPress(data, index);
      };
    
      render() {
        const { data, index } = this.props;
        return (
          <TouchableOpacity style={styles.item} onPress={this.onPress}>
            <Text style={styles.serial}>{index + 1}</Text>
            <View style={styles.centerView}>
              <Text style={styles.title}>{data.title}</Text>
              <View style={styles.info}>
                <View style={styles.iconView}>
                  <Icon name="icon-V" color="#939393" />
                  <Text style={styles.iconText}>{data.playVolume}</Text>
                </View>
                <View style={styles.iconView}>
                  <Icon name="icon-shengyin" color="#939393" />
                  <Text style={styles.iconText}>{data.duration}</Text>
                </View>
              </View>
            </View>
            <Text style={styles.date}>{data.date}</Text>
          </TouchableOpacity>
        );
      }
    }
    
    const styles = StyleSheet.create({
      item: {
        flexDirection: 'row',
        padding: 20,
        borderBottomWidth: StyleSheet.hairlineWidth,
        borderBottomColor: '#dedede',
        justifyContent: 'center',
        alignItems: 'center',
      },
      serial: {
        color: '#838383',
        fontWeight: '800',
      },
      title: {
        fontWeight: '500',
        color: '#333',
        marginBottom: 15,
      },
      centerView: {
        flex: 1,
        marginHorizontal: 25,
      },
      info: {
        flexDirection: 'row',
      },
      iconView: {
        flexDirection: 'row',
        marginRight: 10,
      },
      iconText: {
        marginHorizontal: 5,
        color: '#939393',
        fontWeight: '100',
      },
      date: {
        color: '#939393',
        fontWeight: '100',
      },
    });
    
    export default Item;
    

四、更新频道页面

  1. 更新 Album 页面

    Album/index.tsx 中导入 Tab 组件,并在 render 方法中使用:

    import React from 'react';
    import { View, StyleSheet } from 'react-native';
    import Tab from './Tab';
    
    class Album extends React.Component {
      render() {
        return (
          <View style={styles.container}>
            {this.renderHeader()}
            <Tab />
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
      },
    });
    
    export default Album;
    

五、总结

在本节中,我们完成了频道页面的标签组件,分别显示简介和节目列表。通过使用 react-native-tab-view 库,我们实现了标签组件的布局和功能。下一节,我们将学习如何使用手势响应系统实现频道页面的交互效果。