Top Sheet Dialogを実現する
今回はJetpack composeでTop Sheet Dialogを実現するプログラムに挑戦してみました! Material DesignのBottom sheets という部品を利用すれば、下部からダイアログを表示することはできますが、 上部からの表示はサポートされていないため、カスタムレイアウトやアニメーションを使用して、 BottomSheetのような動作を上部から表示させるための工夫をする必要があります。 これには、AnimatedVisibilityやSlideなどのJetpack ComposeのアニメーションとトランジションAPIを使用します。 今回の開発環境はAndroid Studio Giraffe(2022.3.1)を利用しています。
build.gradle(Module:app)
依存関係には現時点で最新のバージョンを指定しています。
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.example.topsheet'
compileSdk 33
defaultConfig {
applicationId "com.example.topsheet"
minSdk 29
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.4.3'
}
packaging {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
// Jetpack Composeのバージョン
def compose_version = "1.0.5" // このバージョンは適宜更新してください
// Material3のバージョン
def material3_version = "1.1.0-alpha06" // このバージョンは適宜更新してください
implementation "androidx.compose.material3:material3:$material3_version"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.runtime:runtime-rxjava2:$compose_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07"
implementation "androidx.activity:activity-compose:1.3.1"
}
MainActivity.kt
このコードでは、AnimatedVisibilityとSlideを使用して、上部からスライドダウンするパネルを作成しています。
アプリバーのメニューボタンをクリックするとTopSheetが表示され、TopSheet内のボタンをクリックするとTopSheetが非表示になります。
package com.example.topsheet
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.topsheet.ui.theme.TopsheetTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TopsheetTheme {
Surface(
modifier = Modifier.fillMaxSize()
) {
TopSheetExample()
}
}
}
}
}
@Composable
fun TopSheetExample() {
var showTopSheet by remember { mutableStateOf(false) }
Scaffold(
topBar = {
TopAppBar(
title = { Text("Top Sheet Example") },
actions = {
IconButton(onClick = { showTopSheet = true }) {
Icon(Icons.Filled.Menu, contentDescription = "Menu")
}
}
)
}
) { contentPadding ->
Box(modifier = Modifier.fillMaxSize()) {
// メインコンテンツはココに記述
// TopSheetコンテンツ
AnimatedVisibility(
visible = showTopSheet,
enter = slideInVertically(initialOffsetY = { -it }),
exit = slideOutVertically(targetOffsetY = { -it })
) {
Surface(
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
color = Color.LightGray,
elevation = 10.dp
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "This is a top sheet")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = { showTopSheet = false }) {
Text("Hide")
}
}
}
}
}
}
}
処理結果は次の通りです。メニューをクリックすると上部からTopSheetが表示されます。
こういった部品はありそうで無かったりするので、 同じような機能を実現するために、どのコンポーネントを組み合わせるかイメージができるとカスタム部品を作りやすいかも知れませんね。 イメージできそうですか?
あまりイメージできなかった人は、どんな部品でも良いので、自分なりのカスタム部品を作ってみてはいかがでしょうか?