- 投稿日:2020-06-30T19:17:28+09:00
Flutter(Dart)でValidationを実装する
バリデーション
validation_helper.dart// 英数のみ、6桁のバリデーション class Validator { Validator._(); static bool isValidHoge(String hoge) { const _hogeRegExpString = r'^[0-9a-zA-Z]{6}$'; return RegExp(_hogeRegExpString, caseSensitive: true).hasMatch(hoge); } }接頭辞
r
を付することでraw(生)文字列として扱うことができます。(エスケープシーケンス回避のため)参考記事
1. form用正規表現判定/備忘
2. RegExp constructor実装例
example_notifier.dart// freezedを使用した実装例 class Hoge { void onChangeHoge(String hoge) { state = state.copyWith(hoge: hoge); if (Validator.isValidHoge(hoge)) { state = state.copyWith(isEnable: true); } else { state = state.copyWith(isEnable: false); } } }
- 投稿日:2020-06-30T17:06:51+09:00
agora.io RENDERモード (映像表示モード)
概要
agora.io の VideoSDK for Native では、RENDERモードという機能により、画面上の映像表示設定をすることができます。
本記事では、Androidを利用して、本機能の利用方法および各モードの映像表示を説明します。RENDERモードの利用方法
RENDERモードは、以下のAPIコール時に引数で指定します。
setupLocalVideo と setupRemoteVideo は、ビデオ通話開始時にそれぞれ自拠点、他拠点の映像表示を初期化するためのAPIです。
setLocalRenderMode と setRemoteRenderMode は、初期化後に映像表示を変更するためのAPIです。// Java private void setupLocalVideo() { // Enable the video module. mRtcEngine.enableVideo(); // Create a SurfaceView object. private FrameLayout mLocalContainer; private SurfaceView mLocalView; mLocalView = RtcEngine.CreateRendererView(getBaseContext()); mLocalView.setZOrderMediaOverlay(true); mLocalContainer.addView(mLocalView); // Set the local video view. VideoCanvas localVideoCanvas = new VideoCanvas(mLocalView, VideoCanvas.RENDER_MODE_HIDDEN, 0); mRtcEngine.setupLocalVideo(localVideoCanvas); }各RENDERモードについて
RENDERモードは、2020年6月現在3種類あります。
各モードについて説明します。
※2拠点入室時のほうが各モードの差異がわかりやすいので、1拠点入室時と2拠点入室時両方の画面キャプチャを載せています。HIDDEN
余白なく、映像を表示します。
カメラ映像と画面のアスペクト比が一致しない場合、カメラ映像の一部をトリミングして、表示します。FIT
カメラで取得したアスペクト比のまま、映像を画面上に表示します。
カメラ映像と画面のアスペクト比が一致しない場合、画面上に余白 (黒い部分) ができます。FILL
余白なく、映像を表示します。
カメラ映像と画面のアスペクト比が一致しない場合、映像を拡大もしくは収縮させて、画面全体に映像が表示されるようにします。最後に
- 投稿日:2020-06-30T14:51:51+09:00
【Kotlin】初心者向け: 画面回転やスリープ後のUIの保持
はじめに
今回は画面を回転させたときや本体をスリープさせた後、ビューの一部がその前後で変わってしまう問題について取り組みます。下の簡易的なボタンカウンターアプリでその様子が確認できます。縦画面でカウントアップさせたとき、横に切り替えると数字が0に戻ってしまうのです。
原因
この問題の原因は、画面の向きを変えるなどの諸動作によって、Activityがライフサイクルを一からやり直す事にあります。これによってハードウェアは、柔軟にかつ素早くユーザーのアクションを反映させることが出来ますが、同時にUIまでもが破棄されて初期化されてしまいます。これを防ぐためには、ライフサイクルが終わる前に、UI等の必要な情報を一時的に保持しておく必要があります。
*ライフサイクルについてはこちら:https://qiita.com/K4N4/items/2f4babe2bab67ddacf89
onSaveInstanceState
一時的に情報を保持する方法の一つに、onSaveInstanceStateを利用するというものがあります。この方法はあまり大容量ではない情報を、簡単に保持する時に利用します。使い方は非常にシンプルです。
override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState?.putInt("key", i) } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) i = savedInstanceState?.getInt("key", 0) textView.text = i.toString() }
onSaveInstanceState
メソッド(データを保持する)とonRestoreInstanceState
メソッド(保持したデータを復元する)を呼び出し、put〇〇
とget〇〇
で実際にデータの受け渡しを行っています。
onSaveInstanceState
メソッドのoutState?.putInt("key", i)
というのはoutState
(データをメモリに保存する実体)に"key"(任意)というキーを使って、iというIntの値を保持する。という事を指しています。キーとはその名の通り鍵の事です。呼び出し側と共通のキーを持たなければ、データの受け渡しは出来ません。
onRestoreInstanceState
メソッドのi = savedInstanceState?.getInt("key", 0)
は、Activityが破棄されて値が初期化されてしまった i に、保持したデータの実態savedInstanceState
からgetInt
メソッドで共通のキーを持つ値を取り出しています。getInt
の第二引数は、putInt
がnullの場合の値を設定します。
この二つのメソッドとその中身を入れるだけで、簡単に設定した値の保持が行えます。onSaveInstanceStateとonRestoreInstanceStateとライフサイクル
先述したようにこの二つのメソッドを使えば、簡単にUIの保持を行えます。では、この二つのメソッドはActivityのライフサイクルにおいて、どの段階で機能してるのでしょうか?
それを示すのがこの図です。この図はActivityの基本的な工程に、今回用いた二つのメソッド等を加えたものです。この図の通り、onSaveInstanceState
はonPauseの後に値の保持を、onRestoreInstanceState
はonStart
の後に値の復元を行っています。onRestoreInstanceState
の値はonCreate
に影響を及ぼさなかったり、onSaveInstanceState
が呼び出されたら必ずアプリが止まることなどが分かります。実際にこのライフサイクルを可視化してみる
class MainActivity : AppCompatActivity() { private var i : Int = 0 override fun onCreate(savedInstanceState: Bundle?) { Log.d(TAG, "onStart: called") super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) button.setOnClickListener { i += 1 textView.text = i.toString() } } override fun onStart() { Log.d(TAG, "onStart: called") super.onStart() } override fun onResume() { Log.d(TAG, "onResume: called") super.onResume() } override fun onPause() { Log.d(TAG, "onPause: called") super.onPause() } override fun onSaveInstanceState(outState: Bundle) { Log.d(TAG, "onSaveInstanceState: called") super.onSaveInstanceState(outState) outState.putInt("key", i) } override fun onRestoreInstanceState(savedInstanceState: Bundle) { Log.d(TAG, "onRestoreInstanceState: called") super.onRestoreInstanceState(savedInstanceState) i = savedInstanceState.getInt("key", 0) textView.text = i.toString() } override fun onStop() { Log.d(TAG, "onStop: called") super.onStop() } override fun onRestart() { Log.d(TAG, "onRestart: called") super.onRestart() } override fun onDestroy() { Log.d(TAG, "onDestroy: called") super.onDestroy() } }MainActivityの中身をこのようにすることで、実際にどこでデータの保持等が行われているかログを通して可視化することが出来ます。このソースコードでは、上で示したカウントアップ機能のためにデータのやり取りを行っています。上手くいけば、各工程ごとにログに出力がなされます。是非回転などさせて試してみてください。
- 投稿日:2020-06-30T09:57:10+09:00
Ionic & golangでリアルタイム大喜利アプリを作りました
アプリのリリースまでなんとか漕ぎ着けたので、宣伝も兼ねて使ったフレームワークについて紹介します。
どんなアプリか
写真で一言アプリです。
特徴的なのはリアルタイムで集まった人たちが、お互いに投稿し合ったボケを評価し合う部分です。マッチングした最大10名が、制限時間内に一つのお題写真に対してボケを投稿します。
投稿はお互いに評価することができ、一定数イイネを集めると一本獲得になります。こちらからダウンロード可能です。
よかったら遊んでみてください。android
https://play.google.com/store/apps/details?id=jp.co.popbits.funnyappios
https://apps.apple.com/jp/app/funny-one/id1515018792アプリ側
ionic
アプリのベースはIonic Frameworkで作成しました。
ざっくりいうと、
webview + Angular
にネイティブっぽいリッチな見た目のコンポーネントと、共通化されたプラグインが利用できます。
プラグインの例としてはSocial Sharingやdeeplink等があります。Angularではなく、vueやreactで記載することも可能なようです。
※reactは最近入ったらしい。
android, ios同一のコードで記載できるので工数削減になりました。
環境構築やビルドなどもかなりスムーズにできました。
(多少のつまづきはありますが、ネイティブアプリの環境構築にはつきものです)懸念点として、webviewベースであるためパフォーマンスがあまり良くないのではないか気になりましたが、
端末の性能も上がってきているからか、思ったよりサクサク動いてくれています。createjs
大喜利バトルの部分はcreatejsで作成しました。
https://createjs.com/Adobe Animate
https://www.adobe.com/jp/products/animate.html
でcreatejs用にアニメーションを書き出しています。アプリへのつなぎ込みには色々と試行錯誤が必要でしたが、細かい話になるので割愛します。
大喜利バトルでは後述するgolangサーバとwebsocketで接続し、イベント毎にアニメーションを変化させます。
サーバ側
golang
リアルタイムや並列処理に強そうなので採用しました。
pythonもよく書くのですが、golangは型があるのでバグが起きづらい、
パフォーマンスが良いなどのメリットがあります。コードは誰が書いても同じ書き方になるよう矯正され、必然的に可読性が高くなります。
一方、リアルタイム部分(websoket接続)はgoroutine と channelを駆使して作るのですが、
正直自分以外理解できないコードになってしまったと思います。並列処理そのものが難しいのもあると思います。
通常の処理とは違いコードの上から追っていくだけでは読み解くことができず、
どこからchannnelにアクセスが有るなど把握していないといけません。並列処理は色々な書き方ができるので正解がない感じです。
gin
https://github.com/gin-gonic/gin
デファクトスタンダードっぽいので採用しました。
良さげです。melody
https://github.com/olahol/melody
websocketを扱う際にいい感じの機能を提供してくれます。
sqlx + squirrel
https://github.com/jmoiron/sqlx
クエリ結果をstructsにマッピングしてくれます。https://github.com/Masterminds/squirrel
SQLクエリビルダーです。
where句の検索条件をqueryオブジェクトに対して注入していく、
みたいなことがやりたい場合はこれでSQLを作ります。最初はgormを使っていたのですが、最終的に上の構成に書き換えることにしました。
gormは人気があるようなので使っていたのですが、挙動が直感的でなかったのと、
思いも寄らない部分で大量のSQLを発行されていたことがあったりで、嫌になってやめました。まとめ
色々紹介しましたが、ionicはかなり気軽にアプリが作成できるので本当におすすめです。
- 投稿日:2020-06-30T00:40:59+09:00
【Android】EditTextで日本語入力⇔英語入力の相互切替ハンドリングは難しい
初めに
すみません、解決策を提示する内容ではないです。
Emailアドレスの入力時は
inputType:textEmailAddress
で英字入力モード
テキスト入力時はを初期表示にしたい
inputType:text
で日本語入力モードを初期表示にしたい
と格闘し、結論無理と行き着きました。。非常に時間を浪費し、悲しみを覚えたので、同じような悩みを抱えて嵌り続ける方がもしいたら早めに切り上げるよう共有したくメモします。
いやいや出来るよ!という賢者がいらっしゃったら是非ご教示下さい。。事象
以下のように、
TextInputEditTextにを指定して初期のソフトキーボードを英字入力にしようとしたのですが、
別のEditText等で日本語入力に切り替わると、以降inputType:textEmailAddressの初期表示も日本語入力になってしまいます。activity_main.xml<com.google.android.material.textfield.TextInputLayout android:id="@+id/login_input_email_layer" android:layout_width="335dp" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:minHeight="60dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/login_error_message_layer"> <com.google.android.material.textfield.TextInputEditText android:id="@+id/login_input_email" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="@string/login_email_placeholder" android:inputType="textEmailAddress" // 何故か効かない。。。 android:textColor="@color/font_color_black" android:textSize="22sp" android:maxLength="50" android:maxLines="1" /> </com.google.android.material.textfield.TextInputLayout>最後に
内容がなく恐縮です。。
InputFilter
を使用してせめてメールアドレス形式のみ入力可能にしようかと思いましたが、
ユーザビリティがかなり下がったので見送りました。
いつか日本語も正しくハンドリング出来るようになると嬉しいですね。。
海外企業なので厳しいでしょうけど。。。