ReactNative — используйте FlatList для реализации списка фильмов Douban

React Native

В прошлой статье мы узнали об использовании ListView и реализовали три различных стиля представления списка. Хотя ListView широко используется, у него также есть много недостатков, таких как: он не поддерживает отдельные компоненты head и tail, когда объем данных слишком велик, значительно увеличивается занимаемая память, снижается производительность и пропускаются кадры. Таким образом, с итеративными обновлениями версии React Native ListView был заменен FlatList и SectionList. FlatList используется для несгруппированных списков, тогда как SectionList используется для реализации сгруппированных списков. FlatList и SectionList имеют следующие преимущества:

* 完全跨平台。
* 支持水平布局模式。
* 行组件显示或隐藏时可配置回调事件。
* 支持单独的头部组件。
* 支持单独的尾部组件。
* 支持自定义行间分隔线。
* 支持下拉刷新。
* 支持上拉加载。
* 支持跳转到指定行(ScrollToIndex)。

В этой статье мы научимся использовать FlatList для реализации представления списка, похожего на фильмы Douban, шаг за шагом. Прежде всего, нам все еще нужно понять связанные свойства FlatList. Вы можете обратиться к официальной документации веб-сайта. Объяснение очень подробное. Здесь описаны только несколько ключевых свойств.

data— Массив источника данных, который отличается от dataSource ListView, вы можете напрямую установить массив в атрибут данных в качестве источника данных FlatList, а API проще и удобнее.

keyExtractor——Функция используется для создания уникального ключа для каждого элемента в списке.Функция ключа состоит в том, чтобы позволить React различать разные элементы одного и того же элемента, чтобы можно было определить положение его изменения при его обновлении. , а накладные расходы на повторный рендеринг могут быть уменьшены. Если эта функция не указана, item.key извлекается по умолчанию как значение ключа. Если item.key не существует, используется индекс нижнего индекса массива. Это свойство недоступно в ListView, оно используется как уникальный идентификатор для каждого элемента в FlatList и SectionList.

renderItem- компонент, который отображает каждую строку на основе каждой строки данных в списке. Подобно RenderRow в ListView, разница в том, что складки должны указывать клавиш-сигнал, а RenderRow не нужен этот ключ.

С помощью трех вышеперечисленных свойств мы можем реализовать представление списка, но следующие свойства очень важны:

numColumns——Задание количества колонок в негоризонтальном режиме позволяет реализовать сеточное расположение элементов, что намного удобнее, чем ListView, и нет необходимости задавать flexWrap.

columnWrapperStyle- Если вы настроили макет с несколькими столбцами (то есть установили значение numColumns на целое число больше 1), вы можете дополнительно указать, что этот стиль применяется к каждому контейнеру строк. Например, у нас есть список-сетка с 4 строками и 3 столбцами, в каждой строке по 3 элемента, эти 3 элемента отображаются в одной строке, мы можем думать, что эти 3 элемента обернуты контейнером, свойство columnWrapperStyle должно устанавливать style для этого контейнера, который используется для настройки положения отображения этих трех элементов в контейнере, чтобы сделать пользовательский интерфейс более красивым.

onEndReachedThreshold- Определяет, как далеко запускается обратный вызов onEndReached, когда он находится далеко от нижней части содержимого. Обратите внимание, что этот параметр представляет собой соотношение, а не единицу измерения в пикселях. Например, 0,5 означает, что расстояние от нижней части содержимого равно половине видимой длины текущего списка. Значение находится в диапазоне от 0 до 1, исключая 0 и 1. Это следует четко отличать от ListView.

ОК, это некоторые из наиболее важных атрибутов, о которых следует упомянуть.Чтобы узнать о других атрибутах, обратитесь к официальной документации. Давайте посмотрим, как использовать FlatList для реализации списка фильмов, показанного на рисунке ниже.

Чтобы реализовать список фильмов, нам сначала нужны данные.Здесь мы используем открытый интерфейс Douban Movies для получения данных о фильмах.

First, the root directory is created in the project directory src, sequentially creating common, and widgets Screen src directory listings, respectively, used to store generic class, UI interface and custom UI components, to create a Service.js stored in the common interface адрес:

/// 查询正在上映的电影
export function queryMovies(city, start, count) {
  return "https://api.douban.com/v2/movie/in_theaters?city=" + city + "&start=" + start + "&count=" + count
}

