React NativeのFlat List使用及びピット包装まとめ


RNの中でFlatListは高性能のリストコンポーネントで、それはListViewコンポーネントのアップグレード版で、性能の面で大きな向上があります。もちろん、リスト機能を実現する時にFlat Listを使うことをお勧めします。FlatListといえば、まずサポート機能を復習します。
  • は完全にプラットフォームにまたがります。
  • は、水平配列モードをサポートする。
  • 行のコンポーネントは、表示または非表示の場合、コールバックイベントを設定することができます。
  • は、単独のヘッドセットをサポートしています。
  • は、別個の末尾コンポーネントをサポートする。
  • はユーザー定義の行間分離線をサポートします。
  • はドロップダウンリフレッシュをサポートします。
  • はアップロードをサポートします。
  • は指定行へのジャンプをサポートします。
  • 今日のこの文章は具体的にどのように使うかを紹介していません。使い方を見たいなら、GitHub https://github.com/xiehui999/helloReactNativeのいくつかの例を参考にしてください。今日のこの文章は主に私の使用過程で感じた大きな穴を紹介し、Flat Listの二次パッケージを紹介します。
    次に、簡単な例を紹介します。私たちの文章もこの例があります。
    
        <FlatList
          data={this.state.dataList} extraData={this.state}
          refreshing={this.state.isRefreshing}
          onRefresh={() => this._onRefresh()}
          keyExtractor={(item, index) => item.id}
          ItemSeparatorComponent={() => <View style={{
            height: 1,
            backgroundColor: '#D6D6D6'
          }}/>}
          renderItem={this._renderItem}
          ListEmptyComponent={this.emptyComponent}/>
          
          
      //     
        emptyComponent = () => {
        return <View style={{
          height: '100%',
          alignItems: 'center',
          justifyContent: 'center',
        }}>
          <Text style={{
            fontSize: 16
          }}>        </Text>
        </View>
      }
    上のコードでは、私達は主にListEmptyComponentを見ています。データがない時に充填するレイアウトを表しています。普通は中間にヒントを表示します。紹介のために簡単にデータがないので、更新します。上のコードは一時的にデータがないように見えますが、運行後、目がくらみました。データがないので、一番上の中間に表示されます。この時、高さ100%は効果がありません。もちろんflex:1を使ってみてください。Viewの高ビューをフルスクリーンに充填しますが、まだ効果がありません。
    なぜ設定したのか効果がないです。好奇心があるなら、ソースコードを見に来ます。ソースコードはreact-native->Libries->Listsにあります。リストのコンポーネントはこのディレクトリの下にあります。まずFlatListファイルに行ってキーワードListEmptyComponentを検索します。このコンポーネントが使われていないことを発見しました。それではレンダーに行きます。
    
     render() {
      if (this.props.legacyImplementation) {
       return (
        <MetroListView
         {...this.props}
         items={this.props.data}
         ref={this._captureRef}
        />
       );
      } else {
       return (
        <VirtualizedList
         {...this.props}
         renderItem={this._renderItem}
         getItem={this._getItem}
         getItemCount={this._getItemCount}
         keyExtractor={this._keyExtractor}
         ref={this._captureRef}
         onViewableItemsChanged={
          this.props.onViewableItemsChanged && this._onViewableItemsChanged
         }
        />
       );
      }
     }
    
    メトロListView(内部実装はScrrollView)は古いListViewの実現方式で、Virtualzed Listは新しい性能がより良い実現です。私たちはこの書類に行きます
    
      //      
      const itemCount = this.props.getItemCount(data);
      if (itemCount > 0) {
        ....      
      } else if (ListEmptyComponent) {
       const element = React.isValidElement(ListEmptyComponent)
        ? ListEmptyComponent // $FlowFixMe
        : <ListEmptyComponent />;
       cells.push(
        /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
         * comment suppresses an error when upgrading Flow's support for React.
         * To see the error delete this comment and run Flow. */
        <View
         key="$empty"
         onLayout={this._onLayoutEmpty}
         style={inversionStyle}>
         {element}
        </View>,
       );
      }
    ここで私たちが定義しているListEmptyComponentの食パンを見ました。このviewにはinversionSteyleのスタイルが追加されています。
    
    const inversionStyle = this.props.inverted
       ? this.props.horizontal
        ? styles.horizontallyInverted
        : styles.verticallyInverted
       : null;
       
      :
    verticallyInverted: {
      transform: [{scaleY: -1}],
     },
     horizontallyInverted: {
      transform: [{scaleX: -1}],
     },
    
    上のスタイルはアニメを追加しました。高さを設定していません。だから私達はListEmptyComponentでheight:'100%またはflex:1を使っても効果がありません。
    私たちが望む効果を実現するためには、heightを具体的な値に設定する必要があります。この値の設定はどれぐらいですか?FlatListにスタイルを設定すると、背景属性は色を設定します。FlatListはデフォルトで画面の高さを占めています。ListEmptyComponentでviewの高さをFlat Listの高さに設定します。Flat Listの高さを取得するには、onLayoutで取得できます。
    コード調整:
    
    //    
    fHeight = 0;
    
        <FlatList
          data={this.state.dataList} extraData={this.state}
          refreshing={this.state.isRefreshing}
          onRefresh={() => this._onRefresh()}
          keyExtractor={(item, index) => item.id}
          ItemSeparatorComponent={() => <View style={{
            height: 1,
            backgroundColor: '#D6D6D6'
          }}/>}
          renderItem={this._renderItem}
          onLayout={e => this.fHeight = e.nativeEvent.layout.height}
          ListEmptyComponent={this.emptyComponent}/>
          
          
      //     
        emptyComponent = () => {
        return <View style={{
          height: this.fHeight,
          alignItems: 'center',
          justifyContent: 'center',
        }}>
          <Text style={{
            fontSize: 16
          }}>    </Text>
        </View>
      }
    
    
    上記の調整により、Android上で運転している時に私達が望む効果が得られましたが、iOS上では制御できず、たまに中央に表示され、たまに一番上に表示されます。理由はiOS上でのオンラyout呼び出しのタイミングとAndroidのタイミングが少し違っているからです。(iOSではempyComponentレンダリング時のオンラyoutはまだ返事がないので、fHeightはまだ値していません)。
    だから、変化した値をempyComponentに作用させるために、fHeightをstateに設定します。
    
    state={
      fHeight:0
    }
    
    onLayout={e => this.setState({fHeight: e.nativeEvent.layout.height})}
    
    このように設定すれば完璧ですよね。でも、androidでは私たちの要求した効果を完璧に実現できます。iOSではフラッシュ画面を往復する問題があります。ロゴの発見値は常に0と測定値を往復変換します。ここでは測定値だけが必要ですので、onLayoutを修正します。
    
               onLayout={e => {
                 let height = e.nativeEvent.layout.height;
                 if (this.state.fHeight < height) {
                   this.setState({fHeight: height})
                 }
               }}
    処理を経て、iosの上でついに完璧に私達の要した効果を実現しました。
    上のピット以外にも、個人的な感じはもう一つのピットがone EndReachedです。もし私達がプルダウンローディング機能を実現すれば、いずれもこの属性を使います。それについて言えば、もちろん私達はonenEnd ReachedThress holdに言及します。FlatListの中でonedReachedThreshedはnumberタイプです。なお、FlatListとListViewのone EntdReachedThress holdは意味が違っています。ListViewでは、ListViewでは、one EndReachedThress holdは、具体的な底にあとどれぐらいのピクセルがあるかを表しています。デフォルト値は1000です。FlatListでは倍数(比値ともいい、画素ではない)を表していますが、デフォルトは2です。
    じゃ、慣例によって、次のように実現します。
    
          <FlatList
            data={this.state.dataList}
            extraData={this.state}
            refreshing={this.state.isRefreshing}
            onRefresh={() => this._onRefresh()}
            ItemSeparatorComponent={() => <View style={{
              height: 1,
              backgroundColor: '#D6D6D6'
            }}/>}
            renderItem={this._renderItem}
            ListEmptyComponent={this.emptyComponent}
            onEndReached={() => this._onEndReached()}
            onEndReachedThreshold={0.1}/>
    その後、component DidMountに下記のコードを入れます。
    
      componentDidMount() {
        this._onRefresh()
      }
    つまり、最初のページのデータの読み込みを開始し、さらに多くのデータをローディングし、データソースdata Listを更新します。完璧に見えますが、運転後には常にワンドリンクを呼び出しています。すべてのデータがロードされるまでは、おそらく皆さんも推測できます。オンラインfreshでデータをロードするには時間がかかります。データ要求前にレンダー方法で実行します。この時はデータがないので、onentendReached方法で一回実行します。この時は2回のデータをロードするのに相当します。
    ワンドリセットは何回実行するかというと、エンドレスドレスの値が必要になりますので、慎重にワンドレストを設定します。設定ピクセルとして理解したら、比較的大きな数に設定します。例えば、100は終わりました。個人的には0.1を設定したほうがいいと思います。
    上記の分析を通して、個人的な感覚でFlatListを一回二回パッキングする必要があります。自分のニーズに合わせて二回パッキングしました。
    
    import React, {
      Component,
    } from 'react'
    import {
      FlatList,
      View,
      StyleSheet,
      ActivityIndicator,
      Text
    } from 'react-native'
    import PropTypes from 'prop-types';
    
    export const FlatListState = {
      IDLE: 0,
      LoadMore: 1,
      Refreshing: 2
    };
    export default class Com extends Component {
      static propTypes = {
        refreshing: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
      };
      state = {
        listHeight: 0,
      }
    
      render() {
        var {ListEmptyComponent,ItemSeparatorComponent} = this.props;
        var refreshing = false;
        var emptyContent = null;
        var separatorComponent = null
        if (ListEmptyComponent) {
          emptyContent = React.isValidElement(ListEmptyComponent) ? ListEmptyComponent : <ListEmptyComponent/>
        } else {
          emptyContent = <Text style={styles.emptyText}>        </Text>;
        }
        if (ItemSeparatorComponent) {
          separatorComponent = React.isValidElement(ItemSeparatorComponent) ? ItemSeparatorComponent :
            <ItemSeparatorComponent/>
        } else {
          separatorComponent = <View style={{height: 1, backgroundColor: '#D6D6D6'}}/>;
        }
        if (typeof this.props.refreshing === "number") {
          if (this.props.refreshing === FlatListState.Refreshing) {
            refreshing = true
          }
        } else if (typeof this.props.refreshing === "boolean") {
          refreshing = this.props.refreshing
        } else if (typeof this.props.refreshing !== "undefined") {
          refreshing = false
        }
        return <FlatList
          {...this.props}
          onLayout={(e) => {
            let height = e.nativeEvent.layout.height;
            if (this.state.listHeight < height) {
              this.setState({listHeight: height})
            }
          }
          }
          ListFooterComponent={this.renderFooter}
          onRefresh={this.onRefresh}
          onEndReached={this.onEndReached}
          refreshing={refreshing}
          onEndReachedThreshold={this.props.onEndReachedThreshold || 0.1}
          ItemSeparatorComponent={()=>separatorComponent}
          keyExtractor={(item, index) => index}
          ListEmptyComponent={() => <View
            style={{
              height: this.state.listHeight,
              width: '100%',
              alignItems: 'center',
              justifyContent: 'center'
            }}>{emptyContent}</View>}
        />
      }
    
      onRefresh = () => {
        console.log("FlatList:onRefresh");
        if ((typeof this.props.refreshing === "boolean" && !this.props.refreshing) ||
          typeof this.props.refreshing === "number" && this.props.refreshing !== FlatListState.LoadMore &&
          this.props.refreshing !== FlatListState.Refreshing
        ) {
          this.props.onRefresh && this.props.onRefresh()
        }
    
      };
      onEndReached = () => {
        console.log("FlatList:onEndReached");
        if (typeof this.props.refreshing === "boolean" || this.props.data.length == 0) {
          return
        }
        if (!this.props.pageSize) {
          console.warn("pageSize must be set");
          return
        }
        if (this.props.data.length % this.props.pageSize !== 0) {
          return
        }
        if (this.props.refreshing === FlatListState.IDLE) {
          this.props.onEndReached && this.props.onEndReached()
        }
      };
    
    
      renderFooter = () => {
        let footer = null;
        if (typeof this.props.refreshing !== "boolean" && this.props.refreshing === FlatListState.LoadMore) {
          footer = (
            <View style={styles.footerStyle}>
              <ActivityIndicator size="small" color="#888888"/>
              <Text style={styles.footerText}>     …</Text>
            </View>
          )
        }
        return footer;
      }
    }
    const styles = StyleSheet.create({
      footerStyle: {
        flex: 1,
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        padding: 10,
        height: 44,
      },
      footerText: {
        fontSize: 14,
        color: '#555555',
        marginLeft: 7
      },
      emptyText: {
        fontSize: 17,
        color: '#666666'
      }
    })
    
    
    proptyComponentに定義があれば、カスタムViewを使っています。同じItemSeparatoComponentもカスタマイズできます。
    データをドロップダウンロードする際に、データをロードしていることをユーザに促すためのListFooter Componentが定義されています。refring属性がbootleanであれば、ドロップダウンローディング機能がないことを表しています。numberタイプであれば、PageSizeは伝えなければなりません。データソース長とPageSizeは残りが0に等しくないか?もっと多くのデータがあるかどうかを判断します。(最終的に要求されたデータはPageSizeに等しい場合にだけ多くのデータがあります。小さい場合はオンデマンドReachedをリセットしなくてもいいです。)もちろん上のコードも簡単です。分かりやすいと信じています。他は多く紹介しません。以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。