Android Kotlin基礎講座 07.1:RecyclerViewの基礎

タスク:コードを改善する

RecyclerViewは既に完璧な状態です。AdapterとViewHolderの実装方法を学習し、RecyclerView Adapterを用いてリストを表示するためにそれらを組み合わせました。

ここまでのコードはアダプターとビューホルダーの制作過程を示しています。しかしながら、このコードには改善の余地があります。表示するためのコードとビューホルダーを管理するためのコードが一緒になっており、onBindViewHolder()はどのようにViewHolderを更新するかを知っています。

製品版アプリでは、複数のビューホルダーや、より複雑なアダプターを持つことになりますし、複数人の開発者が変更を加えることになります。ビューホルダーに関連するものは全てビューホルダー内にのみ含まれているようにコードを構成するべきでしょう。

ステップ1:onBindViewHolder()をリファクタリングする

このステップでは、コードをリファクタリングし、全てのビューホルダーの機能をViewHolderに移していきます。このリファクタリングの目的はアプリがどのようにユーザーに見えるかを変更するためのものではなく、開発者がコードを簡単に、安全に編集することができるようにするためのものです。幸いにもAndroid Studioにはそのためのツールがあります。

  1. SleepNightAdapterのonBindViewHolder()内のitem変数の宣言文以外の全てのコードを選択してください。
  2. 右クリックし、Refactor > Functionを選択してください。
  3. 関数をbindと命名し、表示されたパラメーターに全てチェックを入れて、OKをクリックしてください。

bind()関数はonBindViewHolder()の下に配置されます。

    private fun bind(holder: ViewHolder, item: SleepNight) {
        val res = holder.itemView.context.resources
        holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
        holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
        holder.qualityImage.setImageResource(when (item.sleepQuality) {
            0 -> R.drawable.ic_sleep_0
            1 -> R.drawable.ic_sleep_1
            2 -> R.drawable.ic_sleep_2
            3 -> R.drawable.ic_sleep_3
            4 -> R.drawable.ic_sleep_4
            5 -> R.drawable.ic_sleep_5
            else -> R.drawable.ic_sleep_active
        })
    }
  1. カーソルをbind()のholderパラメーターのholderというワードの上に置き、Alt+Enter(MacはOption+Enter)を押し、インテンションメニューを開いてください。
    Convert parameter to receiverを選択し、これを以下のシグネチャを持つ拡張関数に変換してください。
private fun ViewHolder.bind(item: SleepNight) {...}
  1. bind()関数を切り取りし、ViewHolder内にペーストしてください。
  2. bind()をpublicに変更してください。
  3. 必要な場合はbind()をアダプターにインポートしてください。
  4. 現在bindはViewHolder内にあるので、シグネチャのViewHolder部分は削除できます。以下がViewHolderクラス内のbind()関数の完成形になります。
fun bind(item: SleepNight) {
   val res = itemView.context.resources
   sleepLength.text = convertDurationToFormatted(
           item.startTimeMilli, item.endTimeMilli, res)
   quality.text = convertNumericQualityToString(
           item.sleepQuality, res)
   qualityImage.setImageResource(when (item.sleepQuality) {
       0 -> R.drawable.ic_sleep_0
       1 -> R.drawable.ic_sleep_1
       2 -> R.drawable.ic_sleep_2
       3 -> R.drawable.ic_sleep_3
       4 -> R.drawable.ic_sleep_4
       5 -> R.drawable.ic_sleep_5
       else -> R.drawable.ic_sleep_active
   })
}

ステップ2:onCreateViewHolderをリファクタリングする

アダプター内のonCreateViewHolder()メソッドは現在レイアウトリソースのビューをViewHolder用にインフレートしています。しかしながら、インフレーションはアダプターとは何の関係がなく、ViewHolderに関係しています。ですのでインフレーションはViewHolder内で行われるべきです。

  1. onCreateViewHolder()関数内に含まれる全てのコードを選択してください。
  2. 右クリックし、Refactor > Functionを選択してください。
  3. 関数名をfromとし、表示されたパラメーターに全てチェックを入れて、OKをクリックしてください。
  4. カーソルをfromの上にあてて、Alt+Enter(Macの場合はOption+Enter)を押し、インテンションメニューを開いてください。
  5. Move to companion objectを選択してください。from()関数はViewHolderインスタンス上からではなく、ViewHolderクラス上で呼ばれるため、companionオブジェクト内にある必要があります。
  6. companionオブジェクトをViewHolderクラスに移動してください。
  7. from()をpublicにしてください。
  8. onCreateViewHolder()内、return文を変更してViewHolderクラスのfrom()の呼び出しの結果を返すようにしてください。

変更後のonCreateViewHolder()とfrom()メソッドは以下のコードのようになります。これでエラーなしで実行できるはずです。

    override fun onCreateViewHolder(parent: ViewGroup, viewType: 
Int): ViewHolder {
        return ViewHolder.from(parent)
    }
companion object {
   fun from(parent: ViewGroup): ViewHolder {
       val layoutInflater = LayoutInflater.from(parent.context)
       val view = layoutInflater
               .inflate(R.layout.list_item_sleep_night, parent, false)
       return ViewHolder(view)
   }
}
  1. ViewHolderクラスのシグネチャを変更して、コンストラクタをprivateにしてください。from()は現在ViewHolderインスタンスを返すメソッドですので、これ以上ViewHolderのコンストラクタを呼び出す理由はありません。
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
  1. アプリを起動してください。以前と同じ様にビルド、起動できるはずです。

完成済みプロジェクト

お疲れさまでした。完成済みプロジェクトは以下からダウンロードできます。

RecyclerViewFundamentals