/// 查询即将上映的电影
export function comingMovies(city, start, count) {
  return "https://api.douban.com/v2/movie/coming_soon?city=" + city + "&start=" + start + "&count=" + count
}

Среди них город — это город.Чтобы облегчить нам запись непосредственно как Пекин, параметр start указывает, с каких данных начинать (изначально начинается с 0), а count указывает, сколько фрагментов данных загружается каждый раз.

Определите файл Color.js, используемый для хранения значений цвета, необходимых для использования приложения.

export default {
  themeColor: '#268dcd', // 主题颜色
  separatorColor: '#e0e0e0', // 分割线颜色
  backgroundColor: '#f3f3f3' // 背景色
}

Чтобы добиться такого эффекта переключения нижней панели tabBar, мы используем react-navigation для завершения. Найдите корневую директорию проекта и войдите в терминалnpm install --save react-navigationкоманда для установки зависимых библиотек. В случае неудачи вы можете использоватьyarn add react-navigationЗаказ.

После завершения установки давайте создадим связанный интерфейс, создадим RootScreen, MovieListScreen в Screen Directory. Содержание мы можем сначала написать следующим образом:

import React, {Component} from 'react';
import {View} from 'react-native';

export default class MovieListScreen extends Component {
  
  render() {
    return (
      <View/>
    )
  }
}

Как показано на изображениях, у нас есть два интерфейса, которые в настоящее время отображаются и будут выпущены. Адреса двух интерфейсов соответствуют данным двух интерфейсов соответственно. После анализа данных, возвращаемых двумя интерфейсами, обнаружено, что структуры данных двух интерфейсов согласованы Да, поэтому здесь нам нужно только создать страницу MovieListScreen, два интерфейса совместно используют файл js для реализации, просто нужно различать вызываемый интерфейс, чтобы избежать дублирования кода.

У нас уже есть базовая реализация двух страниц фильмов, теперь нам нужен контейнер tabBar в качестве корневого представления для управления этими двумя страницами списка. Здесь вам нужно использовать реагирующую навигацию для реализации RootScreen.

Во-первых, мы собираемся создать контейнер вкладок с помощью компонента TabNavigator:

const Tab = TabNavigator(
  {
    First: {
      screen: MovieListScreen,
      navigationOptions: ({navigation}) => ({
        tabBarLabel: '正在热映',
        tabBarIcon: ({focused, tintColor}) => (
          <TabBarItemComponent
            tintColor={tintColor}
            focused={focused}
            normalImage={require('../../assets/image/playing.png')}
            selectedImage={require('../../assets/image/playing-active.png')}
          />
        )
      }),
      
    },
    Second: {
      screen: MovieListScreen,
      navigationOptions: ({navigation}) => ({
        tabBarLabel: '即将上映',
        tabBarIcon: ({focused, tintColor}) => (
          <TabBarItemComponent
            tintColor={tintColor}
            focused={focused}
            normalImage={require('../../assets/image/coming.png')}
            selectedImage={require('../../assets/image/coming-active.png')}
          />
        )
      })
    }
  },
  {
    tabBarComponent: TabBarBottom,
    tabBarPosition: 'bottom',
    swipeEnabled: false,
    animationEnabled: false,
    lazy: true,
    tabBarOptions: {
      activeTintColor: Color.themeColor,
      inactiveTintColor: '#888888',
      style: {backgroundColor: '#ffffff'}
    }
  }
);

Вы можете видеть, что экраны, соответствующие First и Second в контейнере, — это MovieListScreen. TabBarItemComponent — это настраиваемый компонент tabItem, используемый для отображения значков и текста. После создания контейнера вкладок нам также нужна панель навигации для отображения заголовка, и эта панель навигации также играет роль навигации по страницам, поэтому нам нужно использовать компонент StackNavigator в react-navigation:

const Navigator = StackNavigator(
  {
    Tab: {screen: Tab},
  },
  {
    navigationOptions: {
      headerBackTitle: null,
      headerTintColor: '#ffffff',
      headerStyle: {backgroundColor: Color.themeColor},
      showIcon: true
    }
  }
);

Далее в функции рендеринга в RootScreen нам нужно вернуть компонент Navigator, чтобы общая структура страницы была завершена

