Видео в длинном Flatlist приводит к сбою приложения и низкой производительности - PullRequest
0 голосов
/ 28 января 2020

Topi c: плоский список видео в React Native. (Обычные видео / YouTube видео)

Любой из них будет отображаться в соответствии с данными массива одновременно. Поэтому это будет длинный плоский список, содержащий два типа видео.

Иногда многие события onPress не работают из-за проблем с производительностью

Основные проблемы:

  1. Приложение падает при прокрутке снизу вверх
  2. Производительность очень низкая

Свыше двух - это основная проблема, которую необходимо решить. Есть ли способы решить эти проблемы?

Какие решения я могу попытаться улучшить Flatlist производительность и решить проблему сбоев Video Flatlist?

Ниже мой код в App. js

import React from "react";
import {
    StyleSheet,
    Text,
    View,
    SafeAreaView,
    FlatList,
    TouchableWithoutFeedback,
    Dimensions
} from "react-native";

import Video from "react-native-video";
import ProgressBar from "react-native-progress/Bar";
import YouTube from 'react-native-youtube';
import LightVideo from "./sample.mp4";

import Icon from "react-native-vector-icons/FontAwesome";

// console.disableYellowBox = true;
function secondsToTime(time) {
    return ~~(time / 60) + ":" + (time % 60 < 10 ? "0" : "") + time % 60;
}

const { width } = Dimensions.get("window");
const height = width * 0.5625;


const list = [
    {
        'type': 'YouTube'
    },
    {
        'type': 'Normal'
    },
    {
        'type': 'YouTube'
    },
    {
        'type': 'Normal'
    },
    {
        'type': 'Normal'
    },
];


export default class RNVideo extends React.PureComponent {

    state = {
        video: [],
        duration: [0, 0, 0, 0, 0],
        progress: [0, 0, 0, 0, 0]
    };

    constructor(props) {
        super(props);
        this.player = [];
    }

    componentDidMount() {
        this.progress_state();
        this.duration_state();
        this.video_pause_state(false);
    }

    play_pause_button = (index) => {
        if (this.state.progress >= 1) {
            this.player[index].seek(0);
        }
        var statevisible = this.state.video;
        statevisible[index] = !statevisible[index];
        this.setState({
            video: statevisible
        });
        this.forceUpdate();
    };


    progress_bar_touch(e, index) {
        const position = e.nativeEvent.locationX;
        const progress = (position / 250) * this.state.duration[index];
        this.player[index].seek(progress);
    };

    handleProgress(progress, index) {
        var statevisible = this.state.progress;
        statevisible[index] = progress.currentTime / this.state.duration[index];
        this.setState({
            progress: statevisible
        });
        this.forceUpdate();
    };

    handleLoad(meta, index) {
        var statevisible = this.state.duration;
        statevisible[index] = meta.duration;
        this.setState({
            duration: statevisible
        });
        this.forceUpdate();
    };

    onViewableItemsChanged = async ({ viewableItems }) => {
        this.video_pause_state(true);
        if (viewableItems != '' && viewableItems != null) {
            var indexvalue = viewableItems[0]['index'];
            var statevisible = this.state.video;
            statevisible[indexvalue] = !statevisible[indexvalue];
            this.setState({
                video: statevisible
            });
            this.forceUpdate();
        }
    }


    // shouldComponentUpdate() {
    //   return false
    // }


    video_pause_state(value) {
        var statevisible = [];
        for (var i = 0; i < list.length; i++) {
            statevisible[i] = value;
        }
        this.setState({
            video: statevisible
        });
    }

    progress_state() {
        var statevisible = [];
        for (var i = 0; i < list.length; i++) {
            statevisible[i] = 0;
        }
        this.setState({
            progress: statevisible
        });
    }


    duration_state() {
        var statevisible = [];
        for (var i = 0; i < list.length; i++) {
            statevisible[i] = 0;
        }
        this.setState({
            duration: statevisible
        });
    }

    video_end(index) {
        var statevisible1 = this.state.duration;
        statevisible1[index] = 0;
        var statevisible2 = this.state.progress;
        statevisible2[index] = 0;
        this.setState({
            duration: statevisible1,
            progress: statevisible2
        });
        this.play_pause_button(index);
    }

    keyExtractor = (item, index) => index.toString()

