DataBindingを使っているChipをChipGroupに動的に追加/削除する


要件

  • アプリ全体ではDataBindingを使っているものとする
  • Chipの個数は可変であり、動的に増減させることが出来る
  • Chipには✕ボタンを表示し、押したら消えるようにする

実装

ライブラリの追加

Chipを使うためのライブラリを追加する。

app/build.gradle

dependencies {
    (中略)
    implementation 'com.google.android.material:material:1.1.0'
    (中略)
}

AppThemeの変更

Chipを使う際にAppComponentだとクラッシュするので暫定対応。
本記事ではTheme.MaterialComponents.Light.DarkActionBarを利用している。

styles.xml
<resources>

    <!-- Base application theme. -->
<!--    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">--> 
    <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

タグの定義

Tag.kt
data class Tag(
    val id: String,
    val name: String,
)

Chipのレイアウトを用意

view_tag_chip.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="tag"
            type="com.masaibar.chipsample.Tag" />
    </data>

    <com.google.android.material.chip.Chip
        style="@style/Widget.MaterialComponents.Chip.Entry"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checkable="false"
        android:text="@{tag.name}"
        android:textAppearance="@style/TextAppearance.AppCompat.Small"
        tools:text="Tag" />

</layout>

Chipを追加する画面レイアウトを用意

activity_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="16dp"
        tools:context=".view.main.MainActivity">

        <com.google.android.material.chip.ChipGroup
            android:id="@+id/chip_group_tags"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:singleSelection="false" />
    </FrameLayout>
</layout>

動的に追加

ChipにもDataBindingを使いたいのでinflateしてタグ情報をバインドしている。
✕ボタンが押された際のイベントはsetOnCloseIconClickListenerで設定できる。
Chipが生成できたらChipGroupTagsのaddView()に渡してやると、ChipGroupの子要素に追加される。

MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(
                this,
                R.layout.activity_main
        )

        for (i in 0 until 20) {
            DataBindingUtil.inflate<ViewTagChipBinding>(
                    LayoutInflater.from(this),
                    R.layout.view_tag_chip,
                    null,
                    false
            ).apply {
                tag = Tag(
                        id = i,
                        name = UUID.randomUUID().toString().substring(0, Random.nextInt(10))
                )
                (root as? Chip)?.setOnCloseIconClickListener {
                    binding.chipGroupTags.removeView(root)
                }
            }.let {
                binding.chipGroupTags.addView(it.root)
            }
        }
    }
}

結果

ChipGroupのおかげでChipの大きさに応じて良しなにレイアウト、収まらない場合は改行してくれているのがわかる。

✕ボタンを押すとChipが消え、消えたところは良しなにレイアウトを詰めてくれる。

参考

https://stackoverflow.com/questions/40917521/android-data-binding-programmatically-instantiated-view
https://stackoverflow.com/questions/50494502/how-can-i-add-the-new-android-chips-dynamically-in-android/50823177