【9】React Nativeでテキストエディタを作ってみる!【デザインからアプリの見た目作成編】

こんにちは!かたつむり(@Katatumuri_nyan)です!

Reactを触ってみて、サイト的なものは作れるようになりました(*´ω`)
そこで、次はReactNativeを触ってみようと思い、簡単なテキストエディタを作成しようと企んでおります(笑)

前回、デザインから再スタートしました!
今回は、デザインにそって見た目を作っていきます(*´ω`)

GitHubでソースコードを管理しています!

最初から見る↓
【1】React Nativeでテキストエディタを作ってみる!【下調べ編】【1】React Nativeでテキストエディタを作ってみる!【下調べ編】

前回を見る↓
【8】React Nativeでテキストエディタを作ってみる!【再出発・デザイン編】

コンポーネントとデザイン

picture 7

今回はこれらの↑コンポーネントを作っていきます。

picture 8
↑各コンポーネントのデザインはこんな感じ
(ちょっと足りてないですがw手元にはあります!)

環境構築

第4回目の記事でWSL2での環境構築をしました。
今回は、expo initからやっていきます。別のディレクトリに作りたいので、

expo init snil_Markdown_TextEditorしました。

App.jsの作成

まずは、テーマを適応させるため、テーマライブラリのインストールをしました。
react-native-elementsでtheme機能を使う
↑よりテーマ作成のためにreact-native-elementsを使うことにしました。
UIキットなので、結構色んなコンポーネントが用意されていますね(*´ω`)
使えそう!
アイコンもあるみたいなので、後で見てみます✨

expo install react-native-elementsします。

expo initApp.jsは作成されているので、これを手直しします。

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { ThemeProvider } from 'react-native-elements';
import theme from './theme/theme';
import TopBar from './components/TopBar/TopBar';

export default function App() {

  return (
    <ThemeProvider theme={theme} >
      <StatusBar style="auto" />
      <View style={styles.container}>
        <Text>ここに子コンポーネント</Text>
      </View>
    </ThemeProvider>
  );
}


const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: theme.Night.main.mainBackgroundColor,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

TopBar.jsの作成

ここで、タイトルやナビを表示するためのコンポーネントが必要なことに気づき、急遽TopBar.jsを作成しました。

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

export default function TopBar(props) {

    return(
        <Text>ここに子コンポーネント</Text>
    )
}

テーマカラーの設定

theme.jsを作って、テーマの色設定書きました。
App.jsに読み込んで使います。

とりあえず、テーマのひな型を作りました。
これにそって、各テーマの色を設定していきます。

adobeXDからカラーコードを拾ってきては、追加しますw

export default theme = {
    Night: {
        main: {
            mainBackgroundColor: '',
            secondBackgroundColor: '',
            scrollBarColor: ''
        },
        textView:{
            backgroundColor: '',
            textColor:''
        },
        toolBar: {
            titleTextColor: ''
        },
        nav: {
            IconColor: ''
        },
        menu: {
            TitleColor: ''
        },
        menuBtn: {
            BackgroundColor: '',
            TextColor: '',
            IconColor: '',
            onPress: {
                BackgroundColor: '',
                TextColor: '',
                IconColor: '',
            }
        },
        menuBtnChild: {
            BackgroundColor: '',
            TextColor: '',
            IconColor: '',
            BoderColor: '',
            onPress: {
                BackgroundColor: '',
                TextColor: '',
                IconColor: '',
                BoderColor: '',
            }
        },
        PlusBtn: {
            BackgroundColor: '',
            IconColor: '',
        },
        typeSelectMenu: {
            BackgroundColor: '',
            TextColor: '',
            IconColor: '',
            onPress: {
                BackgroundColor: '',
                TextColor: '',
                IconColor: '',
            }
        }
    },
}

これで、theme.Night.main.mainBackgroundColor等と呼び出すと使えるようになります。
あれ、これ、テーマ用のライブラリ要らないですよねw
(各コンポーネントでtheme.jsをインポートしたらよさそう…)
どっちがいいかは分からないですが、ちょっとした修正で切り替えられるので、おいておきます。

テーマ切り替え機能の作成

テーマカラーを用意したので、ユーザーが切り替えれるようにしたいと思います。
例えば、theme.Night.main.mainBackgroundColorNightの部分がユーザーの選択によってRoseQuartzとかにできるようにしたい!

snailSetting.jsonというファイルを作成して、テーマの選択をここに保存するようにします。
FileSystemとかで、アプリ内のディレクトリに保存されるようにしよう!
そこから読み取れば完璧ですね(なのかな)

ちなみに、プレビューコンポーネントの色をテーマのカラーと帰れるようにしたり、自動保存の設定とかもここに保存されるようにします。

{
    "theme":"Night",
    "preview":"Inheritance",
    "autoSave":"30"
}

独自設定を保存する

expo-file-systemを使って、テーマをアプリ内に保存します。
snailSetting.jsonが既に存在したら作成しない、存在しなければ作成するようにして、そこからテーマを読み取ろうとおもいます。

expo install expo-file-systemして、使っていきます!

readSetting.jsをつくって、App.jsで起動時に読み込むようにしました。

// readSetting.js
import * as FileSystem from 'expo-file-system';

const snailSetting = {
    "theme": "Night",
    "preview": "Inheritance",
    "autoSave": "30"
}

export default async function readSetting() {
    const directoryUri = FileSystem.documentDirectory + 'SimpleMarkdown/setting/'
    const settingFileName = 'snailSetting.json'
    const fileUri = directoryUri + settingFileName
    let settingData

    await FileSystem.makeDirectoryAsync(directoryUri, { intermediates: true })
        .then(e => {
        }).catch(err => {
            console.error(err);
        })

    const fileList = await FileSystem.readDirectoryAsync(directoryUri)
        .then(e => {
            return e
        }).catch(err => {
            console.error(err);
        })

    if (fileList.includes(settingFileName) === false) {
        settingData = await FileSystem.writeAsStringAsync(fileUri, JSON.stringify(snailSetting), { encoding: FileSystem.EncodingType.UTF8 })
            .then(e => {
                return e
            }).catch(err => {
                console.log(fileUri);
                console.error("writeAsStringAsync >>" + err);
            })
    } else {
        settingData = await FileSystem.readAsStringAsync(fileUri, { encoding: FileSystem.EncodingType.UTF8 })
            .then(e => {
                return e
            }).catch(err => {
                console.error("readAsStringAsync >>" + err);
            })
    }

    return JSON.parse(settingData)
}
// App.js
const[appTheme,setAppTheme]=useState(null)

useEffect(()=>{
  readSetting().then(e=>{
    setAppTheme(e.theme)
    console.log('useEffect'+e.theme);
  })
}, [])

Title.jsの作成

picture 9

タイトルを作っていきます(*´ω`)
TopBar.jsにおきます!