    renderItem = ({ item, index }) => (
        <>
            {item.type != 'YouTube' ?
                <>
                    <View style={styles.container}>
                        <View>
                            <Video
                                paused={this.state.video[index]}
                                muted={this.state.video[index]}
                                source={LightVideo}
                                // controls={true}
                                // repeat={true}
                                style={styles.videos}
                                resizeMode="cover"
                                onLoad={(meta) => this.handleLoad(meta, index)}
                                onProgress={(progress) => this.handleProgress(progress, index)}
                                onEnd={() => this.video_end(index)}
                                ref={(ref) => {
                                    this.player[index] = ref
                                }}
                            />
                            <View style={styles.controls}>
                                <TouchableWithoutFeedback onPress={(event) => this.play_pause_button(index)}>
                                    <Icon name={!this.state.video[index] ? "pause" : "play"} size={30} color="#FFF" />
                                </TouchableWithoutFeedback>
                                <TouchableWithoutFeedback onPress={(event) => this.progress_bar_touch(event, index, this)}>
                                    <View>
                                        <ProgressBar
                                            progress={this.state.progress[index]}
                                            color="#FFF"
                                            unfilledColor="rgba(255,255,255,.5)"
                                            borderColor="#FFF"
                                            width={230}
                                            height={20}
                                        />
                                    </View>
                                </TouchableWithoutFeedback>
                                <Text style={styles.duration}>
                                    {secondsToTime(Math.floor(this.state.progress[index] * this.state.duration[index]))}
                                </Text>
                            </View>
                        </View>
                    </View>
                </>

                :

                <>
                    <View style={{ marginTop: 50 }}>
                        <YouTube
                            apiKey="YOUR_API_KEY"
                            videoId="YOUR_YOUTUBE_VIDEO_ID_FROM_URL"
                            play={this.state.video[index]}
                            style={styles.videos}
                        />
                    </View>
                </>
            }
        </>
    )

    render() {
        return (
            <>
                <SafeAreaView style={{ flex: 1 }}>
                    <FlatList
                        keyExtractor={this.keyExtractor}
                        data={list}
                        renderItem={this.renderItem}
                        extraData={this.state}
                        onViewableItemsChanged={this.onViewableItemsChanged}
                        removeClippedSubviews={true}
                        maxToRenderPerBatch={3}
                        initialNumToRender={3}
                        legacyImplementation={true}
                    />
                </SafeAreaView>
            </>
        );
    }
}

const wid = "95%";
const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginBottom: 60,
        marginTop: 60,
    },
    controls: {
        backgroundColor: "rgba(0, 0, 0, 0.5)",
        height: 48,
        left: 0,
        bottom: 0,
        right: 0,
        position: "absolute",
        flexDirection: "row",
        alignItems: "center",
        justifyContent: "space-around",
        width: wid,
        margin: 8,
        borderBottomLeftRadius: 10,
        borderBottomRightRadius: 10,
        paddingHorizontal: 10,
    },
    mainButton: {
        marginRight: 15,
    },
    duration: {
        color: "#FFF",
        marginLeft: 15,
    },
    videos: {
        width: wid,
        backgroundColor: '#ccc',
        borderRadius: 10,
        overflow: 'hidden',
        margin: 8,
        height
    },
});

Обновление

Я пришел к этому окончательному решению. Но не в состоянии сделать автозапуск. Нужны идеи по этому поводу.

import React from 'react';
import { View, StyleSheet, SafeAreaView } from 'react-native';
import YouTube from 'react-native-youtube';
import { OptimizedFlatList } from 'react-native-optimized-flatlist'
import VideoPlayer from 'react-native-video-player';

const list = [
  {
    'type': 'Normal'
  },
  {
    'type': 'YouTube'
  },
  {
    'type': 'Normal'
  },
  {
    'type': 'Normal'
  },
  {
    'type': 'YouTube'
  },
  {
    'type': 'Normal'
  },
  {
    'type': 'Normal'
  },
  {
    'type': 'Normal'
  },
];

export default class App extends React.PureComponent {
  constructor() {
    super();
    this.player = [];

    this.state = {
      video: { width: undefined, height: undefined, duration: undefined },
      thumbnailUrl: undefined,
      videoUrl: undefined,
    };
  }

  keyExtractor = (item, index) => index.toString()

  renderItem = ({ item, index }) => (
    <>
      {item.type != 'YouTube' ?
        <>
          <View style={styles.videos}>
            <VideoPlayer
              // autoplay
              // pauseOnPress
              video={{ uri: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4', type: 'mp4' }}
              resizeMode={'cover'}
              ref={r => this.player[index] = r}
            />
          </View>
        </>
        :
        <>
          <View style={{ marginTop: 50 }}>
            <YouTube
              apiKey="YOUR_API_KEY"
              videoId="3NQRhE772b0"
              style={{...styles.videos,...styles.videos1}}
            />
          </View>
        </>
      }
    </>
  )

  onViewableItemsChanged = async ({ viewableItems }) => {
    if (viewableItems != '' && viewableItems != null) {
      var indexvalue = viewableItems[0]['index'];
      indexvalue++;
      if (indexvalue != 1) {
        if (!this.player[indexvalue].state.isPlaying) {
          this.player[indexvalue].pause();
        } else {
          this.player[indexvalue].resume();
        }
      }
    }
  }


  render() {
    return (
      <>
        <SafeAreaView style={{ flex: 1 }}>
          <OptimizedFlatList
            keyExtractor={this.keyExtractor}
            data={list}
            renderItem={this.renderItem}
            removeClippedSubviews={true}
            onViewableItemsChanged={this.onViewableItemsChanged}
          />
        </SafeAreaView>
      </>
    );
  }
}


const styles = StyleSheet.create({
  videos: {
    width: "95%",
    backgroundColor: '#ccc',
    borderRadius: 10,
    overflow: 'hidden',
    margin: 10,
    marginBottom: 20,
    marginTop: 20
  },
  videos1: {
    height: 250
  }
});
...