PR

Kotlinの実装時につまずいたこと、気づいたことをまとめて紹介します

Androidアプリ

この記事ではKotlin初心者の私が、つまずいたことを書き連ねています。
目次から参考になりそうなのがあったら見ていってくださいませ

姫野秀徳

組み込みエンジニアとしてメーカー勤務10年。
第二種電工事士、基本情報技術者、Code.org認定教師。
microbitを用いた電子工作&プログラミング教室を開校。
組み込み開発の実際を発信するyoutube活動も行う。

無料で学べる組み込み開発オンラインスクールはじめました!
https://kumicla.tatepro.com

姫野秀徳をフォローする

JSONバリューに小数点の桁数を指定して格納する

val array = arrayOf(1.2, 2.34, 5.678)
val test = JSONObject()
test.put("TEST", array[2])
test.put("TESTa", "%,.1f".format(array[2]))
test.put("TESTb", "%,.2f".format(array[2]))
test.put("TESTc", "%,.3f".format(array[2]))
test.put("TESTd", "%,.4f".format(array[2]))

//{"TEST":5.678,"TESTa":"5.7","TESTb":"5.68","TESTc":"5.678","TESTd":"5.6780"}

値以上の桁を取ろうとすると0埋めされるんですね。

配列要素が文字列だった場合

val array = arrayOf(1.2, 2.34, "5.678")
val test = JSONObject()
test.put("TEST", array[2])
test.put("TESTa", "%,.1f".format(array[2].toString().toDouble()))
test.put("TESTb", "%,.2f".format(array[2].toString().toDouble()))
test.put("TESTc", "%,.3f".format(array[2].toString().toDouble()))
test.put("TESTd", "%,.4f".format(array[2].toString().toDouble()))

//{"TEST":"5.678","TESTa":"5.7","TESTb":"5.68","TESTc":"5.678","TESTd":"5.6780"}

文字列型にしてdouble型にして…何かもっと簡単な方法がありそうな気がする…。

アプリ動作中にテキストビューの中身を変える

  1. フラグメントのテキストビューをメイン UI に渡す
  2. メイン UI のグローバル変数にフラグメントのテキストビューを格納する
  3. メイン UI 上で処理してグローバル変数に値をセットする
  4. フラグメントのテキストビューが自動で更新される

という流れです。

フラグメント生成時だけの場合

フラグメントの読み込み時だけテキストビューを設定するのであれば、フラグメントのONCREATE 関数の中でテキストビューを宣言した時に、テキストビューのテキストメソッドを用いて、メインUIの値を格納してやれば良い。

メイン UI 内の関数にて表示したいテキストをリターンで返すような関数を作っておいて、それをフラグメントから呼び出すことで使用できる。

メインスレッド以外からUI要素にアクセスするとアプリが落ちる

メインアクティビティ{
  ~~
  ~~
  fun aaa(str: String){
          val textView: TextView = findViewById(R.id.txt)
          textView.text = str
  }
}

スレッド{
  ~~
  ~~
 aaa("insert text")
}

これでスレッドからinsert textを引数にメインアクティビティの関数を実行、この中でUIをいじるからOK…とはならない。

考え方として、スレッド間の通信はハンドラとルーパーを用いてやりとりしなければならない。

ルーパーとハンドラの簡略化として、runOnUiThreadを用いるとよい。書き方は↓。

メインアクティビティ{
  ~~
  ~~
  fun aaa(str: String){
          val textView: TextView = findViewById(R.id.txt)
          textView.text = str
  }
}

スレッド{
  ~~
  ~~
  runOnUiThread{
     aaa("insert text")
  }
}

runOnUiThreadでくくるだけでうまくいく。

runOnUiThreadはルーパーやハンドラの記述をする必要なくスレッド間通信を実現できるすぐれもの!

runOnUiThreadって何?

runOnUiThreadの中身を見てみます。

    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

UIスレッドだったらそのまま実行し、それ以外だったらハンドラに中身を送っているんですね。

勉強になるー…><

UI要素とは

UI要素はレイアウトファイルで配置するボタンやテキストビュー、スピナーなどですね。

これらUI要素はIDを振ることで一意にそれを定義することができます。

こうして定義したIDはfindViewByIdメソッドを使用して取得することができ、変数に格納することができます。

そしてUI要素を格納した変数に対して、ボタンならsetOnClickListnerメソッド、テキストならtextメソッドなどの処理を行います。

findViewByIdは、指定したidの要素を検索して、一致したらそのビュー要素を返すメソッド。

変数にビュー要素を入れて、その変数にtextメソッドでテキスト入れるとか。

例えばこんな感じ。

