如何在 Jetpack Compose 中使用事务执行此滚动隐藏 fab 按钮

GvH*_*ish 7 android android-layout android-jetpack android-jetpack-compose

如何在 Jetpack Compose 中使用事务执行此滚动隐藏 fab 按钮

像这样我需要它:

And*_*i R 9

您可以使用NestedScrollConnectionAnimatedVisibility来更改 的可见性ExtendedFloatingActionButton

  1. 您应该使用remeberSaveable来存储以下状态ExtendedFloatingActionButton
// Visibility for FAB, could be saved in viewModel
val isVisible = rememberSaveable { mutableStateOf(true) }
Run Code Online (Sandbox Code Playgroud)
  1. 实施NestedScrollConnection,您可以使用您的值来显示/隐藏 FAB:
// Nested scroll for control FAB
val nestedScrollConnection = remember {
    object : NestedScrollConnection {
        override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
            // Hide FAB
            if (available.y < -1) {
                isVisible.value = false
            }

            // Show FAB
            if (available.y > 1) {
                isVisible.value = true
            }

            return Offset.Zero
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 实现ExtendedFloatingActionButtoninside AnimatedVisibility,设置可见值isVisible并设置进入和退出动画,在我的例子中,我用于slideInVertically进入动画和slideOutVertically退出动画:
AnimatedVisibility(
    visible = isVisible.value,
    enter = slideInVertically(initialOffsetY = { it * 2 }),
    exit = slideOutVertically(targetOffsetY = { it * 2 }),
) {
    ExtendedFloatingActionButton(
        onClick = {
            // FAB click
        }
    ) {
        Text(
            text = "Extended FAB"
        )
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 适用NestedScrollConnection于可滚动内容,在我的例子中LazyColumn
LazyColumn(
    modifier = Modifier
        .fillMaxWidth()
        .padding(innerPadding)
        .nestedScroll(nestedScrollConnection),
) {
    items(100) { index ->
        Text(
            modifier = Modifier.padding(16.dp),
            text = "Item: $index"
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

MainActivity的完整代码:

package com.andreirozov.animatedfab

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Add
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.dp
import com.andreirozov.animatedfab.ui.theme.AnimatedFabTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AnimatedFabApp()
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AnimatedFabApp() {
    AnimatedFabTheme {
        // Visibility for FAB, could be saved in viewModel
        val isVisible = rememberSaveable { mutableStateOf(true) }

        // Nested scroll for control FAB
        val nestedScrollConnection = remember {
            object : NestedScrollConnection {
                override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                    // Hide FAB
                    if (available.y < -1) {
                        isVisible.value = false
                    }

                    // Show FAB
                    if (available.y > 1) {
                        isVisible.value = true
                    }

                    return Offset.Zero
                }
            }
        }

        Scaffold(
            floatingActionButtonPosition = FabPosition.Center,
            floatingActionButton = {
                AnimatedVisibility(
                    visible = isVisible.value,
                    enter = slideInVertically(initialOffsetY = { it * 2 }),
                    exit = slideOutVertically(targetOffsetY = { it * 2 }),
                ) {
                    ExtendedFloatingActionButton(
                        onClick = {
                            // FAB click
                        }
                    ) {
                        Text(
                            text = "Extended FAB"
                        )
                    }
                }
            }
        ) { innerPadding ->
            LazyColumn(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(innerPadding)
                    .nestedScroll(nestedScrollConnection),
            ) {
                items(100) { index ->
                    Text(
                        modifier = Modifier.padding(16.dp),
                        text = "Item: $index"
                    )
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

在此输入图像描述


Aeg*_*gir 5

您需要监听滚动状态并应用AnimatedVisibiltiyLazyColumn这是一个使用with的示例LazyListState(您也可以使用Columnwith ScrollState

@Composable
fun Screen() {

    val listState = rememberLazyListState()
    val fabVisibility by derivedStateOf {
        listState.firstVisibleItemIndex == 0
    }

    Box(modifier = Modifier.fillMaxSize()) {
        LazyColumn(
            Modifier.fillMaxSize(),
            state = listState,
        ) {
            items(count = 100, key = { it.toString() }) {
                Text(modifier = Modifier.fillMaxWidth(),
                    text = "Hello $it!")
            }
        }

        AddPaymentFab(
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .padding(bottom = 40.dp),
            isVisibleBecauseOfScrolling = fabVisibility
        )
    }
}

@Composable
private fun AddPaymentFab(
    modifier: Modifier,
    isVisibleBecauseOfScrolling: Boolean,
) {
    val density = LocalDensity.current
    AnimatedVisibility(
        modifier = modifier,
        visible = isVisibleBecauseOfScrolling,
        enter = slideInVertically {
            with(density) { 40.dp.roundToPx() }
        } + fadeIn(),
        exit = fadeOut(
            animationSpec = keyframes {
                this.durationMillis = 120
            }
        )
    ) {
        ExtendedFloatingActionButton(
            text = { Text(text = "Add Payment") },
            onClick = { },
            icon = { Icon(Icons.Filled.Add, "Add Payment") }
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述