import React from 'react';
import {Text } from 'react-native';
import { useTheme} from 'react-native-elements';


export default function Title(props) {
    const { theme } = useTheme();

    const style={
        color: theme.topBar.titleTextColor
    }

    return (
        <Text style={style}>{props.title}</Text>
    )
}

useThemeでテーマの情報を受け取ってます(*´ω`)

useContextの設定

こんなに簡単なの?React Hook useContextでデータ共有
App.jsから情報を受け取りたいので、useContextを使っていきます!

propsでもいいんですが、今回煩雑になるところはuseContextにします。

// App.js

export const fileDataGetter = React.createContext()

export default function App() {
  const [appTheme, setAppTheme] = useState(null)
  const [title, setTitle] = useState("aaatitle")

  const fileDataGetterValue={
    appTheme,
    setAppTheme,
    title, 
    setTitle
  }

...

↑ちなみに、App.jsではこのような設定をしました。

Nav.jsの作成

picture 10

ナビを作っていきます(*´ω`)
react-native-elementsiconに私がデザインで使ったアイコンが入ってるみたいなので、使っていきます。
→ラウンドタイプが使いたいのに、ラウンドタイプがないので、とりあえずシャープタイプのものを使っています。

@expo/vector-icons@12.0.5

右から左にスワイプでナビを閉じたいので、ジェスチャーを感知するライブラリを導入します。
expo install react-native-gesture-handler
公式ドキュメント見てもわからな過ぎて困りましたw

readSetting.jsの変更

AndroidとiOSでFileSystemの使い分けが必要だったので、変更しました。
expo install expo-deviceをして、デバイスの情報を取得します。

他のコンポーネントでも使う可能性があるので、App.jsで取得するようにしました。
Device.osNameとすると、iPadもiPhoneもiOSとして表示されるので、これで分岐をしたいと思います。

if (os == 'iOS') {
    FS = FileSystem
} else if (os == 'Android') {
    FS = StorageAccessFramework
}

AndroidのJSONの取り扱いでハマった…

↑では分岐が必要だと思っていましたが、もしかしたらJSONがダメかもです。

AndroidでJSON.parse()するとアプリごと終了してしまう事がある…。
なんでだろう
AndroidではJSONが使えないのかもしれない…

How to parse json data in react native
↑のような質問を結構見かけたので、JSONは使えないみたいですね…

console.logだときれいに表示されるけど、useStateにセットするのはできないみたい…謎

const [appTheme, setAppTheme] = useState(null)
const os = Device.osName

useEffect(() => {
  readSetting(os).then(e => {
    console.log(JSON.parse(e).theme);
    setAppTheme(JSON.parse(e).theme)
  })
}, [])

問題なのは↑
useState('Night')にしてもエラーが出るので、useStateに文字列が入れられないことが原因見たいです。
なんでだろう…。iOSはうまくいくし、他のStateは文字列も入るんですよね。

<ThemeProvider theme={theme[appTheme]} >

↑原因を突き止めていくと、これがダメ見たいです。
うーんどうしよう。

<ThemeProvider theme={theme}>
...
import fileDataGetter from '../../../App';

export default function name(props) {
  const { appTheme } = useContext(fileDataGetter)
  let { theme } = useTheme();
  theme = theme[appTheme]
...

とりあえず色々やってみましたが、原因が分からないまま普通に動き始めました…。
AndroidStudio謎すぎる。

謎で終わりましたが、AndroidStudioの挙動がおかしくて1日つぶしてしまったので、今回はこの辺で終わります(*´ω`)

↓続き
【10】React Nativeでテキストエディタを作ってみる!【ナビ・エディタエリア作成編】【10】React Nativeでテキストエディタを作ってみる!【ナビ・エディタエリア作成編】

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です