みんなの「作ってみた」

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

2019/08/30

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

前回のあらすじ

前回までの記事

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

前回のあらすじ

  • リストに区切り線ができた
  • タイトルを設定した

今回の目標

  • RecyclerView でデータの追加・削除

RecyclerView でデータの追加・削除

#2 を見返して、RecyclerView を調べた際の参考記事をまねします。
まねする記事はこちらにしました。

RecyclerViewと仲良くなる

理由は次の通りです。

  • 表示されているリストに追加、削除ができること
  • 一時的なデータ保存なので、model などの要素をいったん除外して、目的に絞って作成できそう

それでは早速進めていきます。

Android Support Design Library

design ライブラリをインポートします。
最新のバージョンをインポートするため、#4 の手順に倣って行います。
#4 Kotlin で TODO アプリを開発・リリースするまでの記録

design でライブラリ検索します。
選択肢に AndroidX がないようですが、こういうものなのでしょうか?
「OK」をクリックして反映させます。
build.gradle(app)
dependencies {
    implementation 'com.android.support:design:28.0.0'
}
Version 28 (intended for Android Pie and below) is the last version of the legacy support library, so we recommend that you migrate to AndroidX libraries when using Android Q and moving forward. The IDE can help with this: Refactor > Migrate to AndroidX... less... (Ctrl+F1) 
Inspection info:There are some combinations of libraries, or tools and libraries, that are incompatible, or can lead to bugs. One such incompatibility is compiling with a version of the Android support libraries that is not the latest version (or in particular, a version lower than your targetSdkVersion).
"Issue id: GradleCompatible

言われた通りにやっただけなのにエラーが発生。

Refactor > Migrate to AndroidX
AndroidX に変換してください、みたいなことを指示されている気がします。
いや、AndroidX が選択肢にあれば選択してましたけど・・・

それに Migrate to AndroidX は、個別に変換するのではなく全体変換するはずです。
変にほかのソースで手直しが発生したら嫌なので試したくないなぁ、と思って、他の方法を調べてみます。

小さな Android アプリを AndroidX に置き換えてみる
大体は com.android.support -> androidx でいけそうです。

ほんとだ・・・

build.gradle(app)
dependencies {
    implementation 'androidx.design:28.0.0'
}

書き直したらエラーがなくなりました!
それなら最初から AndroidX を選択肢に入れておいてくれ。

ビルド実行

本当はもう少し後に気づきましたが、紛らわしいのでここに書いておきます。

ビルドを実行しようとしたら、エラーになりました。

ERROR: Failed to resolve: androidx.design:28.0.0:
Affected Modules: app

androidx.design:28.0.0 が解決できないと言われてる・・・

build.gradle(app)
dependencies {
    implementation 'com.android.support:design:28.0.0'
}

当初設定したライブラリに切り戻してビルドしたところ、無事実行できました。
エラーは相変わらず出ていますが、ビルドが実行できるので問題なしとします。

なんでなんや

Material Icons からボタン素材を追加する

Material Icons とは

【Google提供】マテリアルアイコンの使い方とカスタマイズ方法まとめ
Material Icons(マテリアルアイコン)とは、Googleが2014年に提唱したMaterial Design(マテリアルデザイン)です。
ざっくり言うと、どのデバイスでもデザインを統一させて、使いやすくしていきましょうという動きですね。

数多あるフリーアイコンの中で、Material Icons を選ぶのはほかでもなく、Android Studio から直接挿入できる利便性です。
後は Google 先生の出したデザインなので間違いないです。すりすり。

+ - ボタン素材を追加する

アプリ内ボタンにマテリアルデザインアイコンを使う [Android]

さくっとボタン素材となるアイコンイメージを追加しました。

  • ic_add_black_24dp.xml
  • ic_remove_black_24dp.xml

Item のレイアウトを作成

1 行に対するレイアウトは、これまでの作業の中で list_item.xml として作成しています。
基本仕様ではテキスト欄が 1 つだけなので、TextView は 1 つで良さそうです。

ビルド実行

いいかんじ!

list_item.xml

変更したソースコードはこちらです。

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"
        card_view:cardElevation="2dp"
        android:foreground="?android:attr/selectableItemBackground">

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal"
            android:paddingStart="25dp"
            android:paddingEnd="0dp"
            android:gravity="center_vertical">

        <TextView
                android:id="@+id/itemTextView"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"/>

        <ImageButton
                android:id="@+id/itemImageButton"
                android:layout_width="70dp"
                android:layout_height="match_parent"
                android:background="#000f"
                card_view:srcCompat="@drawable/ic_remove_black_24dp"
                android:contentDescription="@string/app_name"/>

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