render() {
    return <Navigator/>
  }

Теперь мы получаем структуру страницы с панелью вкладок внизу и панелью навигации вверху, как показано ниже.

Настало время реализовать список фильмов.

В MovieListScreen нам нужно вызвать интерфейс для получения данных, а затем назначить данные компоненту FlatList после соответствующей обработки. Сначала создайте данные состояния в функции-конструкторе.

constructor(props) {
    super(props);
    this.state = {
      movieList: [],  // 电影列表的数据源
      loaded: false,  // 用来控制loading视图的显示,当数据加载完成,loading视图不再显示
    };
  }

Используйте функцию выборки, чтобы вызвать интерфейс для получения данных.Здесь метод загрузки данных фильма, который показывается, выглядит следующим образом:

/**
   * 加载正在上映的电影列表,此处默认城市为北京,取20条数据显示
   */
  loadDisplayingMovies() {
    let that = this;
    fetch(queryMovies('北京', 0, 20)).then((response) => response.json()).then((json) => {
      console.log(json);
      let movies = [];
      for (let idx in json.subjects) {
        let movieItem = json.subjects[idx];
        let directors = ""; // 导演
        for (let index in movieItem.directors) {
          // 得到每一条电影的数据
          let director = movieItem.directors[index];
          // 将多个导演的名字用空格分隔开显示
          if (directors === "") {
            directors = directors + director.name
          } else {
            directors = directors + " " + director.name
          }
        }
        movieItem["directorNames"] = directors;
        
        // 拼装主演的演员名字,多个名字用空格分隔显示
        let actors = "";
        for (let index in movieItem.casts) {
          let actor = movieItem.casts[index];
          if (actors === "") {
            actors = actors + actor.name
          } else {
            actors = actors + " " + actor.name
          }
        }
        movieItem["actorNames"] = actors;
        movies.push(movieItem)
      }
      that.setState({
        movieList: movies,
        loaded: true
      })
    }).catch((e) => {
      console.log("加载失败");
      that.setState({
        loaded: true
      })
    }).done();
  }

В componentDidMount определите, какой интерфейс вызывать для получения соответствующих данных в соответствии с текущей страницей.

componentDidMount() {
  /// 根据routeName来判断当前是哪个界面,react-navigation中可以通过navigation.state.routeName来获取
  let routeName = this.props.navigation.state.routeName;
  if (routeName === 'First') {
    this.loadDisplayingMovies();
  } else {
    this.loadComingMovies();
  }
}

Следующее - визуализировать страницу в функции рендеринга.

render() {
  if (!this.state.loaded) {
    return (
      <View style={styles.loadingView}>
        <ActivityIndicator animating={true} size="small"/>
        <Text style={{color: '#666666', paddingLeft: 10}}>努力加载中</Text>
      </View>
    )
  }
  return (
    <FlatList
      data={this.state.movieList}
      renderItem={this._renderItem}
      keyExtractor={(item) => item.id}
    />
  )
}

Перед окончанием запроса интерфейса мы сначала визуализируем нагрузку на загрузку, чтобы напомнить пользователю, что он загружается. После завершения интерфейса установите нагруженное поле в True, чтобы рендер напрямую отобразил список сплав и использует идентификатор Из фильма как уникальный идентификатор для каждой строки. KeyExtractor. Рендера представляет каждый ряд просмотров:

_renderItem = (item) => {
  return (
    <MovieItemCell movie={item.item} onPress={() => {
      console.log('点击了电影----' + item.item.title);
    }}/>
  )
};

Строка MovieItemCell — это настроенные представления для отображения каждого из данных фильма. Здесь не размещен код, который использует TouchableHighlight The, выделенный в качестве фонового компонента, следует отметить, что контейнер не может использоваться как пакет TouchableHighlight View, как и другие элементы управления, а может TouchableOpacity.

Простой список фильмов здесь завершен,Конкретный демонстрационный адрес нажмите здесь,Полная версия простого приложения со страницей сведений находится здесь.. В общем, очень просто реализовать страницу списка с помощью React Native.В следующей статье мы будем использовать SectionList, чтобы преобразовать эту демонстрацию в единую страницу списка и отобразить в настоящее время выпущенные и предстоящие фильмы в группах, а также продолжить изучение и резюмируйте контент, связанный с React Native.