Remove corner clip, draw content behind system bars
This commit is contained in:
parent
e565780015
commit
c7c97a3866
@ -14,9 +14,11 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.blur
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@ -111,7 +113,6 @@ class LauncherActivity : BaseActivity() {
|
||||
PullDownScaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
.graphicsLayer {
|
||||
scaleX = 0.5f + enterTransition.value * 0.5f
|
||||
scaleY = 0.5f + enterTransition.value * 0.5f
|
||||
@ -125,7 +126,6 @@ class LauncherActivity : BaseActivity() {
|
||||
PagerScaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
.graphicsLayer {
|
||||
scaleX = enterTransition.value
|
||||
scaleY = enterTransition.value
|
||||
|
||||
@ -22,21 +22,17 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
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.input.pointer.pointerInput
|
||||
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.Velocity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.ktx.toPixels
|
||||
@ -61,7 +57,6 @@ fun PagerScaffold(
|
||||
) {
|
||||
val viewModel: LauncherScaffoldVM = viewModel()
|
||||
val searchVM: SearchVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
|
||||
val isSearchOpen by viewModel.isSearchOpen.observeAsState(false)
|
||||
val isWidgetEditMode by viewModel.isWidgetEditMode.observeAsState(false)
|
||||
@ -70,6 +65,25 @@ fun PagerScaffold(
|
||||
val searchScrollState = rememberScrollState()
|
||||
val swipeableState = rememberSwipeableState(if (isSearchOpen) Page.Search else Page.Widgets)
|
||||
|
||||
val showStatusBarScrim by remember {
|
||||
derivedStateOf {
|
||||
if (isSearchOpen) {
|
||||
searchScrollState.value < searchScrollState.maxValue
|
||||
} else {
|
||||
widgetsScrollState.value > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
val showNavBarScrim by remember {
|
||||
derivedStateOf {
|
||||
if (isSearchOpen) {
|
||||
searchScrollState.value > 0
|
||||
} else {
|
||||
widgetsScrollState.value > 0 && widgetsScrollState.value < widgetsScrollState.maxValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val isWidgetsScrollZero by remember {
|
||||
derivedStateOf {
|
||||
widgetsScrollState.value == 0
|
||||
@ -79,11 +93,15 @@ fun PagerScaffold(
|
||||
val systemUiController = rememberSystemUiController()
|
||||
|
||||
val colorSurface = MaterialTheme.colorScheme.surface
|
||||
LaunchedEffect(isWidgetEditMode, darkStatusBarIcons, colorSurface) {
|
||||
LaunchedEffect(isWidgetEditMode, darkStatusBarIcons, colorSurface, showStatusBarScrim) {
|
||||
if (isWidgetEditMode) {
|
||||
systemUiController.setStatusBarColor(
|
||||
colorSurface
|
||||
)
|
||||
} else if (showStatusBarScrim) {
|
||||
systemUiController.setStatusBarColor(
|
||||
colorSurface.copy(0.75f),
|
||||
)
|
||||
} else {
|
||||
systemUiController.setStatusBarColor(
|
||||
Color.Transparent,
|
||||
@ -92,12 +110,18 @@ fun PagerScaffold(
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(darkNavBarIcons) {
|
||||
systemUiController.setNavigationBarColor(
|
||||
Color.Transparent,
|
||||
darkIcons = darkNavBarIcons,
|
||||
navigationBarContrastEnforced = false
|
||||
)
|
||||
LaunchedEffect(darkNavBarIcons, showNavBarScrim) {
|
||||
if (showNavBarScrim) {
|
||||
systemUiController.setNavigationBarColor(
|
||||
colorSurface.copy(0.75f),
|
||||
)
|
||||
} else {
|
||||
systemUiController.setNavigationBarColor(
|
||||
Color.Transparent,
|
||||
darkIcons = darkNavBarIcons,
|
||||
navigationBarContrastEnforced = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val blurWallpaper by remember {
|
||||
@ -138,11 +162,11 @@ fun PagerScaffold(
|
||||
}
|
||||
}
|
||||
|
||||
val notificationDragThreshold = with(LocalDensity.current) {200.dp.toPx()}
|
||||
val notificationDragThreshold = with(LocalDensity.current) { 200.dp.toPx() }
|
||||
val notificationShadeController = rememberNotificationShadeController()
|
||||
|
||||
val nestedScrollConnection = remember {
|
||||
object: NestedScrollConnection {
|
||||
object : NestedScrollConnection {
|
||||
private var pullDownTotalY: Float? = 0f
|
||||
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||
val diff = widgetsScrollState.value - available.y
|
||||
@ -171,6 +195,8 @@ fun PagerScaffold(
|
||||
}
|
||||
}
|
||||
|
||||
val insets = WindowInsets.systemBars.asPaddingValues()
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
) {
|
||||
@ -218,12 +244,12 @@ fun PagerScaffold(
|
||||
val editModePadding by animateDpAsState(if (isWidgetEditMode) 56.dp else 0.dp)
|
||||
|
||||
val clockPadding by animateDpAsState(
|
||||
if (isWidgetsScrollZero) 64.dp else 0.dp
|
||||
if (isWidgetsScrollZero) 64.dp + insets.calculateBottomPadding() else 0.dp
|
||||
)
|
||||
|
||||
val clockHeight by remember {
|
||||
derivedStateOf {
|
||||
height - (64.dp - clockPadding)
|
||||
height - (64.dp + insets.calculateTopPadding() + insets.calculateBottomPadding() - clockPadding)
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,10 +257,10 @@ fun PagerScaffold(
|
||||
modifier = Modifier
|
||||
.requiredWidth(width)
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 8.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.nestedScroll(nestedScrollConnection)
|
||||
.verticalScroll(widgetsScrollState)
|
||||
.systemBarsPadding()
|
||||
.padding(horizontal = 8.dp)
|
||||
.padding(top = 8.dp, bottom = 64.dp)
|
||||
.padding(top = editModePadding),
|
||||
clockHeight = { clockHeight },
|
||||
@ -254,10 +280,10 @@ fun PagerScaffold(
|
||||
modifier = Modifier
|
||||
.requiredWidth(width)
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 8.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.verticalScroll(searchScrollState, reverseScrolling = true)
|
||||
.imePadding()
|
||||
.systemBarsPadding()
|
||||
.padding(horizontal = 8.dp)
|
||||
.padding(top = 8.dp, bottom = 64.dp)
|
||||
.padding(bottom = webSearchPadding),
|
||||
reverse = true,
|
||||
@ -302,6 +328,7 @@ fun PagerScaffold(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)
|
||||
.systemBarsPadding()
|
||||
.imePadding()
|
||||
.offset(y = widgetEditModeOffset),
|
||||
level = { searchBarLevel }, focused = focusSearchBar, onFocusChange = {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package de.mm20.launcher2.ui.launcher
|
||||
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
@ -19,7 +18,6 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
@ -43,6 +41,7 @@ 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 de.mm20.launcher2.ui.modifier.verticalFadingEdges
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@ -55,7 +54,6 @@ fun PullDownScaffold(
|
||||
) {
|
||||
val viewModel: LauncherScaffoldVM = viewModel()
|
||||
val searchVM: SearchVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
|
||||
val density = LocalDensity.current
|
||||
|
||||
@ -67,12 +65,41 @@ fun PullDownScaffold(
|
||||
|
||||
val systemUiController = rememberSystemUiController()
|
||||
|
||||
val isWidgetsScrollZero by remember {
|
||||
derivedStateOf {
|
||||
widgetsScrollState.value == 0
|
||||
}
|
||||
}
|
||||
|
||||
val showStatusBarScrim by remember {
|
||||
derivedStateOf {
|
||||
if (isSearchOpen) {
|
||||
searchScrollState.value > 0
|
||||
} else {
|
||||
widgetsScrollState.value > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
val showNavBarScrim by remember {
|
||||
derivedStateOf {
|
||||
if (isSearchOpen) {
|
||||
searchScrollState.value < searchScrollState.maxValue
|
||||
} else {
|
||||
widgetsScrollState.value > 0 && widgetsScrollState.value < widgetsScrollState.maxValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val colorSurface = MaterialTheme.colorScheme.surface
|
||||
LaunchedEffect(isWidgetEditMode, darkStatusBarIcons, colorSurface) {
|
||||
LaunchedEffect(isWidgetEditMode, darkStatusBarIcons, colorSurface, showStatusBarScrim) {
|
||||
if (isWidgetEditMode) {
|
||||
systemUiController.setStatusBarColor(
|
||||
colorSurface
|
||||
)
|
||||
} else if (showStatusBarScrim) {
|
||||
systemUiController.setStatusBarColor(
|
||||
colorSurface.copy(0.75f),
|
||||
)
|
||||
} else {
|
||||
systemUiController.setStatusBarColor(
|
||||
Color.Transparent,
|
||||
@ -81,12 +108,18 @@ fun PullDownScaffold(
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(darkNavBarIcons) {
|
||||
systemUiController.setNavigationBarColor(
|
||||
Color.Transparent,
|
||||
darkIcons = darkNavBarIcons,
|
||||
navigationBarContrastEnforced = false
|
||||
)
|
||||
LaunchedEffect(darkNavBarIcons, showNavBarScrim) {
|
||||
if (showNavBarScrim) {
|
||||
systemUiController.setNavigationBarColor(
|
||||
colorSurface.copy(0.75f),
|
||||
)
|
||||
} else {
|
||||
systemUiController.setNavigationBarColor(
|
||||
Color.Transparent,
|
||||
darkIcons = darkNavBarIcons,
|
||||
navigationBarContrastEnforced = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val offsetY = remember { mutableStateOf(0f) }
|
||||
@ -151,7 +184,7 @@ fun PullDownScaffold(
|
||||
consumed
|
||||
}
|
||||
isSearchOpen && (offsetY.value < 0 || source == NestedScrollSource.Drag && newValue > searchScrollState.maxValue) -> {
|
||||
val consumed = available.y - (value- searchScrollState.maxValue)
|
||||
val consumed = available.y - (value - searchScrollState.maxValue)
|
||||
offsetY.value = (offsetY.value + (consumed * 0.5f)).coerceIn(-maxOffset, 0f)
|
||||
consumed
|
||||
}
|
||||
@ -182,11 +215,13 @@ fun PullDownScaffold(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val insets = WindowInsets.systemBars.asPaddingValues()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.verticalFadingEdges(
|
||||
top = insets.calculateTopPadding(),
|
||||
amount = 0.85f
|
||||
)
|
||||
.nestedScroll(nestedScrollConnection)
|
||||
.offset { IntOffset(0, offsetY.value.toInt()) },
|
||||
contentAlignment = Alignment.TopCenter
|
||||
@ -230,12 +265,21 @@ fun PullDownScaffold(
|
||||
.fillMaxWidth()
|
||||
.requiredHeight(height)
|
||||
.verticalScroll(searchScrollState)
|
||||
.padding(vertical = 8.dp)
|
||||
.systemBarsPadding()
|
||||
.padding(8.dp)
|
||||
.padding(top = 56.dp)
|
||||
.padding(top = webSearchPadding)
|
||||
.imePadding()
|
||||
)
|
||||
val editModePadding by animateDpAsState(if (isWidgetEditMode) 56.dp else 0.dp)
|
||||
val clockPadding by animateDpAsState(
|
||||
if (isWidgetsScrollZero) insets.calculateBottomPadding() else 0.dp
|
||||
)
|
||||
val clockHeight by remember {
|
||||
derivedStateOf {
|
||||
height - (insets.calculateTopPadding() + insets.calculateBottomPadding() - clockPadding)
|
||||
}
|
||||
}
|
||||
WidgetColumn(
|
||||
modifier =
|
||||
Modifier
|
||||
@ -248,9 +292,11 @@ fun PullDownScaffold(
|
||||
.fillMaxWidth()
|
||||
.requiredHeight(height)
|
||||
.verticalScroll(widgetsScrollState)
|
||||
.padding(vertical = 8.dp)
|
||||
.systemBarsPadding()
|
||||
.padding(8.dp)
|
||||
.padding(top = editModePadding),
|
||||
clockHeight = { height },
|
||||
clockHeight = { clockHeight },
|
||||
clockBottomPadding = { clockPadding },
|
||||
editMode = isWidgetEditMode,
|
||||
onEditModeChange = {
|
||||
viewModel.setWidgetEditMode(it)
|
||||
@ -268,6 +314,8 @@ fun PullDownScaffold(
|
||||
exit = slideOut { IntOffset(0, -it.height) }
|
||||
) {
|
||||
CenterAlignedTopAppBar(
|
||||
modifier = Modifier
|
||||
.systemBarsPadding(),
|
||||
title = {
|
||||
Text(stringResource(R.string.menu_edit_widgets))
|
||||
},
|
||||
@ -285,7 +333,7 @@ fun PullDownScaffold(
|
||||
offsetY.value != 0f -> SearchBarLevel.Raised
|
||||
isSearchOpen && searchScrollState.value == 0 -> SearchBarLevel.Active
|
||||
isSearchOpen && searchScrollState.value > 0 -> SearchBarLevel.Raised
|
||||
widgetsScrollState.value > 0 -> SearchBarLevel.Raised
|
||||
!isWidgetsScrollZero -> SearchBarLevel.Raised
|
||||
else -> SearchBarLevel.Resting
|
||||
}
|
||||
}
|
||||
@ -300,7 +348,8 @@ fun PullDownScaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(vertical = 8.dp)
|
||||
.systemBarsPadding()
|
||||
.padding(8.dp)
|
||||
.offset { IntOffset(0, searchBarOffset.value.toInt()) }
|
||||
.offset {
|
||||
IntOffset(
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
package de.mm20.launcher2.ui.modifier
|
||||
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.LinearGradientShader
|
||||
import androidx.compose.ui.graphics.Paint
|
||||
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.pow
|
||||
|
||||
fun Modifier.verticalFadingEdges(
|
||||
enabled: Boolean = true,
|
||||
top: Dp = 0.dp,
|
||||
bottom: Dp = 0.dp,
|
||||
/**
|
||||
* How strong the fading effect should be.
|
||||
* If 1, edges will be completely transparent.
|
||||
* If 0, the modifier will have no effect at all.
|
||||
*/
|
||||
@FloatRange(from = 0.0, to = 1.0, fromInclusive = true, toInclusive = true) amount: Float = 1f
|
||||
): Modifier {
|
||||
if(!enabled) return this
|
||||
if (top == 0.dp && bottom == 0.dp) return this
|
||||
|
||||
return this then drawWithContent {
|
||||
|
||||
val topColors = if (top > 0.dp) createColors(
|
||||
1f - amount,
|
||||
top.roundToPx() + 1,
|
||||
) else emptyList()
|
||||
val bottomColors = if (bottom > 0.dp) createColors(
|
||||
1f - amount,
|
||||
bottom.roundToPx() + 1,
|
||||
reverse = true
|
||||
) else emptyList()
|
||||
|
||||
val topSteps = if (top > 0.dp) createColorSteps(
|
||||
size.height,
|
||||
top.toPx() * 1.3f,
|
||||
top.roundToPx() + 1
|
||||
) else emptyList()
|
||||
val bottomSteps = if (bottom > 0.dp) createColorSteps(
|
||||
size.height,
|
||||
bottom.toPx() * 1.3f,
|
||||
bottom.roundToPx() + 1,
|
||||
reverse = true
|
||||
) else emptyList()
|
||||
|
||||
val paint = Paint().apply {
|
||||
blendMode = BlendMode.DstIn
|
||||
shader = LinearGradientShader(
|
||||
Offset.Zero,
|
||||
Offset(0f, size.height),
|
||||
colors = topColors + bottomColors,
|
||||
colorStops = topSteps + bottomSteps
|
||||
)
|
||||
}
|
||||
drawContent()
|
||||
drawIntoCanvas {
|
||||
it.drawRect(
|
||||
Rect(0f, 0f, size.width, size.height),
|
||||
paint
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createColors(alpha: Float, steps: Int, reverse: Boolean = false): List<Color> {
|
||||
val interval = 1f / (steps - 1)
|
||||
return (0 until steps).map {
|
||||
val x = interval * if (reverse) (steps - 1 - it) else it
|
||||
val y = (1 - alpha) * (1f - (x - 1f).pow(2)) + alpha
|
||||
Color.Black.copy(alpha = y)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createColorSteps(height: Float, size: Float, steps: Int, reverse: Boolean = false): List<Float> {
|
||||
val interval = 1f / (steps - 1)
|
||||
return (0 until steps).map {
|
||||
val x = interval * if (reverse) (steps - 1 - it) else it
|
||||
if (reverse) 1 - (x * size / height) else (x * size / height)
|
||||
}
|
||||
}
|
||||
93
ui/src/main/java/de/mm20/launcher2/ui/modifier/Scrims.kt
Normal file
93
ui/src/main/java/de/mm20/launcher2/ui/modifier/Scrims.kt
Normal file
@ -0,0 +1,93 @@
|
||||
package de.mm20.launcher2.ui.modifier
|
||||
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.LinearGradientShader
|
||||
import androidx.compose.ui.graphics.Paint
|
||||
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.pow
|
||||
|
||||
fun Modifier.verticalScrims(
|
||||
enabled: Boolean = true,
|
||||
top: Dp = 0.dp,
|
||||
bottom: Dp = 0.dp,
|
||||
/**
|
||||
* How strong the fading effect should be.
|
||||
* If 1, edges will be completely transparent.
|
||||
* If 0, the modifier will have no effect at all.
|
||||
*/
|
||||
@FloatRange(from = 0.0, to = 1.0, fromInclusive = true, toInclusive = true) amount: Float = 1f
|
||||
): Modifier {
|
||||
if (!enabled) return this
|
||||
if (top == 0.dp && bottom == 0.dp) return this
|
||||
|
||||
return this then drawWithCache {
|
||||
onDrawWithContent {
|
||||
val topColors = if (top > 0.dp) createColors(
|
||||
1f - amount,
|
||||
top.roundToPx() + 1,
|
||||
) else emptyList()
|
||||
val bottomColors = if (bottom > 0.dp) createColors(
|
||||
1f - amount,
|
||||
bottom.roundToPx() + 1,
|
||||
reverse = true
|
||||
) else emptyList()
|
||||
|
||||
val topSteps = if (top > 0.dp) createColorSteps(
|
||||
size.height,
|
||||
top.toPx() * 1.3f,
|
||||
top.roundToPx() + 1
|
||||
) else emptyList()
|
||||
val bottomSteps = if (bottom > 0.dp) createColorSteps(
|
||||
size.height,
|
||||
bottom.toPx() * 1.3f,
|
||||
bottom.roundToPx() + 1,
|
||||
reverse = true
|
||||
) else emptyList()
|
||||
|
||||
val paint = Paint().apply {
|
||||
shader = LinearGradientShader(
|
||||
Offset.Zero,
|
||||
Offset(0f, size.height),
|
||||
colors = topColors + bottomColors,
|
||||
colorStops = topSteps + bottomSteps
|
||||
)
|
||||
}
|
||||
drawContent()
|
||||
drawIntoCanvas {
|
||||
it.drawRect(
|
||||
Rect(0f, 0f, size.width, size.height),
|
||||
paint
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createColors(alpha: Float, steps: Int, reverse: Boolean = false): List<Color> {
|
||||
val interval = 1f / (steps - 1)
|
||||
return (0 until steps).map {
|
||||
val x = interval * if (reverse) (steps - 1 - it) else it
|
||||
val y = 1f - ((1 - alpha) * (1f - (x - 1f).pow(2)) + alpha)
|
||||
Color.Black.copy(alpha = y)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createColorSteps(
|
||||
height: Float,
|
||||
size: Float,
|
||||
steps: Int,
|
||||
reverse: Boolean = false
|
||||
): List<Float> {
|
||||
val interval = 1f / (steps - 1)
|
||||
return (0 until steps).map {
|
||||
val x = interval * if (reverse) (steps - 1 - it) else it
|
||||
if (reverse) 1 - (x * size / height) else (x * size / height)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user