複数回に渡り、Android向けのTODOアプリ作成についてまとめます。
使用環境
- AndroidStudio:Android Studio Bumblebee | 2021.1.1 Patch 2
- Koltin:1.6.21
- 確認端末:Android12(Emulator)
つくるもの
Android向けのTODOアプリです。
画面は以下の2つを用意し、カテゴリを選択することでタスク一覧画面に遷移するようにします。
- カテゴリ一覧画面
- タスク一覧画面
アプリ機能として以下を実装予定です。
- カテゴリの登録、削除ができる
- カテゴリごとにタスクを登録、削除できる
- タスク完了時にチェックマークがつけられる
イメージ
このイメージでは追加時のタスク名入力や削除機能、データの永続性はありませんが、最終的にはそれらの機能を実装する予定です。
その1で扱う内容
その1では土台となるレイアウト周りを扱います。
- レイアウトの取得:ViewBinding
以下はその1では扱わず、続編で扱う予定です。
- 画面遷移:Fragment(その2)
- リストの表示方法: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に以下を記述します。
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.
で書いてしまうと別物として再度レイアウトを取得してしまい、うまく描画されないため注意が必要です。
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を許容せずに取得することができます。
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
}
}
続編へ続きます...