Material Design

【Material Design】Android Bottom Sheets 底部面板 範例

【Material Design】Android Bottom Sheets 底部面板 範例

Bottom Sheets 是 Android 界面設計中一種常見元素,它是一個可滑動出現在畫面底部的面板。它可以用來顯示額外的內容或作為互動面板,例如在地圖應用中顯示點擊地點的詳細信息,或在音樂播放器中顯示歌單。

Bottom Sheets 可以有兩種不同的類型:模態和非模態。 模態 Bottom Sheets 需要用戶進行互動才能關閉,而非模態 Bottom Sheets 可以被用戶輕鬆關閉。


文章目錄

  1. 非模態 Bottom Sheets 樣式UI
  2. 非模態 Bottom Sheets 控制狀態
  3. 非模態 Bottom Sheets 動態設置
  4. 非模態 Bottom Sheets 滑動監聽
  5. 模態 Bottom Sheets Dialog 樣式UI
  6. 模態 Bottom Sheets Dialog
  7. Developer Documents Bottom Sheets

1.非模態 Bottom Sheets 樣式UI

drawable/rounded_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/black" />
    <corners
        android:topLeftRadius="15dp"
        android:topRightRadius="15dp" />
</shape>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?><!-- Use DrawerLayout as root container for activity -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/change"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Change"
            android:textAllCaps="false"
            android:layout_marginTop="20dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.coordinatorlayout.widget.CoordinatorLayout
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="20dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/change">

            <FrameLayout
                android:id="@+id/bottomSheetLayout"
                android:background="@drawable/rounded_dialog"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                app:behavior_peekHeight="100dp"
                app:layout_behavior="@string/bottom_sheet_behavior">

                <androidx.appcompat.widget.AppCompatButton
                    android:id="@+id/hello"
                    android:textAllCaps="false"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:text="Hello" />

            </FrameLayout>
        </androidx.coordinatorlayout.widget.CoordinatorLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

2.非模態 Bottom Sheets 控制狀態

狀態
STATE_COLLAPSED:坍塌(默認)
STATE_EXPANDED:展開
MainActivity.kt
@SuppressLint("RestrictedApi", "VisibleForTests")
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var bottomSheetBehavior: BottomSheetBehavior<View>

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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomSheetLayout)
        bottomSheetBehavior.disableShapeAnimations()

        init()
    }

    private fun init() {
        binding.change.setOnClickListener {
            if (bottomSheetBehavior.state != BottomSheetBehavior.STATE_EXPANDED) {
                bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
            } else {
                bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
            }
        }

        binding.hello.setOnClickListener {
            Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show()
        }
    }

}

3.非模態 Bottom Sheets 動態設置

Extensions.kt
val Float.dp get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics)
MainActivity.kt
@SuppressLint("RestrictedApi", "VisibleForTests")
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var bottomSheetBehavior: BottomSheetBehavior<View>

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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomSheetLayout)
        bottomSheetBehavior.disableShapeAnimations()

        val bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomSheetLayout)
        bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
        bottomSheetBehavior.peekHeight = 90f.dp.toInt()
    }

}

4.非模態 Bottom Sheets 滑動監聽

MainActivity.kt
@SuppressLint("RestrictedApi", "VisibleForTests")
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var bottomSheetBehavior: BottomSheetBehavior<View>
    private lateinit var bottomSheetCallback: BottomSheetBehavior.BottomSheetCallback

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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomSheetLayout)
        bottomSheetBehavior.disableShapeAnimations()

        init()
    }

    private fun init() {
        bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
            override fun onStateChanged(bottomSheet: View, newState: Int) {
                Log.e("bottomSheet", "onStateChanged")
            }

            override fun onSlide(bottomSheet: View, slideOffset: Float) {
                Log.e("bottomSheet", "onSlide")
            }
        }
    }

    override fun onStart() {
        super.onStart()
        bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback)
    }

    override fun onDestroy() {
        bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallback)
        super.onDestroy()
    }

}

5.模態 Bottom Sheets Dialog 樣式UI

drawable/rounded_dailog.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/black" />
    <corners
        android:topLeftRadius="15dp"
        android:topRightRadius="15dp" />
</shape>
values/themes.xml
<resources>

    <style name="Theme.JetpackDemo" parent="Theme.MaterialComponents.Light.NoActionBar">
        <item name="bottomSheetDialogTheme">@style/AppBottomSheetDialogTheme</item>
    </style>

    <style name="AppBottomSheetDialogTheme" parent="ThemeOverlay.Material3.BottomSheetDialog">
        <item name="bottomSheetStyle">@style/AppModalStyle</item>
    </style>

    <style name="AppModalStyle" parent="Widget.Design.BottomSheet.Modal">
        <item name="android:background">@drawable/rounded_dialog</item>
    </style>

</resources>
bottom_sheet_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

    </data>

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/hello"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:text="hello"
            android:textAllCaps="false" />

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:text="A" />

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:text="B" />

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:layout_marginBottom="20dp"
            android:text="C" />

    </androidx.appcompat.widget.LinearLayoutCompat>
</layout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?><!-- Use DrawerLayout as root container for activity -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/open"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="open"
            android:textAllCaps="false"
            android:layout_marginTop="20dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

6.模態 Bottom Sheets Dialog

BottomSheetDialog.kt
class BottomSheetDialog : BottomSheetDialogFragment() {

    private var mBinding: BottomSheetDialogBinding? = null
    private val binding get() = requireNotNull(mBinding)

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        if (mBinding == null) {
            mBinding = BottomSheetDialogBinding.inflate(inflater, container, false)
        }

        init()
        return binding.root
    }

    private fun init() {
        binding.hello.setOnClickListener {
            Toast.makeText(context, "Hello", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onDestroy() {
        mBinding = null
        super.onDestroy()
    }

}
MainActivity.kt
@SuppressLint("RestrictedApi", "VisibleForTests")
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        init()
    }

    private fun init() {
        binding.open.setOnClickListener {
            BottomSheetDialog().show(supportFragmentManager, "Dialog")
        }
    }

}

7.Developer Documents Bottom Sheets

Open in Documents Bottom Sheets

發表迴響