Floating Menu

This commit is contained in:
tom5079
2021-09-17 17:22:41 +09:00
parent cfe6a814d4
commit 29aefa4197
2 changed files with 120 additions and 13 deletions

View File

@@ -31,11 +31,17 @@ import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material.icons.filled.Science
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.accompanist.appcompattheme.AppCompatTheme
import org.kodein.di.DIAware
import org.kodein.di.android.closestDI
import xyz.quaver.pupil.databinding.ReaderActivityBinding
import xyz.quaver.pupil.ui.composable.FloatingActionButtonState
import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton
import xyz.quaver.pupil.ui.composable.SubFabItem
import xyz.quaver.pupil.ui.viewmodel.ReaderViewModel
@@ -43,15 +49,14 @@ import xyz.quaver.pupil.ui.viewmodel.ReaderViewModel
class ReaderActivity : ComponentActivity(), DIAware {
override val di by closestDI()
private var menu: Menu? = null
private lateinit var bindiddng: ReaderActivityBinding
private val model: ReaderViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
AppCompatTheme {
Scaffold(
topBar = {
@@ -60,10 +65,15 @@ class ReaderActivity : ComponentActivity(), DIAware {
)
},
floatingActionButton = {
MultipleFloatingActionButton(items = listOf(
SubFabItem(Icons.Default.Science, "Testing"),
SubFabItem(Icons.Default.DateRange, "EY")
))
MultipleFloatingActionButton(
items = listOf(
// TODO
),
targetState = isFABExpanded,
onStateChanged = {
isFABExpanded = it
}
)
}
) {

View File

@@ -1,6 +1,8 @@
package xyz.quaver.pupil.ui.composable
import androidx.compose.animation.core.updateTransition
import android.view.animation.AccelerateInterpolator
import android.view.animation.OvershootInterpolator
import androidx.compose.animation.core.*
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -10,13 +12,18 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Stop
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
enum class FloatingActionButtonState(val isExpanded: Boolean) {
@@ -41,6 +48,9 @@ data class SubFabItem(
fun MiniFloatingActionButton(
modifier: Modifier = Modifier,
item: SubFabItem,
buttonScale: Float = 1f,
labelAlpha: Float = 1f,
labelOffset: Dp = 0.dp,
onClick: ((SubFabItem) -> Unit)? = null
) {
Row(
@@ -53,15 +63,20 @@ fun MiniFloatingActionButton(
item.label?.let { label ->
Surface(
modifier = Modifier
.alpha(labelAlpha)
.offset(x = labelOffset),
shape = RoundedCornerShape(4.dp),
elevation = elevation.elevation(interactionSource).value
) {
Text(modifier = Modifier.padding(4.dp), text = label)
Text(modifier = Modifier.padding(8.dp, 4.dp), text = label)
}
}
FloatingActionButton(
modifier = Modifier.size(32.dp),
modifier = Modifier
.size(32.dp)
.scale(buttonScale),
onClick = { onClick?.invoke(item) },
elevation = elevation,
interactionSource = interactionSource
@@ -90,12 +105,94 @@ fun MultipleFloatingActionButton(
) {
val transition = updateTransition(targetState = targetState, label = "expand")
val rotation by transition.animateFloat(
label = "FABRotation",
transitionSpec = {
spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessMedium
)
}) { state ->
when (state) {
FloatingActionButtonState.COLLAPSED -> 0f
FloatingActionButtonState.EXPANDED -> 45f
}
}
Column(
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items.forEach { item ->
MiniFloatingActionButton(modifier = Modifier.padding(end = 12.dp),item = item) {
items.forEachIndexed { index, item ->
val delay = when (targetState) {
FloatingActionButtonState.COLLAPSED -> index
FloatingActionButtonState.EXPANDED -> (items.size - index)
} * 50
val buttonScale by transition.animateFloat(
label = "miniFAB scale",
transitionSpec = {
tween(
durationMillis = 100,
delayMillis = delay
)
}
) { state ->
when (state) {
FloatingActionButtonState.COLLAPSED -> 0f
FloatingActionButtonState.EXPANDED -> 1f
}
}
val labelAlpha by transition.animateFloat(
label = "miniFAB alpha",
transitionSpec = {
tween(
durationMillis = 150,
delayMillis = delay,
)
}
) { state ->
when (state) {
FloatingActionButtonState.COLLAPSED -> 0f
FloatingActionButtonState.EXPANDED -> 1f
}
}
val labelOffset by transition.animateDp(
label = "miniFAB offset",
transitionSpec = {
keyframes {
durationMillis = 200
delayMillis = delay
when (targetState) {
FloatingActionButtonState.COLLAPSED -> {
0.dp at 0
64.dp at 200
}
FloatingActionButtonState.EXPANDED -> {
64.dp at 0
(-4).dp at 150 with LinearEasing
0.dp at 200 with FastOutLinearInEasing
}
}
}
}
) { state ->
when (state) {
FloatingActionButtonState.COLLAPSED -> 64.dp
FloatingActionButtonState.EXPANDED -> 0.dp
}
}
MiniFloatingActionButton(
modifier = Modifier.padding(end = 12.dp),
item = item,
buttonScale = buttonScale,
labelAlpha = labelAlpha,
labelOffset = labelOffset
) {
onItemClick?.invoke(it)
}
}
@@ -103,7 +200,7 @@ fun MultipleFloatingActionButton(
FloatingActionButton(onClick = {
onStateChanged?.invoke(!targetState)
}) {
Icon(imageVector = fabIcon, contentDescription = null)
Icon(modifier = Modifier.rotate(rotation), imageVector = fabIcon, contentDescription = null)
}
}
}