Improve pager scaffold performance
This commit is contained in:
parent
c922d62228
commit
764aa3e587
@ -7,38 +7,46 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.slideIn
|
||||
import androidx.compose.animation.slideOut
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.gestures.LocalOverScrollConfiguration
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.FractionalThreshold
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Done
|
||||
import androidx.compose.material.rememberSwipeableState
|
||||
import androidx.compose.material.swipeable
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import com.google.accompanist.pager.HorizontalPager
|
||||
import com.google.accompanist.pager.rememberPagerState
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.ktx.toPixels
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchBar
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchBarLevel
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchColumn
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchVM
|
||||
import de.mm20.launcher2.ui.launcher.widgets.WidgetColumn
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class)
|
||||
@OptIn(
|
||||
ExperimentalPagerApi::class, ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class,
|
||||
ExperimentalFoundationApi::class
|
||||
)
|
||||
@Composable
|
||||
fun PagerScaffold(
|
||||
modifier: Modifier = Modifier,
|
||||
@ -52,9 +60,9 @@ fun PagerScaffold(
|
||||
val isSearchOpen by viewModel.isSearchOpen.observeAsState(false)
|
||||
val isWidgetEditMode by viewModel.isWidgetEditMode.observeAsState(false)
|
||||
|
||||
val pagerState = rememberPagerState()
|
||||
val widgetsScrollState = rememberScrollState()
|
||||
val searchScrollState = rememberScrollState()
|
||||
val swipeableState = rememberSwipeableState(if (isSearchOpen) Page.Search else Page.Widgets)
|
||||
|
||||
val isWidgetsScrollZero by remember {
|
||||
derivedStateOf {
|
||||
@ -88,8 +96,8 @@ fun PagerScaffold(
|
||||
|
||||
val blurWallpaper by remember {
|
||||
derivedStateOf {
|
||||
isSearchOpen || pagerState.currentPage == 0 && pagerState.currentPageOffset > 0.5f ||
|
||||
pagerState.currentPage == 1 && pagerState.currentPageOffset <= 0.5f ||
|
||||
isSearchOpen || swipeableState.progress.to == Page.Widgets && swipeableState.progress.fraction <= 0.5f ||
|
||||
swipeableState.progress.to == Page.Search && swipeableState.progress.fraction > 0.5f ||
|
||||
!isWidgetsScrollZero
|
||||
}
|
||||
}
|
||||
@ -97,6 +105,7 @@ fun PagerScaffold(
|
||||
val density = LocalDensity.current
|
||||
|
||||
|
||||
|
||||
LaunchedEffect(blurWallpaper) {
|
||||
if (!isAtLeastApiLevel(31)) return@LaunchedEffect
|
||||
(context as Activity).window.attributes = context.window.attributes.also {
|
||||
@ -110,16 +119,16 @@ fun PagerScaffold(
|
||||
}
|
||||
}
|
||||
|
||||
val currentPage = pagerState.currentPage
|
||||
val currentPage = swipeableState.currentValue
|
||||
LaunchedEffect(currentPage) {
|
||||
if (currentPage == 1) viewModel.openSearch()
|
||||
if (currentPage == Page.Search) viewModel.openSearch()
|
||||
else viewModel.closeSearch()
|
||||
}
|
||||
|
||||
LaunchedEffect(isSearchOpen) {
|
||||
if (isSearchOpen) pagerState.animateScrollToPage(1)
|
||||
if (isSearchOpen) swipeableState.animateTo(Page.Search)
|
||||
else {
|
||||
pagerState.animateScrollToPage(0)
|
||||
swipeableState.animateTo(Page.Widgets)
|
||||
searchVM.search("")
|
||||
}
|
||||
}
|
||||
@ -136,64 +145,93 @@ fun PagerScaffold(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
) {
|
||||
|
||||
HorizontalPager(
|
||||
count = 2,
|
||||
state = pagerState,
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
userScrollEnabled = !isWidgetEditMode
|
||||
BoxWithConstraints(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
if (it == 1) {
|
||||
val websearches by searchVM.websearchResults.observeAsState(emptyList())
|
||||
val webSearchPadding by animateDpAsState(
|
||||
if (websearches.isEmpty()) 0.dp else 48.dp
|
||||
)
|
||||
SearchColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(searchScrollState, reverseScrolling = true)
|
||||
.imePadding()
|
||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp)
|
||||
.padding(bottom = webSearchPadding),
|
||||
reverse = true,
|
||||
)
|
||||
val height by remember {
|
||||
derivedStateOf { maxHeight }
|
||||
}
|
||||
val width by remember {
|
||||
derivedStateOf { maxWidth }
|
||||
}
|
||||
|
||||
if (it == 0) {
|
||||
val editModePadding by animateDpAsState(if (isWidgetEditMode) 56.dp else 0.dp)
|
||||
|
||||
val clockPadding by animateDpAsState(
|
||||
if (isWidgetsScrollZero) 64.dp else 0.dp
|
||||
)
|
||||
var size by remember { mutableStateOf(IntSize.Zero) }
|
||||
val widthPx = width.toPixels()
|
||||
|
||||
val clockHeight by remember {
|
||||
derivedStateOf {
|
||||
with(density) { size.height.toDp() } - (64.dp - clockPadding)
|
||||
}
|
||||
}
|
||||
|
||||
WidgetColumn(
|
||||
CompositionLocalProvider(
|
||||
LocalOverScrollConfiguration provides null
|
||||
) {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.onSizeChanged {
|
||||
size = it
|
||||
.requiredWidth(width * 2)
|
||||
.fillMaxHeight()
|
||||
.swipeable(
|
||||
swipeableState,
|
||||
orientation = Orientation.Horizontal,
|
||||
anchors = mapOf(
|
||||
-widthPx / 2f to Page.Search,
|
||||
widthPx / 2f to Page.Widgets,
|
||||
),
|
||||
thresholds = { _, _ ->
|
||||
FractionalThreshold(0.5f)
|
||||
},
|
||||
enabled = !isWidgetEditMode
|
||||
)
|
||||
.offset {
|
||||
IntOffset(swipeableState.offset.value.roundToInt(), 0)
|
||||
}
|
||||
) {
|
||||
|
||||
|
||||
val editModePadding by animateDpAsState(if (isWidgetEditMode) 56.dp else 0.dp)
|
||||
|
||||
val clockPadding by animateDpAsState(
|
||||
if (isWidgetsScrollZero) 64.dp else 0.dp
|
||||
)
|
||||
|
||||
val clockHeight by remember {
|
||||
derivedStateOf {
|
||||
height - (64.dp - clockPadding)
|
||||
}
|
||||
.verticalScroll(widgetsScrollState)
|
||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp)
|
||||
.padding(top = editModePadding),
|
||||
clockHeight = { clockHeight },
|
||||
clockBottomPadding = { clockPadding },
|
||||
editMode = isWidgetEditMode,
|
||||
onEditModeChange = {
|
||||
viewModel.setWidgetEditMode(it)
|
||||
}
|
||||
)
|
||||
|
||||
WidgetColumn(
|
||||
modifier = Modifier
|
||||
.requiredWidth(width)
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(widgetsScrollState)
|
||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp)
|
||||
.padding(top = editModePadding),
|
||||
clockHeight = { clockHeight },
|
||||
clockBottomPadding = { clockPadding },
|
||||
editMode = isWidgetEditMode,
|
||||
onEditModeChange = {
|
||||
viewModel.setWidgetEditMode(it)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
val websearches by searchVM.websearchResults.observeAsState(emptyList())
|
||||
val webSearchPadding by animateDpAsState(
|
||||
if (websearches.isEmpty()) 0.dp else 48.dp
|
||||
)
|
||||
SearchColumn(
|
||||
modifier = Modifier
|
||||
.requiredWidth(width)
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(searchScrollState, reverseScrolling = true)
|
||||
.imePadding()
|
||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp)
|
||||
.padding(bottom = webSearchPadding),
|
||||
reverse = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(visible = isWidgetEditMode,
|
||||
@ -215,7 +253,7 @@ fun PagerScaffold(
|
||||
val searchBarLevel by remember {
|
||||
derivedStateOf {
|
||||
when {
|
||||
pagerState.isScrollInProgress -> SearchBarLevel.Raised
|
||||
swipeableState.direction != 0f -> SearchBarLevel.Raised
|
||||
!isSearchOpen && isWidgetsScrollZero -> SearchBarLevel.Resting
|
||||
isSearchOpen && searchScrollState.value == 0 -> SearchBarLevel.Active
|
||||
else -> SearchBarLevel.Raised
|
||||
@ -242,5 +280,9 @@ fun PagerScaffold(
|
||||
reverse = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Page {
|
||||
Widgets,
|
||||
Search
|
||||
}
|
||||
@ -216,7 +216,6 @@ fun PullDownScaffold(
|
||||
val height by remember {
|
||||
derivedStateOf { maxHeight }
|
||||
}
|
||||
Log.d("MM20", "PullDownScaffold recompose, $maxHeight")
|
||||
CompositionLocalProvider(
|
||||
LocalOverScrollConfiguration provides null
|
||||
) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user