
Android Developers > スタートガイド > Kotlin と Android をゼロから学ぶ > Unit2 – レイアウト >Unit2-1 第5回 の内容をまとめたものです(翻訳ではありません)
Unit2 – 1 第5回 のポイント

- KotlinコードからUI部品に設定した値を参照したり、修正したりする方法
- findViewById() のかわりに View Binding を使う方法
- KotlinでDouble型を使って小数の計算をします
- 数値を通貨の形に直して表示する方法
- Stinrg型(文字列型)の使い方
- Android Studio の Logcat 機能の使い方(アプリの問題を見つけるのに使う)
プロジェクトに含まれるファイルの確認
Android Studio のプロジェクトに含まれるファイルを確認しておきますね
- 前回UIを作成したプロジェクトを開いておきます
- 使用するファイルは Project(ウィンドウの左)から開くことができます。Project の表示は Android にしておきます
重要なフォルダはこの3つですね
- Kotlinファイル(たとえばMainActivity.kt)は java フォルダ以下にあります(Kotlinコードも java フォルダで管理されています)
- アプリのリソース(たとえば activity_main.xml , strings.xml など)は res フォルダ以下にあります
- Gladle(Android Studio で使われているアプリのビルドを自動化するツール)関連のファイル(build.gradle など)はGradle Scripts 以下にあります
- アプリのコードやリソースを変更すると、Gradle はそれを検知してちゃんとビルドできるように自動で再設定します
- アプリをエミュレータや実機にインストールして実行をコントロールするのも Gradle が行っています
View Binding を設定する
これからは View Binding ですね!
- KotlinコードからUI部品にアクセスするのに今までは findViewById() メソッドを使っていました
- findViewById() は使用するUI部品ごとに呼び出さないといけないのでUI部品が増えてくるとどうしてもゴチャゴチャしてしまいます
- そこでこれからは View Binding を使うようにします
- 最初に View Binding を使えるようにしておけば、findViewById() よりも速く簡単にUI部品にアクセスできます
- View Binding を使うには、まずプロジェクト毎に Gradle への設定が必要です
- モジュールの build.gradle を開きます(Project から Gradle Scripts > build.gradle (Module:~) をダブルクリック )
- ファイルの android セクションに次の設定を追加します(Android Studio 4.0以降)
android { buildFeatures { viewBinding = true } }
- build.gradle の書き換えをするとウィンドウの上の方にメッセージが出て「Sync Now」というリンクが表示されます。これをクリックします
- ウィンドウの下の方に「Gradle sync finished」と出れば作業完了です。プロジェクトで View Binding が使えるようになったので build.gradle は閉じて構いません
View Binding を使う
Kotlin で View Binding を使うには、必ず最初に次のようなコードを書いておきます
class MainActivity : AppCompatActivity() { lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) } }
- バインディング用のクラスの名前は snake_case で書かれたレイアウトファイル(XMLファイル)名を PascalCase に直して最後に Binding を付けたものです。 snake_case や PascalCase camelCase についてはこちら です
- findViewById() と違って、onCreate() の中で一度 binding というインスタンスを作っておけば準備完了です。上のコードにある lateinit については「Unit1とView Binding」 の最後のほうにまとめてあります。その他、詳しくはこちらをどうぞ
- setContentView() では UI部品(View)の階層の root を指定します
- これで findViewById() を使わなくても binding オブジェクトですべてのUI部品(View)を参照できるようになります
- UI部品(View)ごとに参照するための変数をひとつひとつ用意する必要もありません。bindingオブジェクトを使って直接呼び出して構いません
findViewById() を使うとこんな感じですね
// Old way with findViewById() val myButton: Button = findViewById(R.id.my_button) myButton.text = "A button"
View Binding で置き換えて少しすっきり
// Better way with view binding val myButton: Button = binding.myButton myButton.text = "A button"
とてもすっきり!
// Best way with view binding and no extra variable binding.myButton.text = "A button"
チップの金額を計算するアプリを作ってみる
UI部品の情報をKotlinコードから参照して計算を行うアプリを作ります
ボタンに click listener を設定する
ユーザがボタンをタップしたことを検出します。click listener という機能を使いますね
binding.calculateButton.setOnClickListener{ calculateTip() }
- このコードを
MainActivity.kt
のonCreate()
メソッドの中に記述します - ボタンがタップされたことを検知するのに click listener 機能を追加します。setOnClickListener() メソッドを使います
- タップが検知されたら calculateTip() という関数を呼び出すように指定しています
- とりあえず空の calculateTip() を宣言しておくと次のようになります
class MainActivity : AppCompatActivity() { lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.calculateButton.setOnClickListener{ calculateTip() } } fun calculateTip() { } }
calculateTip() メソッドを作る
EditText(UI部品)に記入された金額を小数として読み込みます
fun calculateTip() {
val stringInTextField = binding.costOfService.text.toString()
val cost = stringInTextField.toDouble()
}
- EditText に入力されている内容を文字列(String型)で変数 stringInTextField に読み込みます
- EditText の text プロパティは Editable型 です。String型でないので toString() で変換しないといけません
- stringInTextField 変数の内容(String型)を小数(Double型)に変換して cost変数に読み込みます
- レイアウトでUI部品(View)につけられたid名(リソースID名, snake_case で付けられていることが多い)は 、View Binding では camelCase に変換した名前で利用します。たとえばid名が cost_of_service のUI部品は binding.costOfService.~ として参照します
UIのラジオボタンを読み取る
ラジオボタンの選択にあわせてチップの率を設定します
fun calculateTip() {
val stringInTextField = binding.costOfService.text.toString()
val cost = stringInTextField.toDouble()
val selectedId = binding.tipOptions.checkedRadioButtonId
val tipPercentage = when (selectedId) {
R.id.option_twenty_percent -> 0.20
R.id.option_eighteen_percent -> 0.18
else -> 0.15
}
}
- RadioGroup のインスタンスの checkedRadioButtonId を使って、選択されている RadioButton の id(リソースID)を変数 selectedId に取得します
- when 文を使って、選択されているボタンのリソースIDに応じた率を変数 tipPercentage に取得します
チップの金額を計算をする
数学のライブラリを使って小数の計算をします
fun calculateTip() {
val stringInTextField = binding.costOfService.text.toString()
val cost = stringInTextField.toDouble()
val selectedId = binding.tipOptions.checkedRadioButtonId
val tipPercentage = when (selectedId) {
R.id.option_twenty_percent -> 0.20
R.id.option_eighteen_percent -> 0.18
else -> 0.15
}
var tip = tipPercentage * cost
val roundUp = binding.roundUpSwitch.isChecked
if (roundUp) {
tip = kotlin.math.ceil(tip)
}
}
- 変数 tip にチップの額を計算して代入します
- スイッチの On/Off(true/false)を isChecked を使って取得して、変数 roundUp に代入します
- スイッチがOnのとき、変数tip を kotlin.math ライブラリの ceil() を使って切り上げ処理します
- kotlin.math.ceil() のように書くことで、import せずに使うことができます
- import するときは
import kotlin.math.*
とします
- import するときは
計算結果を書式を整えて表示する
計算結果を strings.xml に登録してある文字列に組み込んで表示してみますね
strings.xml を開いて、tip_amount のところを次のように書き換えます
<string name="tip_amount">Tip Amount: %s</string>
calculateTip関数 は次のように書き換えます
fun calculateTip() {
val stringInTextField = binding.costOfService.text.toString()
val cost = stringInTextField.toDouble()
val selectedId = binding.tipOptions.checkedRadioButtonId
val tipPercentage = when (selectedId) {
R.id.option_twenty_percent -> 0.20
R.id.option_eighteen_percent -> 0.18
else -> 0.15
}
var tip = tipPercentage * cost
val roundUp = binding.roundUpSwitch.isChecked
if (roundUp) {
tip = kotlin.math.ceil(tip)
}
val formattedTip = NumberFormat.getCurrencyInstance().format(tip)
binding.tipResult.text = getString(R.string.tip_amount, formattedTip)
}
NumberFormat
クラス(ドキュメント)getCurrencyInstance()
メソッド・・・通貨としてフォーマット処理してNumberFormat型のインスタンスを返すformat()
メソッド・・・Double型の数値を処理してString型で返す
NumberFormat
は同じ名前のものが複数あるため Android Studio で赤く表示されます(自動で import を決定できない)。クリックして NumberFormat (java.text) を選んでおきますgetString()
・・・strings.xml(文字列リソース)から文字列を取得する関数(ドキュメント)getString(リソースID)
・・・リソースIDで指定された文字列を取得します- strings.xml に記述された文字列に %s などを使うことで書式設定することもできる。書式設定されている場合は
getString(リソースID, 値, 値, ・・・)
のように使います
- 同じようなものに
getText()
というのもあります(少し仕様が異なる)
getString(R.string.tip_amount, formattedTip)
は、strings.xml の tip_amount から文字列を取得、その文字列の%s
の部分に変数 formattedTip の内容を代入しています- 最後に TextView の text プロパティに代入することで結果を表示しています
結果表示用のTextViewにプレースホルダーを設定する
プレースホルダー(placeholder)は、Android Studio でレイアウトをつくるときに目安としてとりあえず表示しておくテキストのことです
- 結果表示用の TextView(リソースIDは tip_result)にプレースホルダーを設定してみます
- TextView の text 項目の内容は、計算結果が出てからKotlinコードで設定しています。計算前に何かが表示されている必要はないので削除してしまいます
要らなくなった項目を削除します
activity_main.xml
を開きます (app > res > layout > activity_main.xml)- TextView の id が tip_result のブロックを探します
- android:text=”@string/tip_amount” の行を削除します
プレースホルダーを設定します
さきほど削除したあたりに次の行を追加します
tools:text="Tip Amount: $10"
プレースホルダーに設定した文字列が使われるのは Android Studio の Layout Editor の中だけ。アプリを実行するときには使わないので strings.xml に登録しなくて大丈夫。ハードコードしちゃっていいかも
テストとデバッグ
デバッグに Logcat を使ってみますね
- アプリをエミュレータで動かすときに Run > Run ‘app’, でなく Run > Debug ‘app’ で実行します
- 下の方にある「Logcat」ボタンをクリック(または View > Tool Windows > Logcat )
- アプリがクラッシュしたときの原因究明に Logcat に表示される内容が役立ちます
- Logcat にはスタックトレースが表示されています(クラッシュするまでに呼ばれたメソッドがつぎつぎと表示されている)
- たとえば空の入力をそのまま toDecimal にかけるとクラッシュしてしまいます(小数に変換できない)。Logcat を追えば toDecimal でエラーが起きていることを突き止めることができます
- クラッシュの原因に応じて、たとえば toDouble のかわりに toDoubleOrNull() を使って小数に変換できない場合の処理を付け加えるなど、コードの修正を行います
- null は「値が存在しない」ことを示します
警告をまとめてチェック
- Android Studio 上で、グレーになっていたり下線があったりしたときはマウスカーソルをのせてみます。アドバイスが出るので必要に応じて修正します(修正も選択肢から選ぶだけで自動的に修正されることが多い)
- すべての警告を一覧にして表示するには Analyze > Inspect Code… とします。チェックしながら問題点をつぶしていくことができます
ここで作成したコード
MainActivity.kt
package com.example.tiptime
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.tiptime.databinding.ActivityMainBinding
import java.text.NumberFormat
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.calculateButton.setOnClickListener { calculateTip() }
}
private fun calculateTip() {
val stringInTextField = binding.costOfService.text.toString()
val cost = stringInTextField.toDoubleOrNull()
if (cost == null) {
binding.tipResult.text = ""
return
}
val tipPercentage = when (binding.tipOptions.checkedRadioButtonId) {
R.id.option_twenty_percent -> 0.20
R.id.option_eighteen_percent -> 0.18
else -> 0.15
}
var tip = tipPercentage * cost
if (binding.roundUpSwitch.isChecked) {
tip = kotlin.math.ceil(tip)
}
val formattedTip = NumberFormat.getCurrencyInstance().format(tip)
binding.tipResult.text = getString(R.string.tip_amount, formattedTip)
}
}
strings.xml
<string name="tip_amount">Tip Amount: %s</string>
activity_main.xml で修正するところ
...
<TextView
android:id="@+id/tip_result"
...
tools:text="Tip Amount: $10" />
...
build.gradle で修正するところ
android {
...
buildFeatures {
viewBinding = true
}
...
}
まとめ
お疲れ様でした!
- View Binding を使ったUI部品の利用
- Double型と小数の計算(kotlin.math.*)
- ラジオボタンの使い方。Radio Group , RadioButton , checkRadioButtonId
- NumberFormat クラスを利用して数値を通貨の形式に変換する。NumberFormat , getCurrencyInstance() , format()
- 書式を利用した文字列の処理
- Logcat によるスタックトレースとデバッグ
- コード中の警告をまとめてチェックする( Analyze > Inspect Code )
参考
Double
data type in Kotlin- Numeric data types in Kotlin
- Null Safety in Kotlin
- App Manifest
View
bindingNumberFormat.getCurrencyInstance()
- string parameters
- testing
- Logcat
- Analyze a stack trace