change questDetail layout to fix the background parallax
This commit is contained in:
		
							parent
							
								
									4967989315
								
							
						
					
					
						commit
						fb0ae34b4a
					
				
					 6 changed files with 63 additions and 117 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()) })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue