Expo.ioReact Native ScrollView

ScrollView の Scroll 機能を ON・OFF して使おう!〜ReactNative〜

shopping-4974738_1920 Expo.io
スポンサーリンク

環境・条件

  • React Native の初心者向け
  • Expo 開発環境:v36.0.2
  • ScrollView関数コンポーネントで実装します。

手順

App.js を以下のように作成し、設定を追加していきます。

// App.js

import React, { memo } from "react";
import { View, Text, StyleSheet } from "react-native";

const App = () => {
  return (
    <View style={styles.container}>
      <Text>App</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center"
  }
});
export default memo(App);

スクリーンの高さとコンテンツの高さで判断するのがポイントです。

  • Step 1
    コンテンツの最下段までスクロールできるように。

    以下は、ScrollViewSafeAreaView でラップして使う前提です。

    SafeAreaView の style に {flexGrow:1} を追加します。
    {flex:1}に設定するとコンテンツの最下段が見えなくなります。

    // App.js
    
    import React, { memo } from "react";
    import { Text, StyleSheet, SafeAreaView, ScrollView } from "react-native";
    
    const App = () => {
      return (
        <SafeAreaView style={styles.container}>
          <ScrollView contentContainerStyle={styles.scrollView}>
            <Text>App</Text>
          </ScrollView>
        </SafeAreaView>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        flexGrow: 1
      },
      scrollView: {
        backgroundColor: "pink",
        alignItems: "center",
        justifyContent: "center"
      }
    });
    export default memo(App);
    
  • Step 2
    コンテンツの高さを取得します。

    onContentSizeChange props を利用します。

    ハンドラー関数 ( handleContentSizeChange ) を作成し、デフォルトでパラメーターとして渡される contentWidthcontentHeight の中で高さ ( contentHeight ) だけを利用します。

    ScrollViewonContentSizeChange={handleContentSizeChange} を追加します。

    <ScrollView
      onContentSizeChange={handleContentSizeChange}  // 追加
      ...
    >

    取得したコンテンツの高さを State ( ctxHeight ) に保存します。

    import React, { memo } from "react";
    import { SafeAreaView } from "react-native";
    ...
    
    const [ctxHeight, setCtxHeight] = React.useState(0);
    const handleContentSizeChange = (contentWidth, contentHeight) => {
      setCtxHeight(contentHeight);
    };

    ↓↓

    // App.js
    
    import React, { memo } from "react";
    import { Text, StyleSheet, SafeAreaView, ScrollView } from "react-native";
    
    const App = () => {
      const [ctxHeight, setCtxHeight] = React.useState(0);
      const handleContentSizeChange = (contentWidth, contentHeight) => {
        setCtxHeight(contentHeight);
      };
      return (
        <SafeAreaView style={styles.container}>
          <ScrollView
            contentContainerStyle={styles.scrollView}
            onContentSizeChange={handleContentSizeChange}
          >
            <Text style={styles.text}>App</Text>
          </ScrollView>
        </SafeAreaView>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        flexGrow: 1
      },
      scrollView: {
        backgroundColor: "pink",
        alignItems: "center",
        justifyContent: "center"
      },
      text: {
        fontSize: 42
      }
    });
    export default memo(App);
    
  • Step 3
    スクリーンの高さを取得します。

    Dimensions か、useWindowDimensions フックを利用して取得できます。

    // Dimensions
    import { Dimensions } from "react-native";
    
    const {width, height} = Dimensions.get('window')
    // useWindowDimensions
    import { useWindowDimensions } from "react-native";
    
    const ScrollViewExampleScreen = () => {
      const window = useWindowDimensions();
    ...

    ここでは useWindowDimensionswindow.height を使用します。

  • Step 4
    Scroll 機能を ON / OFF します。

    scrollEnabled props にコンテンツの高さが大きい時だけに true になるように設定します。

    ...
    const scrollEnabled = ctxHeight > window.height;
    ...
    <ScrollView
      scrollEnabled={scrollEnabled}   // 追加
      onContentSizeChange={handleContentSizeChange}
    >
    ...
    
  • Step 5
    動作を確認します。

    <Text style={styles.text}>App</Text>に長いテキストを適宜追加してからテストを行います。

    ※ テスト方法:( StyleSheet 内 )
    1) containerflexGrowflexflexGrow に変更しながらテスト、
    2) textfontSize を 42 → 12 → 42 に変更しながらテストします。

    ↓最終版

    // App.js
    
    import React, { memo } from "react";
    import {
      StyleSheet,
      Text,
      ScrollView,
      SafeAreaView,
      useWindowDimensions
    } from "react-native";
    const App = () => {
      const window = useWindowDimensions();
      const [ctxHeight, setCtxHeight] = React.useState(0);
      const handleContentSizeChange = (contentWidth, contentHeight) => {
        setCtxHeight(contentHeight);
      };
      const scrollEnabled = ctxHeight > window.height;
      console.log("-----------------------");
      console.log("!@# window.height: ", window.height);
      console.log("!@# contentHeight: ", ctxHeight);
      console.log("!@# scrollEnabled: ", scrollEnabled);
      return (
        <SafeAreaView style={styles.container}>
          <ScrollView
            scrollEnabled={scrollEnabled}
            onContentSizeChange={handleContentSizeChange}
            style={styles.scrollView}
          >
            <Text style={styles.text}>
              Lorem Ipsum is simply dummy text of the printing and typesetting
              industry. Lorem Ipsum has been the industry's standard dummy text ever
              since the 1500s, when an unknown printer took a galley of type and
              scrambled it to make a type specimen book. It has survived not only
              five centuries, but also the leap into electronic typesetting,
              remaining essentially unchanged. It was popularised in the 1960s with
              the release of Letraset sheets containing Lorem Ipsum passages, and
              more recently with desktop publishing software like Aldus PageMaker
              including versions of Lorem Ipsum. The standard chunk of Lorem Ipsum
              used since the 1500s is reproduced below for those interested.
              Sections 1.10.32 and 1.10.33 from de Finibus Bonorum et Malorum by
              Cicero are also reproduced in their exact original form, accompanied
              by English versions from the 1914 translation by H. Rackham.
            </Text>
          </ScrollView>
        </SafeAreaView>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        flexGrow: 1
      },
      scrollView: {
        flex: 1,
        backgroundColor: "pink",
        alignItems: "center",
        justifyContent: "center"
      },
      text: {
        fontSize: 42
      }
    });
    export default memo(App);
    
scrollview-01
▲ scrollEnabled = false

window.height:  896
contentHeight:  186.333328
scrollEnabled:  false

scrollview-02
▲ scrollEnabled = true

window.height: 896
contentHeight: 2155.33325
scrollEnabled: true

scrollview-03
▲ scrollEnabled = true

・window.height: 896
・contentHeight: 2155.33325
・scrollEnabled: true

まとめ

React Native で ScrollView を使うときは parent に {flexGrow:1} を設定することでiOS/Android 両方スクロールが正常に動作します。

コンテンツの高さとスクリーンの高さを比較することで、スクロール機能を必要な時だけ ON にすることができました。

スポンサーリンク

コメント

タイトルとURLをコピーしました