var tv = findViewById(R.id.tatepro) //tateproというIDのビュー要素をtv変数に格納
tv.text = "kotlin" //tv(=tateproビュー要素)のテキストに"kotlin"を入れる

こうすれば動くんだー、と思って無意識で使ってきたものの、tvに何が入ってるか気にしたことがなかった…。

つまるところ、上記のコードはただのメソッドチェーンなわけで

findViewById(R.id.tatepro).text = "kotlin"

これと同義なわけですよね。

スコープ範囲(存在範囲)

ここで、UI要素はOnCreate関数内でのみアクセスすることができる点に注意が必要です。

OnCreate関数以外でfindViewByIdメソッドは使えないです。

え、じゃあ外からUI要素をいじりたいときはどうすればいいの?

ってなりますよね。少なくとも私はこれでだいぶ苦労しました。

UI要素を格納した変数をどこからでも参照できるようにするには

Bluetoothやタイマといった別スレッド(スレッドは後述します)からメインアクティビティのUIを変更したい、フラグメントで別のUI要素にアクセスしたいときがあります。

私のやり方が正解か分からないですけど、UI要素を格納する変数をメインアクティビティの上部にlateinitのグローバル変数として宣言させました。

OnCreate関数ではグローバル変数にUI要素を格納させてやることで、どこからでもアクセスできる変数にさせることができました。

フラグメントからメインにビュー要素を渡す

フラグメントのonCreateViewでビュー要素をメインアクティビティのグローバル変数に入れてやる。

フラグメントのライフサイクルが、とかそもそもグローバル変数を使う設計は推奨されないとか、たぶん、このやり方はよくないのだと思う。

ただ今の私の知識だとこのようにしか作れず、もう少し技術が深まったら別途更新します。

メインアクティビティはグローバル変数を介することでフラグメントのビュー要素に対してメソッドを実行できるわけです。

Androidstudioでアプリアイコンを設定する

Kotlin関係ないが…まあ許してちょ。

アプリアイコンはimage assetにて設定する。公式情報は↓

Create app icons  |  Android Studio  |  Android Developers
Call on material icons with Compose or use Image Asset Studio, which helps you generate your own app icons from material...
  • res フォルダを右クリックして、[New] > [Image Asset] を選択。
  • 前面階層と背面階層を設定できます。
  • まず前面階層(Foreground Layer)について、Source Asset欄のAsset Type: imageを選択。
  • Pathにて画像を選択し決定。
  • Scalingで大きさを設定します。
  • 背面についても同様の操作です。
  • Nextボタンを押すと確認画面が表示されるので、Finishボタンを押して設定完了です。

アプリで取得した情報をスマホに保存読込する

スマホに情報保存させるには、SharedPreferences API を使用するとよい。

Key-Value ペアを格納したファイルをポイントし、Key-Value ペアの読み書きを行うためのシンプルなメソッドです。
Android公式情報はこちら

保存

KEYというキーに、文字列型のvalueというバリューを保存させる例です。

val sharedPreff = getSharedPreferences(sharedPrefFile, Context.MODE_PRIVATE)
sharedPreff.edit().putString("KEY", "value").apply()
  • getSharedPreferencesで、sharedPrefFileという名称のファイルを指定(なければ作成される)
  • editで編集を開始
  • putStringでキー/バリューをセット
  • applyで保存

という流れです。実際自分たちがいじくるのはputStringの中身だけですね、簡単簡単。

読込

KEYというキーからバリューを読みこむ例です

val sharedPref = getSharedPreferences(sharedPrefFile, Context.MODE_PRIVATE)
var temp = sharedPref.getString("KEY", "no data").toString()
  • getSharedPreferencesで、sharedPrefFileという名称のファイルを指定(なければ作成される)
  • getStringでキー(上記例では第一引数のKEY)を指定
  • 指定キーがなかった場合のデフォルト値を指定(上記例では第二引数のno data)
  • これをtemp変数に文字列型に変換して入れています。

これも保存と似てますね。

この内容が良いと思ったら、いいね💖を押して教えてください!

組みクラ – 組み込み開発を学び、未来を創るエンジニアへ
無料で学べる!組み込み開発の基礎から応用までをマスターできるオンラインスクール「組みクラ」

組み込みエンジニアは、あらゆる産業の基盤を支える重要な職業です。
私たちタテプロは、次世代の組み込みエンジニアを育成し、業界の競争力を高めるために活動しています。
あなたも、無料で学べる「組みクラ」で、組み込み開発のスキルをしっかり身につけ、キャリアを加速させましょう!
今すぐ学び始めて、将来の自分をステップアップさせる第一歩を踏み出しませんか?

\立プロとLINEで友だちになりませんか/
立プロと友だちになる
Androidアプリ
シェアしていただけると嬉しいです!

コメント

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