ImageView がなくなり、ImageButton が入った以外にも、レイアウトを整えるためいろいろ変更しました。
ワーニングが発生したため、追加したプロパティもあります。

LinearLayout

  • android:paddingStart="25dp"

左端からテキスト開始位置をずらすために追加しました。
CSS では padding-left です。

  • android:paddingEnd="0dp"

レイアウト的には paddingStart のみで問題なかったのですが、paddingStart を入れるなら paddingEnd も入れるようにとのワーニングが発生したため追加しました。
値を正数にするとボタン位置が右端からずれてしまうので、0 指定です。
CSS では padding-right です。

TextView

  • android:layout_width="0dp"

当初は 0 dp ではなく wrap_content を指定していましたが、次のワーニングが出たので変更しました。

Use a layout_width of 0dp instead of wrap_content for better performance

横幅を指定しないことになりますが、次項の layout_width が横幅を担います。

  • android:layout_weight="1"

layout_weightの使い方(比率で画面を構成する)

View に対してアイテムの表示範囲を比率で指定できます。
CSS では flex-grow のようなものだと思っています。

View に対してアイテムが横並びになっているとします。
以下のように割り当てられるイメージです。

100 dp の View の場合
[layout_weight="2"] → 20dp
[layout_weight="8"] → 80dp

200 dp の View の場合
[layout_weight="2"] → 40dp
[layout_weight="8"] → 160dp

layout_weight="1" を指定し、もうひとつの値を固定値にすれば、一方のみレスポンシブ対応が可能です。

100 dp の View の場合
[layout_width="20dp"] → 20dp
[layout_weight="1"] → 80dp

200 dp の View の場合
[layout_width="40dp"] → 40dp
[layout_weight="1"] → 160dp

今回はボタンのサイズを変えずに、テキストの長さがレスポンシブになるデザインが望ましいと考えたので、layout_weight="1" にしました。

レスポンシブデザインの考え方としては以下の通りです。

【図解】レスポンシブデザインとは?定義、特徴、メリットとデメリットを解説

マルチスクリーンではないので必要なのかわかりませんが、意識して適用したのでここに記載しておきます(アプリより WEB 的な考え方だと思います)。
この考え方でなくとも、長さを可変にしたい場合に最適です。

ImageButton

  • android:layout_width="70dp"

前述の通りです。
現在の View サイズから鑑みて、固定値を割り当てました。

  • android:layout_height="match_parent"

リストの縦幅に対して最大値にしています。
理由は誤タップを防ぐためです。

  • android:contentDescription="@string/app_name"

Image には Description を指定するようにとワーニングが出たので追加しました。
最初は文字列にしようとしましたが、@string を挿入するようにとのワーニングが出て、@string と入力すると補完候補が表示されたため、流されるまま @string/app_name を指定しました。

Android Studio が全部導いてくれます。
そして次第にワーニングに踊らされている気分になっていく。

余談:paddingStart と paddingLeft

  • paddingStart と paddingEnd
  • paddingLeft と paddingRight

これらは基本的に同じ動作のようです。
前者は左からの余白、後者は右からの余白を指定します。
違いとしては次の記事をお読みください。

多言語化を見据えた開発 StartとLeftの違いとは | Development aiming at multilingualization, Difference between Start and Left | Android

文字が左から開始する言語、右から開始する言語に対応できるように、左右ではなく文字の開始位置という概念で使用とするようです。
そもそもソース上で Left を書いても、Start を使用するようにとワーニングが発生するので、大人しく従いましょう。

Consider adding android:paddingStart=“25dp” to better support right-to-left layouts Error

RecyclerViewHolder.kt

ImageView を ImageButton に差し替えます。

RecyclerViewHolder.kt
import android.widget.ImageButton

val itemImageView: ImageView = view.findViewById(R.id.itemImageView)

val itemImageButton: ImageButton = view.findViewById(R.id.itemImageButton)

import 構文は自動挿入です。
自動挿入設定は #4 の記事で行っています。

RecyclerAdapter.kt

RecyclerAdapter.kt
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
    holder?.let {
        it.itemTextView.text = itemList.get(position)
    }
}

ImageButton の Image は list_item.xml で直接指定しています。
ここで記載する必要がなかったので消しました。

まとめ

今回はここまで。
ボタンは動きませんが、レイアウト追加ができました。
目標到達までまだまだのようです。
冒頭部分で、こんなに記事が長くなるとは思いませんでしたが・・・。

成果

  • リストに削除ボタンを設置した(イメージ)

参考記事に感謝

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

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

RecyclerViewと仲良くなる

次の記事

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