KotlinでTODOアプリをつくる～その１～

複数回に渡り、Android向けのTODOアプリ作成についてまとめます。

#### 使用環境
- AndroidStudio：Android Studio Bumblebee | 2021.1.1 Patch 2
- Koltin：1.6.21
- 確認端末：Android12（Emulator）

## つくるもの
Android向けのTODOアプリです。

画面は以下の2つを用意し、カテゴリを選択することでタスク一覧画面に遷移するようにします。
- カテゴリ一覧画面
- タスク一覧画面

アプリ機能として以下を実装予定です。
- カテゴリの登録、削除ができる
- カテゴリごとにタスクを登録、削除できる
- タスク完了時にチェックマークがつけられる

#### イメージ
![Android_Emulator_-_to_do_2022-05-21_14-30-15_AdobeCreativeCloudExpress_AdobeCreativeCloudExpress.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396691/8f628011-4c30-5d8c-0af0-13796db07fee.gif)

このイメージでは追加時のタスク名入力や削除機能、データの永続性はありませんが、最終的にはそれらの機能を実装する予定です。


## その１で扱う内容
その１では土台となるレイアウト周りを扱います。
- レイアウトの取得：ViewBinding

以下はその１では扱わず、続編で扱う予定です。
- 画面遷移：Fragment（[その２](https://qiita.com/satona-oinuma/items/f31a4a948eb05a918ee1)）
- リストの表示方法：RecyclerView
- データやデータの保存に関すること：内部ファイルへの保存を予定


### 用意するクラス
以下の2つのクラス、2つのレイアウトファイルを用意します。
- MainActivity：Activityクラスを継承したクラス
Activity全体を管理する基本のクラスです。
- MainFragment：Fragmentクラスを継承したクラス
Fragment周りを管理するクラスです。
- activity_main.xml：MainActivityクラスで呼び出されるレイアウトファイル
- fragment_item_list.xml：MainFragmentクラスで呼び出されるレイアウトファイル


## レイアウトの取得：ViewBinding
ViewBindingは、xmlに指定したレイアウトをクラス内で簡単に呼び出せる機能です。
以前はレイアウトやレイアウトに指定された要素を呼び出す際には、毎回`findViewById`でIDから取得する必要がありました。しかし、AndroidStudio 3.6 Canary11以降、ViewBindingで一度レイアウトの素となるBindingクラスのインスタンスを取得すれば、そのインスタンスを使用して同じxmlの要素を簡単に取得することができます。
今回は公式で推奨しているViewBindingを使用してレイアウトを取得していきます。

ちなみに、ViewBindingと似たものにDataBindingがありますが、こちらはViewBindingよりも幅広いデータを呼び出すことができる機能です。
ViewBindingはレイアウト周りに特化しているため、今回のように、レイアウト取得に必要な機能だけを手軽に実装する際に便利です。

### ViewBindingの使い方
ViewBindingを使用するための準備として、アプリのbuild.gradleに以下を記述します。

```Groovy:build.gradle
android {
   ...
   buildFeatures {
       viewBinding true
   }
}
```
上記を記述することで、同一モジュール内のxmlレイアウトファイルのBindingクラスが作成されます。
レイアウトの取得には、このBindingクラスを使用します。
ViewBindingではActivityとFragmentでは使用方法が若干異なるため、MainActivityとMainFragmentではそれぞれ異なる方法でレイアウトを取得します。

#### Activityでの使用
まずはMainActvityでの取得方法です。
Activityでは、`Bindingクラス名.inflate(layoutInflater)`の形でBindingクラスのインスタンスを取得します。
Bindingクラス名はパスカルケース表記のレイアウトファイル名+Bindingで作成されるため、ここでは`activity_main.xml`が`ActivityMainBinding`になります。
インスタンスが取得できたら、`Bindingクラス名.ID名`で必要な要素を取得します。
今回は特に必要な要素はないため、`binding.root`でルートビューのみを呼び出しています。

ちなみに、setContentViewに設定するレイアウトを`R.layout.`で書いてしまうと別物として再度レイアウトを取得してしまい、うまく描画されないため注意が必要です。

```Kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
// 1. activity_main.xmlのBindingクラスのインスタンスを取得
       val binding = ActivityMainBinding.inflate(layoutInflater)
// 2. setContentViewにbinding.rootを設定
       setContentView(binding.root)
   }
}
```

#### Fragmentでの使用
続いて、MainFragmentでの取得方法です。
FragmentではonCreateViews内にて、以下の方法でBindingクラスのインスタンスを取得します。
`Bindingクラス名.inflate(inflater, Fragmentを設置したいレイアウトID、false)`

インスタンスが取得できたら、`Bindingクラス名.root`でルートビューを呼び出します。

そして、onDestroyView内で_binding=nullを設定しています。
これはメモリリークを防ぐため、つまり「Viewは消えたにも関わらずFragmentではViewの情報を保持している状況」を防ぐために行います。
onDestroyViewはFragmentに関連付けられたView階層が削除されたときに動くため、そこで_binding=nullを設定することでBindingクラスのインスタンス情報を削除しています。

上記のnull代入があるため、プロパティは`_binding`と`binding`の2つを用意しました。
`_` がついているものは一時的に使用するプロパティです。
bindingは`binding get() =`でカスタムゲッターを設定しており、プロパティにアクセスするたびに`get()`が動きます。
都度getterを動かすことで、そのときのBindingクラスのインスタンスをnullを許容せずに取得することができます。

```Kotlin:MainFragment.kt
class MainFragment : Fragment() {

   private var _binding: FragmentItemListBinding? = null
   private val binding get() = _binding!!

   override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?,
       savedInstanceState: Bundle?
   ): View? {
// 1. Bindingクラスのインスタンスを生成
       _binding = FragmentItemListBinding.inflate(inflater, container, false)
// 2. binding.rootでルートビューを取得
       return binding.root
   }

   override fun onDestroyView() {
       super.onDestroyView()
// 3. onDestroyView内でBindingクラスのインスタンスを削除
       _binding = null
   }
}
```


[続編](https://qiita.com/satona-oinuma/items/f31a4a948eb05a918ee1)へ続きます...


## 参考
- [Use view binding to replace findViewById | by Sean McQuillan | Android Developers | Medium](https://medium.com/androiddevelopers/use-view-binding-to-replace-findviewbyid-c83942471fc)
- [View Binding | Android Developers](https://developer.android.com/topic/libraries/view-binding)
- [プロパティとフィールド - Kotlin Programming Language](https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/properties.html)