〈kotlin〉 BottomSheet

Android studioでkotlinを使ってAndroidアプリ作成の勉強中。

Material DesignのBottomSheetをサンプルコードをもとに作成してみた。
Material Design
BottomSheetはこういう下から出てくるビューのこと。

Material Design Bottom Sheetsのページより引用

標準的なBottomSheet

<androidx.coordinatorlayout.widget.CoordinatorLayout  
     ...>
    <Button
        android:id="@+id/buttonBottomSheetPersistent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Open Persistent Bottom Sheet" />

    <LinearLayout
        android:id="@+id/persistent_bottom_sheet"
        ...
        //レイアウトをBottomSheetとして定義する
        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
        app:behavior_hideable="true" //完全に隠れるようにするかどうか
        app:behavior_peekHeight="30dp" // 表示される高さ>

        //以下にBottomSheetに表示するビューを配置
        <TextView
            android:id="@+id/content1"
            ... />

        <TextView
            android:id="@+id/content2"
            ... />

        <TextView
            android:id="@+id/content3"
            .../>
    </LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

設定にapp:layout_behaviorを追加してBottomSheetとして定義する。
その他追加したいBottomSheetの設定があれば、
app:layout_behaviorを追加したビューに設定するか、
Activityで呼び出して(以下のコードの*部分)設定する。

class MainActivity : AppCompatActivity() {

    private lateinit var binding : ActivityPersistentBottomSheetBinding

    private lateinit var bottomSheetBehavior:BottomSheetBehavior<LinearLayout>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityPersistentBottomSheetBinding.inflate(layoutInflater)
        setContentView(binding.root)

        //*
        //BottomSheetを取得して設定を追加できるようにする
        bottomSheetBehavior = BottomSheetBehavior.from(binding.persistentBottomSheet)

        //状態の変化やスライド操作によるコールバックを登録できる
        bottomSheetBehavior.addBottomSheetCallback(object :BottomSheetBehavior.BottomSheetCallback() {
            override fun onSlide(bottomSheet: View, slideOffset: Float) {
            }

            override fun onStateChanged(bottomSheet: View, newState: Int) {
                binding.buttonBottomSheetPersistent.text = when (newState) {
                    //BottomSheetが閉じているか開いているかを表すStateをもとにボタンの文字を変える
                    BottomSheetBehavior.STATE_EXPANDED -> "Close Persistent Bottom Sheet"
                    BottomSheetBehavior.STATE_COLLAPSED -> "Open Persistent Bottom Sheet"
                    else -> "Persistent Bottom Sheet"
                }
            }
        })


        //BottomSheetを閉じたり開いたりするボタン
        binding.buttonBottomSheetPersistent.setOnClickListener {
            val state =
                if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED)
                    BottomSheetBehavior.STATE_COLLAPSED
                else
                    BottomSheetBehavior.STATE_EXPANDED
            bottomSheetBehavior.state = state
        }
    }
}

BottomSheetとして表示するFragmentを別で用意する。
設定はstyle.xml内に記述する。

<style name="ModalBottomSheet" parent="Widget.MaterialComponents.BottomSheet.Modal">
  <!-- Apply attributes here -->
</style>

<style name="ModalBottomSheetDialog" parent="ThemeOverlay.MaterialComponents.BottomSheetDialog">
  <item name="bottomSheetStyle">@style/ModalBottomSheet</item>
</style>

<style name="AppTheme" parent="Theme.MaterialComponents.*">
  <item name="bottomSheetDialogTheme">@style/ModalBottomSheetDialog</item>
</style>
<androidx.constraintlayout.widget.ConstraintLayout
    ...
    android:id="@+id/modal_bottom_sheet"/>

    <TextView
        android:id="@+id/layout_edit"
       .../>

    <TextView
        android:id="@+id/layout_delete"
        .../>

    <TextView
        android:id="@+id/layout_add"
        .../>
</androidx.constraintlayout.widget.ConstraintLayout>
class BottomSheetFragment : BottomSheetDialogFragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_bottom_sheet, container, false)
    }

    companion object{
        const val TAG = "BottomSheet"
    }
}

ActivityでBottomSheetを追加する。

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        binding.bottomSheetButton.setOnClickListener {
            val bottomSheetFragment = BottomSheetFragment()
            bottomSheetFragment.show(supportFragmentManager, BottomSheetFragment.TAG)

            /*
            コード上でbehaviorの属性を追加する場合は以下のように取得する
            val modalBottomSheetBehavior = (bottomSheetFragment.dialog as BottomSheetDialog).behavior
            */
        }
    }
}

参考
Material Design
Bottom Sheet Android Tutorial - Working with BottomSheet