へなちょこSEの考察

0x22歳のへなちょこSEが、日々思うことを考察します。自社内、金融系を経て現在法人系PKG開発に従事。

React Native ElementsでOverlayを使ったらキーボードで入力エリアが隠れた

タイトルの通りです。

OverlayにはKeyboardAboydingViewが効かないらしく、どうしても隠れてしまいました。

最終的にOverlayの中の一番下に、「react-native-keyboard-spacer」を追加して対応しました。

GitHub - Andr3wHur5t/react-native-keyboard-spacer: Plug and play react-native keyboard spacer view.

正直、あまり動きがかっこよくないんですが、とりあえず目的は達成できたのでよしとします。

スマートフォンアプリ「セワシタ?」をリリースしました

ペットのお世話を管理しつつ、家族のつながりを強くするアプリ、「セワシタ?」をリリースしました。

iOS

セワシタ?

セワシタ?

  • Haruya Nakamura
  • ライフスタイル
  • 無料
apps.apple.com

Android

play.google.com

簡単に言うと、お世話の予定を登録し、実施した記録を入力すると、同じ家族の人みんなでその情報が見れる、というものです。

ペットのお世話ってどうしても一人の人がやることになりがちだったり、他の人がやってくれてたのにもう一度やってしまったりすることがあるので、お世話の実施状況を共有できたら良いんじゃないの?と思って作りました。

使用技術について

一応エンジニアのブログなので、使用技術について書いておきます。 またのちのち、時間があればこんなふうに作ったよ的な話もできたら良いなぁと思ってます。

  • React Native(Expo)
  • Firebase cloudstore
  • mobx

SwiftやらKotlinを使うといろいろめんどくさいのでExpo使いました。

Expo超便利。まじ簡単。

ただ、アプリ内課金は使えないので、使いたい人はReact Nativeそのままで行くしかないですね。

あとバックエンド(DB)はサーバー立てたりしたくなかったので、Firebase cloudstoreを使用。 ちなみに認証もFirebase Authentication使ってます。

状態管理はReduxを使ってみたけど、ちょっとめんどくさすぎたのでmobxに変更しました。 mobxはちょっと癖あるけど、シンプルで簡単。 規模の小さいアプリなら全然ありですね。

その他のライブラリとか苦労したことなどはのちのち~。

ぜひ使ってみてください。

【Expo (React Native/Android)】BottomTabNavigation + Admob + GiftedChatでキーボードが入力エリアを隠してしまう問題

React Nativeアプリを開発中に、タイトルの問題に当たって解決したので記録しておきます。


開発環境

ExpoSDK:35 Androidはシミュレータ(Pixel3)と実機(SO-02G)で確認

package.jsonの抜粋

"expo": "^35.0.0",
"expo-ads-admob": "^7.0.0",
"expo-constants": "~7.0.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-35.0.0.tar.gz",
"react-native-gifted-chat": "^0.9.11",
"react-navigation": "^4.0.6",
"react-navigation-stack": "^1.8.1",
"react-navigation-tabs": "^2.5.5",

事象

Expo使って開発していて、BottomTabNavigation + Admob + GiftedChatを使っている画面があります。 この画面でチャットの文字を入力しようとすると、入力エリアがキーボードに隠されてしまいました。

対策1:KeyboardAboidingViewの追加

GiftedChatのGithubを確認したところ、Androidの場合は"KeyboardAboidingView"を使うようにとのこと。 見逃していたため追加しました。

