カテゴリー
Kotlin言語

Unit1 と View Binding(Kotlin と Android の基本まとめ)

チュートリアル Unit1 の内容、チュートリアルで作成したKotlinコードのまとめです。チュートリアルのKotlinコードでは View(UI部品)へのアクセスに findViewByID() を使っていますが、findViewByID() を使わずに View Binding で書き替えたバージョンも載せておきました


Android Studio を使うのに必要な準備

Android Studio をセットアップします

  • Android Studio のインストール
  • AVD(Android Virtual Device)の作成

Android Studio でアプリを作る手順

チュートリアル Unit1 で説明している手順です。View Binding についてはこちらで追加しました(Android Developers で公開されているチュートリアルにはありません)

  • プロジェクトを作成します
  • プロジェクトに import の自動化設定をします
    1. File > New Project Settings > Preferences for New Projects
    2. Other Settings > Auto Import
  • プロジェクトの build.gradle(モジュール)に View Binding の設定をします(View Binding を使う場合)
// Android Studio 4.0
android {
    buildFeatures {
        viewBinding = true
    }
}

UI画面でベクター画像を扱うときは、旧バージョンのAndroidでも動作するようにベクター画像用ライブラリを設定しておく

android {
  defaultConfig {
    ...
    vectorDrawables.useSupportLibrary = true
   }
   ...
}
  • Layout Editor でUI画面を作成します
    • レイアウトはデフォルトの ConstraintLayout を使います
    • UI部品には id 名をつけます
    • UI部品にはかならず constraint(制約)を設定します
  • ActivityMain.kt にKotlinでプログラミングします
  • 使用する画像などは Resource Manager を使って管理します

Unit1 のKotlinコード

チュートリアル Unit1 で作成したKotlinコード(MainActivity.kt)を View Binding を使って書き直しますね


Unit1 の Kotlinコード、findViewById() っていうのを使いましたよね

うん、そうなの。でもこれからは View Binding を使うといいかも

どんなふうに書くのかな?

うん、チュートリアルのKotlinコードを View Binding で書き直したので見比べてみてね

オリジナル(findViewByIdを使用)

これはチュートリアルのままですね

class MainActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       val rollButton: Button = findViewById(R.id.button)
       rollButton.setOnClickListener {
           rollDice()
       }

       rollDice()

   }

   private fun rollDice() {
       val dice = Dice(6)
       val diceRoll = dice.roll()
       val diceImage: ImageView = findViewById(R.id.imageView)

       val drawableResource = when (diceRoll) {
           1 -> R.drawable.dice_1
           2 -> R.drawable.dice_2
           3 -> R.drawable.dice_3
           4 -> R.drawable.dice_4
           5 -> R.drawable.dice_5
           else -> R.drawable.dice_6
       }

       diceImage.setImageResource(drawableResource)

       diceImage.contentDescription = diceRoll.toString()

   }

}

class Dice(val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }

}

View Binding 版(その1:オリジナルにあわせて修正)

findViewById のところを View Binding で置き換えた感じですね

class MainActivity : AppCompatActivity() {

   private lateinit var binding: ActivityMainBinding

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       binding = ActivityMainBinding.inflate(layoutInflater)
       val view = binding.root
       setContentView(view)

       val rollButton: Button = binding.button
       rollButton.setOnClickListener {
           rollDice()
       }

       rollDice()

   }

   private fun rollDice() {
       val dice = Dice(6)
       val diceRoll = dice.roll()
       val diceImage: ImageView = binding.imageView

       val drawableResource = when (diceRoll) {
           1 -> R.drawable.dice_1
           2 -> R.drawable.dice_2
           3 -> R.drawable.dice_3
           4 -> R.drawable.dice_4
           5 -> R.drawable.dice_5
           else -> R.drawable.dice_6
       }

       diceImage.setImageResource(drawableResource)

       diceImage.contentDescription = diceRoll.toString()

   }

}

class Dice(val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }

}

View Binding 版(その2:コンパクトに修正)

すっきりしてるかも!

class MainActivity : AppCompatActivity() {

   private lateinit var binding: ActivityMainBinding

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       binding = ActivityMainBinding.inflate(layoutInflater)
       setContentView(binding.root)

       binding.button.setOnClickListener {
           rollDice()
       }

       rollDice()

   }

   private fun rollDice() {
       val dice = Dice(6)
       val diceRoll = dice.roll()

       val drawableResource = when (diceRoll) {
           1 -> R.drawable.dice_1
           2 -> R.drawable.dice_2
           3 -> R.drawable.dice_3
           4 -> R.drawable.dice_4
           5 -> R.drawable.dice_5
           else -> R.drawable.dice_6
       }

       binding.imageView.setImageResource(drawableResource)

       binding.imageView.contentDescription = diceRoll.toString()

   }

}

class Dice(val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }

}

lateinit について

  • 上のコードの中にある変数 binding は、MainActivity クラスのプロパティとして宣言しています(MainActivityクラスの中で自由に使える)
  • binding lateinit を付けないで宣言するとエラーになります(プロパティは初期化しないといけません)
  • binding がインスタンスを受け取るのは onCreate() の中なので、そこまでエラーを出さずに待ってもらう必要があります
  • lateinit は「インスタンスを生成したらちゃんとプロパティに入れるからそれまでちょっと待って」というお金が返せないときの言い訳みたいな宣言(遅延評価)です。ちょっと待ってもらうことでエラーを回避します
  • lateinit val には使えません。また private が推奨されるので「private lateinit var 変数名: 型」みたいになっています。変数の宣言と初期化のタイミングがずれる View を入れるのに便利なんですね
  • lateinit はnullが扱える型には使えません。Javaのプリミティブ型に対応するような型(Int型など)の変数にも使えないので注意してくださいね