みんなの「作ってみた」

#5 Kotlin で TODO アプリを開発・リリースするまでの記録

2019/08/14

m_n_r
m_n_r
WEBエンジニア(駆け出し)。 アトランティックサーモンがお好き。

前回のあらすじ

前回までの記事

#1 Kotlin で TODO アプリを開発・リリースするまでの記録
#2 Kotlin で TODO アプリを開発・リリースするまでの記録
#3 Kotlin で TODO アプリを開発・リリースするまでの記録
#4 Kotlin で TODO アプリを開発・リリースするまでの記録

前回のあらすじ

  • AndroidX のライブラリをインポートできた
  • Android Studio のファイル追加方法がわかった
  • リストアイテムのレイアウトを作成した
  • ViewHolder を作成した

今回の目標

  • 前回に引き続き RecyclerView でリスト表示

RecyclerView でリスト表示

前回に引き続き、こちらの記事をまねしてリスト表示してみます。

KotlinでRecyclerViewを使ったリスト表示を行う

Adapter を作成する

kt ファイルを新規追加して、参考記事の RecyclerAdapter.kt で上書きするようにそのまま貼り付けます。
Class 名にエラーが出ました。

Class 'RecyclerAdapter' is not abstract and does not implement abstract base class member public abstract fun onBindViewHolder(@NonNull holder: RecyclerViewHolder, position: Int): Unit defined in androidx.recyclerview.widget.RecyclerView.Adapter

エラーの内容を調べたところ、こちらの記事を見つけました。
Support Libraryを27.1.0に更新メモ
要は null を許容できなくなったため、書き方を改めろということです。
Null安全とは?
型の後ろに「?」を入れると null を許容(Nullable)することになるので、コード内で「?」を消します。

Class 名のエラーはなくなりましたが、今度は mRecyclerView 代入している null 自体にエラーが出てしまいました。

RecyclerViewHolder.kt
private var mRecyclerView : RecyclerView = null
"Null can not be a value of a non-null type RecyclerView"

いろいろ試行錯誤してみましたが、null 許容前提で記載されているようなので、どうしようもできません・・・。
と思って絶望したところ、そもそもこれ全部 null が使えないのか?ということに気づきます。遅い。

そういうわけでエラーが出ていた override のみ「?」を削除。

RecyclerViewHolder.kt
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
RecyclerViewHolder.kt
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
RecyclerViewHolder.kt
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
RecyclerViewHolder.kt
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {

無事エラーがなくなりました!
上記の行以外で型名の後ろに「?」がついていても問題ないため、そもそも override に対してのみのエラーだったようです。
結構焦りました・・・。

activity_main を変更する

参考記事の activity_main.xml で上書きするようにそのまま貼り付けます。
エラーが出てしまいました。

activity_main.xml
tools:context="jp.todate.kotlinrecyclerviewsample.MainActivity"

以下の記事を参考に変更し、エラーがなくなりました。
Android レイアウトで使う tools:の便利まとめ

activity_main.xml
tools:context=".MainActivity"

今回参考にしている記事の github を見ると、MainActivity が kotlinrecyclerviewsample フォルダに格納されていたため、MainActivity の位置を固定値で指定していたようです。

MainActivity を変更する

参考記事の MainActivity.kt で上書きするようにそのまま貼り付けます。

エラーが複数出ました。
赤の波線 LinearLayoutManager.VERTICAL から対応していきます。

MainActivity.kt
mainRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
"Must be one of: RecyclerView.HORIZONTAL, RecyclerView.VERTICAL less... (Ctrl+F1) "
"Inspection info:Ensures that when parameter in a method only allows a specific set of constants, calls obey those rules.  Issue id: WrongConstant"

以下を参考に変更し、エラーを消すことができました。
理由はよくわかりません。

Must be one of: RecyclerView.HORIZONTAL, RecyclerView.VERTICAL

MainActivity.kt
mainRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)

次はこのエラーです。

MainActivity.kt
resources.getStringArray(R.array.hoges) // ← array でエラー
mainRecyclerView.adapter // ← adapter でエラー
mainRecyclerView.layoutManager // ← layoutManager でエラー
"Unresolved reference:XXX"

いずれもプロパティで参照ができないエラーが出ています。
Kotlin Android Extensions は標準で設定されているので使えるはずなのに・・・。

adapter と layoutManager の前の mainRecyclerView にもワーニングが出ていますが、これが関係あるのでしょうか?

Widget has an unresolved type 'android.support.v7.widget.RecyclerView', and thus it was upcasted to 'android.view.View'

前述で参照した Kotlin Android Extensions をもう一度読み返していると、以下の項目が目に留まりました。

