change questDetail layout to fix the background parallax

This commit is contained in:
Thomas Andres Gomez 2023-08-02 11:15:48 +02:00
parent 4967989315
commit fb0ae34b4a
6 changed files with 63 additions and 117 deletions

View file

@ -1,44 +0,0 @@
package com.pixelized.rplexicon.ui.composable.remember
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Stable
data class LazyListOffset(
val offsetY: State<Dp>,
val connection: NestedScrollConnection,
)
@Composable
@Stable
fun rememberLazyListOffset(
lazyListState: LazyListState,
): LazyListOffset {
val density = LocalDensity.current
return remember {
val offsetY = mutableStateOf(0.dp)
LazyListOffset(
offsetY = offsetY,
connection = object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
if (lazyListState.canScrollForward || lazyListState.canScrollBackward) {
offsetY.value -= with(density) { available.y.toDp() }
}
return Offset.Zero
}
},
)
}
}

View file

@ -62,6 +62,7 @@ import com.pixelized.rplexicon.utilitary.composable.stringResource
import com.pixelized.rplexicon.utilitary.extentions.annotatedSpan
import com.pixelized.rplexicon.utilitary.extentions.annotatedString
import com.pixelized.rplexicon.utilitary.extentions.highlightRegex
import com.pixelized.rplexicon.utilitary.extentions.scrollOffset
import com.skydoves.landscapist.ImageOptions
@Stable
@ -189,7 +190,7 @@ private fun LexiconDetailContent(
)
},
) { paddingValues ->
Box(
Surface(
modifier = Modifier.padding(paddingValues = paddingValues),
) {
annotatedItem.portrait.firstOrNull()?.let { uri ->
@ -332,15 +333,6 @@ private fun rememberPortraitWidth(): Dp {
}
}
@Stable
private fun Modifier.scrollOffset(
scrollState: ScrollState,
block: (Dp) -> Dp
): Modifier = composed {
val density = LocalDensity.current
this.offset(y = with(density) { block(scrollState.value.toDp()) })
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO, heightDp = 980)
@Preview(uiMode = UI_MODE_NIGHT_YES, heightDp = 980)

View file

@ -3,20 +3,18 @@ package com.pixelized.rplexicon.ui.screens.quest.detail
import android.content.res.Configuration
import android.net.Uri
import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@ -32,11 +30,9 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@ -47,17 +43,15 @@ import androidx.compose.ui.text.style.TextOverflow
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
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.BackgroundImage
import com.pixelized.rplexicon.ui.composable.remember.LazyListOffset
import com.pixelized.rplexicon.ui.composable.remember.rememberLazyListOffset
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexiconDetail
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.lexicon
import com.pixelized.rplexicon.utilitary.extentions.scrollOffset
import java.lang.Integer.min
@Stable
@ -160,11 +154,11 @@ fun QuestDetailScreen(
@Composable
private fun QuestDetailContent(
modifier: Modifier = Modifier,
state: ScrollState = rememberScrollState(),
item: State<QuestDetailUio>,
onBack: () -> Unit,
onGiver: (Int) -> Unit,
) {
val state = rememberLazyListState()
val annotatedQuest = item.value.annotate()
Scaffold(
@ -186,51 +180,42 @@ private fun QuestDetailContent(
)
},
content = { padding ->
val lazyListOffset = rememberLazyListOffset(lazyListState = state)
Surface(
modifier = Modifier
.padding(padding)
.nestedScroll(lazyListOffset.connection),
modifier = Modifier.padding(padding),
) {
BackgroundImage(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(ratio = 1f)
.scrollOffset(scrollState = lazyListOffset) { -it / 2 },
.scrollOffset(scrollState = state) { -it / 2 },
model = { annotatedQuest.background },
)
LazyColumn(
state = state,
contentPadding = PaddingValues(
top = 248.dp,
bottom = 16.dp,
start = 16.dp,
end = 16.dp
),
verticalArrangement = Arrangement.spacedBy(0.dp),
Column(
modifier = Modifier
.verticalScroll(state)
.padding(top = 248.dp, bottom = 16.dp, start = 16.dp, end = 16.dp),
) {
item {
Column {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.displaySmall,
text = annotatedQuest.title,
)
Image(
modifier = Modifier
.height(24.dp)
.graphicsLayer { rotationZ = 180f }
.align(Alignment.CenterHorizontally),
painter = painterResource(id = R.drawable.art_divider_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
}
Column {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.displaySmall,
text = annotatedQuest.title,
)
Image(
modifier = Modifier
.height(24.dp)
.graphicsLayer { rotationZ = 180f }
.align(Alignment.CenterHorizontally),
painter = painterResource(id = R.drawable.art_divider_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
}
items(annotatedQuest.steps) { quest ->
annotatedQuest.steps.forEach { quest ->
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
@ -279,7 +264,7 @@ private fun QuestDetailContent(
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = "Commanditaire",
text = stringResource(id = R.string.quest_detail_giver),
)
Text(
style = MaterialTheme.typography.bodyMedium,
@ -292,7 +277,7 @@ private fun QuestDetailContent(
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = "Lieu",
text = stringResource(id = R.string.quest_detail_area),
)
Text(
style = MaterialTheme.typography.bodyMedium,
@ -305,7 +290,7 @@ private fun QuestDetailContent(
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = "Récompense de groupe",
text = stringResource(id = R.string.quest_detail_individual_reward),
)
Text(
style = MaterialTheme.typography.bodyMedium,
@ -318,7 +303,7 @@ private fun QuestDetailContent(
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = "Récompense individuelle",
text = stringResource(id = R.string.quest_detail_group_rewars),
)
Text(
style = MaterialTheme.typography.bodyMedium,
@ -335,19 +320,10 @@ private fun QuestDetailContent(
}
}
}
},
)
}
@Stable
private fun Modifier.scrollOffset(
scrollState: LazyListOffset,
block: (Dp) -> Dp
): Modifier = composed {
this.offset(y = block(scrollState.offsetY.value))
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)

View file

@ -3,23 +3,27 @@ package com.pixelized.rplexicon.utilitary.extentions
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.google.accompanist.placeholder.PlaceholderHighlight
import com.google.accompanist.placeholder.placeholder
@Composable
fun Modifier.placeholder(
color: Color = MaterialTheme.lexicon.colorScheme.placeholder,
color: Color = Color.Unspecified,
shape: Shape = CircleShape,
highlight: PlaceholderHighlight? = null,
placeholderFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> = { spring() },
@ -28,7 +32,7 @@ fun Modifier.placeholder(
): Modifier = composed {
placeholder(
visible = visible(),
color = color,
color = if (color == Color.Unspecified) MaterialTheme.lexicon.colorScheme.placeholder else color,
shape = shape,
highlight = highlight,
placeholderFadeTransitionSpec = placeholderFadeTransitionSpec,
@ -36,10 +40,19 @@ fun Modifier.placeholder(
)
}
@Composable
@Stable
fun Modifier.cell() = composed {
Modifier
.fillMaxWidth()
.heightIn(min = MaterialTheme.lexicon.dimens.item)
.padding(horizontal = 16.dp, vertical = 4.dp)
}
@Stable
fun Modifier.scrollOffset(
scrollState: ScrollState,
block: (Dp) -> Dp
): Modifier = composed {
val density = LocalDensity.current
this.offset(y = with(density) { block(scrollState.value.toDp()) })
}

View file

@ -47,4 +47,8 @@
<string name="search_item_tags">Mots clés :</string>
<string name="quest_detail_title">Détails de quête</string>
<string name="quest_detail_giver">Commanditaire :</string>
<string name="quest_detail_area">Lieu :</string>
<string name="quest_detail_individual_reward">Récompense individuelle :</string>
<string name="quest_detail_group_rewars">Récompense de groupe :</string>
</resources>

View file

@ -47,4 +47,9 @@
<string name="search_item_tags">Tags:</string>
<string name="quest_detail_title">Quest details</string>
<string name="quest_detail_giver">Quest giver:</string>
<string name="quest_detail_area">Area:</string>
<string name="quest_detail_individual_reward">Individual reward:</string>
<string name="quest_detail_group_rewars">Group reward:</string>
</resources>