Improve pager scaffold performance

This commit is contained in:
MM20 2022-05-16 21:24:19 +02:00
parent c922d62228
commit 764aa3e587
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
2 changed files with 100 additions and 59 deletions

View File

@ -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
}

View File

@ -216,7 +216,6 @@ fun PullDownScaffold(
val height by remember {
derivedStateOf { maxHeight }
}
Log.d("MM20", "PullDownScaffold recompose, $maxHeight")
CompositionLocalProvider(
LocalOverScrollConfiguration provides null
) {