2015/10/19追記

xml の方が関係がある?半信半疑で activity_main.xml を確認して気づきます。
AndroidX ではなく Support Library 前提で書かれてる。

activity_main.xml を修正します。

activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                             xmlns:tools="http://schemas.android.com/tools"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent"
                                             tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/mainRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Support Library から AndroidX への書き換えは、activity_main.xml の import と次の記事を参考にしました。
[Android] ConstraintLayoutでwrap_contentしつつ領域からはみ出さないようにする

無事 adapter と layoutManager のエラー、mainRecyclerView のワーニングが消えました!

残りの array も xml と関係ありそうな匂いがしてきます。
list_item.xml も修正します。

list_item.xml
<androidx.recyclerview.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        card_view:cardElevation="2dp"
        android:foreground="?android:attr/selectableItemBackground">

    <androidx.recyclerview.widget.LinearLayoutManager
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:gravity="center_vertical">

        <ImageView
                android:id="@+id/itemImageView"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_margin="8dp" />

        <TextView
                android:id="@+id/itemTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

    </androidx.recyclerview.widget.LinearLayoutManager>
</androidx.recyclerview.widget.CardView>

そう都合よく array のエラーは消えませんでした・・・。
それから色々調べましたが、原因がわからず。
苦肉の策として固定値を入れて、エラーを消しました。

Kotlin のコレクション使い方メモ

MainActivity.kt
val hoges: List<String> = mutableListOf("a", "b", "c")

型を List<String> にした理由は RecyclerAdapter の引数にこの型が指定されているためです。

ひとまず全てのエラーがなくなったので、ビルド実行です!

ビルド実行

つら

エラーを分析する

4: Run タブに大量のエラーが出ています。

このログから原因を探っていきます。
最初の 3 行ほどを切り出しました。
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.first, PID: 7260
    android.view.InflateException: Binary XML file line #2: Binary XML file line #2: Error inflating class androidx.recyclerview.widget.CardView

1 行目、2 行目のエラーは具体的ではないのでその次のエラーから確認します。
3 行目の最後の部分が気になります。

Error inflating class androidx.recyclerview.widget.CardView

CardView が認識されていないようです。
ライブラリはインポートしていて、CardView を使っているのは list_item.xml のみです。

list_item.xml
<androidx.recyclerview.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        card_view:cardElevation="2dp"
        android:foreground="?android:attr/selectableItemBackground">

    <androidx.recyclerview.widget.LinearLayoutManager
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:gravity="center_vertical">

        <ImageView
                android:id="@+id/itemImageView"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_margin="8dp" />

        <TextView
                android:id="@+id/itemTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

    </androidx.recyclerview.widget.LinearLayoutManager>
</androidx.recyclerview.widget.CardView>

思い当たることとすれば、Support Library を AndroidX に書き換えたことぐらい。
コピペを無理やり使っているのがダメな気がするので、Palette からパーツを移動して作り直そうと試みます。
あれ?パーツが移動できないぞ?
パーツの認識すらしていないダメな状態だったようです・・・残念過ぎる・・・

ということで一から作り直した結果がこちら。

list_item.xml
<androidx.cardview.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        card_view:cardElevation="2dp"
        android:foreground="?android:attr/selectableItemBackground">

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:gravity="center_vertical">

        <ImageView
                android:id="@+id/itemImageView"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_margin="8dp" />

        <TextView
                android:id="@+id/itemTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

    </LinearLayout>
</androidx.cardview.widget.CardView>

ビルド実行


無事リストが表示できました!

おまけにタップ時に波紋が広がり、リスト位置まで取得できちゃいます。すごい。

まとめ

今回はここまで。
無事、RecyclerView でリスト表示ができました。
コピペして不具合が出るのは想定内でしたが、エラーを調べて修正するのも勉強になりますし、一から調べて作る工数より少ないかなとやってみました。
調べるのは大変でしたが、思ったよりエラーが少なくてよかったです。

次回はこのソースを読み解きながら、実装に沿っていろいろ試してみましょう。

成果

  • RecyclerView でリスト表示ができた

参考記事に感謝

たくさんの記事を引用させて頂きました。ありがとうございます。
著者が一部のみを引用したことにより、引用元の意図しない情報として受けられることがないよう、できれば引用元の記事を読んでいただけますようお願いいたします。
またいずれの記事の感想も、著者個人の意見に基づくものですのでご注意ください。

本記事で参考にさせて頂いたこちらの記事に、改めて特別な感謝を。

KotlinでRecyclerViewを使ったリスト表示を行う

次の記事

#6 Kotlin で TODO アプリを開発・リリースするまでの記録