[https://github.com/FaridSafi/react-native-gifted-chat#notes-for-android:title]

<View style={{ flex: 1 }}>
   <GiftedChat />
   {
      Platform.OS === 'android' && <KeyboardAvoidingView behavior="padding" />
   }
</View>

ただ、この方法だけだと結局うまくいきませんでした。

対策2:ReactNavigationのヘッダーの高さ分Offset設定

いろいろ調べたら「ReactNavigationのヘッダーを使っているとうまくいかないよ」という書き込みを見つけたので、その書き込みの通り対応してみることに。

まずimportに下記を追加

import { Header } from 'react-navigation-stack';

続いて先ほどのKeyboardAboidingViewにkeyboardVerticalOffsetを設定しました。

<KeyboardAvoidingView
 keyboardVerticalOffset={Header.HEIGHT}
 behavior="padding" />

動作を確認すると、微妙に動いてくれたんですが、まだ隠れている・・・。 これ、他にもoffsetに足さなきゃダメなんじゃね?ということでさらに調べる。

対策3:ステータスバーの高さ分Offsetを追加

たぶんステータスバーも影響してんだろ、ということでExpoの機能を使ってステータスバーの高さを取得します。

importに下記を追加

import Constants from 'expo-constants';

で、Offsetに追加

<KeyboardAvoidingView
 keyboardVerticalOffset={Header.HEIGHT + Constants.statusBarHeight}
 behavior="padding" />

これでもまだ足りず。 あぁそうか、Admobだ。 しかもAdmobはSmartBannerにしてるから高さ可変だぞ。

対策4:Admobの高さ分Offsetを追加

ExpoのAdmobでサイズをsmartBannerPortraitに指定している場合、iOSの場合は高さ固定ですが、Androidだと端末の画面サイズに応じて広告の高さが変わります。 なので、値を以下の通り設定。

let adHeight = 32;
if (Dimensions.get('window').height > 400 && Dimensions.get('window').height < 720) {
    adHeight = 50;
}
else if (Dimensions.get('window').height > 720) {
    adHeight = 90
}

で、これをまたOffsetに足しこむ。

<KeyboardAvoidingView
 keyboardVerticalOffset={Header.HEIGHT + Constants.statusBarHeight + adHeight}
 behavior="padding" />

うまく動きました!

補足:BottomTabNavigationの設定

BottomTabNavigationでtabBarOptionsを使っている場合、"keyboardHidesTabBar"は"false"を設定してください。

Reactで親子間の制御をする方法

Reactを試してみていて、親子間で制御をする方法に困っていろいろ調べたので簡単にメモ。

やりたかったこと

Material UIを使ってモーダル的なDialogを表示したり閉じたりしたかった。

公式サイトのやり方だと子供(つまりDialog側)のコンポーネントに開いたり閉じたりするイベントを設定していたんだけど、親のコンポーネントから開いて子供のコンポーネントで閉じるようにしたかった。

(親の何かのボタンを押すと開いて、Dialog側の閉じるボタンで閉じる感じ)

試したこと

refなる仕組みを使ってやろうとした。

最初は親のほうで子供をref指定して子供のOpenメソッドを呼び出すことで子供のStateを更新して実現した。

けど、これだとMaterial UIのwithStylesを使って子どものコンポーネントをexportすると動かなくなる。

最終的に

「Dialogを開いている状態(Visible)」は子供が持つべきだと思ってたんだけど、「Dialogを開くべきかどうか」は親が判断するわけだから親のStateに持たせてもいいのか、と思い直して、以下のようになった。

  • 親のStateにVisibleを設定
  • Closeするfunction(setState({visible:false}))を追加
  • 子供のPropsとして親のVisibleとCloseFunctionを渡す
  • 閉じたいときは子供がPropsで渡ってきたCloseFunctionを実行することで親のStateを変更して閉じる

実装

親のState

state = { dialogOpen: false }

親のOpenFunction(ボタン押してDialog開く操作)

handleClose = () => {
    this.setState({ dialogOpen: false });
};

親のCloseFunction(Dialogの閉じるボタンが押された際に実行される)

handleClose = () => {
    this.setState({ dialogOpen: false });
};

親側の子供の呼び出し(JSX)

<MyDialog open={this.state.dialogOpen} close={this.handleClose}/>

子供側のDialogの表記

<Dialog
   open={this.props.open}
   onClose={this.props.close}
   scroll='paper'
   aria-labelledby="scroll-dialog-title"
>

子供側の閉じるボタン

<Button onClick={this.props.close} color="primary">Cancel</Button>

基本的に、親から子供の状態やら中身をいじりたければ親のStateに設定して子供はそれをPropで受け取って使えばいいらしい。

少なくとも、これでやりたかったことは実現できた。

当たり前のことなのかもしれないけど、調べるのに時間がかかったので同じことに悩む人のために記録を残しておきます。

DBMS_SCHEDULERをPL/SQLの中で使うとトランザクションが効かない

DBMS_SCHEDULERを使っているPL/SQLで、エラーが起きた際にロールバックしようとしたが、ロールバックできない事象が発生した。
いろいろ試してみた結果、ExceptionをCatchした中でDBMS_SCHEDULERのDROP_JOBを呼んでいたのだが、これが呼ばれるとトランザクションが強制コミットされているらしい。
その直前にRollbackを追加してやったら、期待したとおりにデータがロールバックした。

DBMS_SCHEDULERはExceptionの中でDROP_JOBしてやらないと、エラーの際にJOBが残り続けてしまう(作りによっては)。
なので、ExceptionでCatchした際は先にRollbackを実行してからDROP_JOBを実行する必要がある。

また、同様にCREATE等でも同じことが起こると思われる(試してないが)ため、ジョブを作るのは一番最初に実行しておいて、テーブルのUPDATE等を実施し、最後にJOB削除、という流れを経る必要がある。
どーりでエラー投げてもデータが登録されてしまうわけだ。。。

herokuでPlay Framework 2.6(Scala)を動かした話

備忘録を兼ねて。 Herokuに登録する辺りの話は書きませんがこちらを参考にしました。

qiita.com

あとPlay Frameworkを動かすのはこちらを参考に。 途中まではそのままです。最後にちょっとつまずきました。

qiita.com

Play Framework 2.6の準備

特に環境作成などはせず、Play Frameworkの公式サイトからサンプルプロジェクトを落としてきます。

https://www.playframework.com/download

適当なフォルダに解凍してコマンドプロンプトでフォルダに移動します。

先のサイトの通り、Procfileを作成します。 中身は先のサイトを参考にしてください。

ローカルのgitに追加してコミット

git init

git add .

git commit -m "init"

herokuにアプリを作成

heroku create app-name

herokuにApplication Secretを追加

heroku config:add APPLICATION_SECRET=[適当な文字列]

herokuにPush

git push heroku master

これで開く、はずでしたが・・・

buildpackの指定とAllowsHostの設定

Play Framework公式のドキュメントにscalaならbuildpackの指定が必要かも、みたいなことが書いてあったのでやってみました。

heroku buildpacks:set heroku/scala

再度Pushしてみたところ、deployは成功。 画面を開くと、Bad Requestの文字が。 まぁ、とりあえず動いたということか。 どうもAllows hostの設定が足りないとか言われたので、application.confの中のAllowed hosts filter configurationでherokuのホストを追加して再度コミット&プッシュ。 めでたくWelcome to Playが開きましたとさ。 さぁ、こっから何をしていこうかな。

Redmineのチケット番号を指定してソース差分を取る

Redmineのチケット番号を指定してソース差分を取る

ソースの差分を取るとき、Redmineのチケット番号を指定したいなと思ったので、バッチを作りました。
チェックアウトしたフォルダで実行して、チケット番号をカンマ区切りで複数指定したら勝手にその前後のソースをフォルダにエクスポートして、StepCounterを使って差分取ってくれます。
※コミットログコメントにチケット番号が書いてあることが前提です。
因みにこちらのStepcounterを利用しています。
マジで感謝です。

https://github.com/takezoe/stepcounter

あんまイケてないけど、とりあえず晒してみます。

ECHO OFF
cd %~dp0
ECHO #######################################
ECHO # 
ECHO # ソース差分取得用
ECHO # 新旧ファイル取得バッチ
ECHO # 
ECHO # リビジョンを指定して新旧ファイルの
ECHO # 取得を行えます。
ECHO # Copyright HAL 2016
ECHO # 
ECHO #######################################
REM 
REM 使い方
REM 
REM  チェックアウトしたSVNのフォルダにこのバッチファイルをコピーし、
REM  stepcounter-3.0.3-jar-with-dependencies.jarを同じ階層に置く。
REM  ダブルクリックで実行後、変更した際のリビジョンをカンマ区切りで入れると
REM  その変更前後の差分を取得してcount.xlsに出力する
REM 
ECHO.
ECHO.
ECHO 対象チケット番号を指定してください(カンマ区切り複数指定可)
SET /P TARGET_TICKET=対象チケット番号:
ECHO 出力フォーマットを指定してください(text、html、excel)
SET /P EXPORT_FORMAT=出力フォーマット:
if "%EXPORT_FORMAT%" == "excel" (
    SET EXPORT_TYPE=xls
) ELSE IF "%EXPORT_FORMAT%" == "html" (
    SET EXPORT_TYPE=html
) ELSE IF "%EXPORT_FORMAT%" == "text" (
    SET EXPORT_TYPE=txt
)
ECHO.
ECHO.
setlocal ENABLEDELAYEDEXPANSION
rd /s /q old
rd /s /q new
mkdir old
mkdir new
ECHO. >changes.list
ECHO. >changes.log
REM チケット番号の件数分繰り返し
FOR %%i IN (%TARGET_TICKET%) DO (
    svn log --search "refs #"%%i | find "|" > ticket.lst
    ECHO ****対象リビジョン(#%%i)****
    FOR /F "tokens=1,2,3,4* delims=|" %%a IN (ticket.lst) DO (
        
        REM リビジョンの逆順を保持
        SET REV_TEMP= %%a
        ECHO !REV_TEMP:r=!
        SET TARGET_REV=!REV_TEMP:r=!,!TARGET_REV!
    )
    ECHO ****変更後ファイル取得(#%%i)****
    FOR %%a IN (%TARGET_REV%) DO (
        
        REM リビジョンの逆順を保持
        SET /A REV_TEMP= %%a
        SET REV_LIST=!REV_TEMP!,!REV_LIST!

        REM 新しい方のファイルをExport
        svn log -r %%a >>changes.log
        echo LIST >> changes.log
        FOR /F "usebackq tokens=1,2" %%t IN (`svn diff -c%%a --summarize`) DO (
         echo %%u >> changes.log
         mkdir new\%%~pu
         svn export -r %%a --force %%u %~dp0\new\%%~pu%%~nxu
        )
    )
    REM リビジョンの新しい順にリストを作成
    FOR %%a IN (%REV_LIST%) DO (
        SET /A REV_TEMP= %%a - 1
        SET REV_LIST=!REV_TEMP!,!REV_LIST!

        FOR /F "usebackq tokens=1,2" %%t IN (`svn diff -c%%a --summarize`) DO (
         echo !REV_TEMP!,%%~pu%%~nxu >> changes.list
         mkdir old\%%~pu
        )
    )
    ECHO ****変更前ファイル取得(#%%i)****
    REM リストを元にEXPORT
    FOR /F "skip=1 tokens=1,2* delims=," %%a IN (changes.list) DO (
        svn export -r %%a --force %%b %~dp0\old\%%b
    )
    ECHO ****出力が完了しました(#%%i)****
    ECHO ****差分出力(#%%i)****
    java -cp stepcounter-3.0.3-jar-with-dependencies.jar jp.sf.amateras.stepcounter.diffcount.Main -format=%EXPORT_FORMAT% -output=%%i.%EXPORT_TYPE% new old
    ECHO ****差分取得が完了しました(#%%i)****
    )
ECHO.
ECHO.
pause