mirror of
https://github.com/fankes/pagecurl-multiplatform.git
synced 2025-09-05 18:25:20 +08:00
Add examples to demo app
This commit is contained in:
29
demo/src/main/kotlin/eu/wewox/pagecurl/Example.kt
Normal file
29
demo/src/main/kotlin/eu/wewox/pagecurl/Example.kt
Normal file
@@ -0,0 +1,29 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
/**
|
||||
* Enumeration of available demo examples.
|
||||
*
|
||||
* @param label Example name.
|
||||
* @param description Brief description.
|
||||
*/
|
||||
enum class Example(
|
||||
val label: String,
|
||||
val description: String,
|
||||
) {
|
||||
SimplePageCurl(
|
||||
"Simple Page Curl",
|
||||
"Basic PageCurl usage"
|
||||
),
|
||||
SettingsPageCurl(
|
||||
"Page Curl With Settings",
|
||||
"Showcases how individual interactions can be toggled on / off"
|
||||
),
|
||||
CustomPageCurl(
|
||||
"Page Curl With State Management",
|
||||
"Example how state can be used to change current page (snap / animate)"
|
||||
),
|
||||
AnimatePageCurl(
|
||||
"Page Curl With Custom Animation",
|
||||
"Example how custom animation can be provided for forward / backward taps"
|
||||
),
|
||||
}
|
46
demo/src/main/kotlin/eu/wewox/pagecurl/HowToPageData.kt
Normal file
46
demo/src/main/kotlin/eu/wewox/pagecurl/HowToPageData.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
data class HowToPageData(
|
||||
val title: String,
|
||||
val message: String,
|
||||
) {
|
||||
companion object {
|
||||
val simpleHowToPages = listOf(
|
||||
HowToPageData(
|
||||
"Welcome \uD83D\uDC4B",
|
||||
"This is a simple demo of the PageCurl. Swipe to the left to turn the page.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Forward & backward",
|
||||
"Nice, now try another direction to go backward.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Taps",
|
||||
"You may also just tap in the right half of the screen to go forward and tap on the left one to go backward.",
|
||||
),
|
||||
HowToPageData(
|
||||
"End",
|
||||
"That is the last page, you cannot go further \uD83D\uDE09",
|
||||
)
|
||||
)
|
||||
|
||||
val interactionHowToPages = listOf(
|
||||
HowToPageData(
|
||||
"Interaction example",
|
||||
"This example demonstrates how drag & tap gestures can be toggled on or off. By default all gestures are allowed.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Custom tap",
|
||||
"This example has a custom tap configured to show a settings popup. Try it and tap somewhere near the center of the page.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Settings",
|
||||
"Try to disable forward / backward drags and / or forward / backward taps.",
|
||||
),
|
||||
HowToPageData(
|
||||
"End",
|
||||
"That is the last page, you cannot go further \uD83D\uDE09",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@@ -1,133 +1,65 @@
|
||||
@file:OptIn(ExperimentalPageCurlApi::class)
|
||||
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.center
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import eu.wewox.pagecurl.components.SettingsAction
|
||||
import eu.wewox.pagecurl.components.SettingsPopup
|
||||
import eu.wewox.pagecurl.config.CurlConfig
|
||||
import eu.wewox.pagecurl.config.InteractionConfig
|
||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||
import eu.wewox.pagecurl.config.ShadowConfig
|
||||
import eu.wewox.pagecurl.config.copy
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.PageCurlState
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
import androidx.core.view.WindowCompat
|
||||
import eu.wewox.pagecurl.components.TopBar
|
||||
import eu.wewox.pagecurl.screens.AnimatePageCurlScreen
|
||||
import eu.wewox.pagecurl.screens.SettingsPageCurlScreen
|
||||
import eu.wewox.pagecurl.screens.SimplePageCurlScreen
|
||||
import eu.wewox.pagecurl.screens.StatePageCurlScreen
|
||||
import eu.wewox.pagecurl.ui.SpacingMedium
|
||||
import eu.wewox.pagecurl.ui.theme.PageCurlTheme
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Main activity for demo application.
|
||||
* Contains simple "Crossfade" based navigation to various examples.
|
||||
*/
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
setContent {
|
||||
PageCurlTheme {
|
||||
Box {
|
||||
val state = rememberPageCurlState(max = 6)
|
||||
var showPopup by remember { mutableStateOf(false) }
|
||||
var interaction by remember {
|
||||
mutableStateOf(
|
||||
InteractionConfig(
|
||||
tap = InteractionConfig.Tap(
|
||||
custom = InteractionConfig.Tap.CustomInteraction(true) { size, position ->
|
||||
if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||
showPopup = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
var example by rememberSaveable { mutableStateOf<Example?>(null) }
|
||||
|
||||
val curlConfig by derivedStateOf {
|
||||
val offset = if (state.progress > 0f) 0f else 1f
|
||||
val shadowAlpha = (offset + state.progress) * 0.5f
|
||||
BackHandler(enabled = example != null) {
|
||||
example = null
|
||||
}
|
||||
|
||||
CurlConfig(
|
||||
shadow = ShadowConfig(
|
||||
alpha = shadowAlpha
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
PageCurl(
|
||||
state = state,
|
||||
config = PageCurlConfig(
|
||||
curl = curlConfig,
|
||||
interaction = interaction
|
||||
)
|
||||
) { index ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.White)
|
||||
) {
|
||||
when (index) {
|
||||
0 -> {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_sleep),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Text(
|
||||
text = if (index % 2 == 1) Data.Lorem1 else Data.Lorem2,
|
||||
fontSize = 22.sp,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = index.toString(),
|
||||
color = Color.White,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.background(Color.Black, RoundedCornerShape(topStartPercent = 100))
|
||||
.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (showPopup) {
|
||||
SettingsPopup(
|
||||
state = state,
|
||||
interaction = interaction,
|
||||
onConfigChange = {
|
||||
interaction = it
|
||||
},
|
||||
onDismiss = {
|
||||
showPopup = false
|
||||
}
|
||||
)
|
||||
Crossfade(targetState = example, Modifier.safeDrawingPadding()) { selected ->
|
||||
when (selected) {
|
||||
null -> RootScreen(onExampleClick = { example = it })
|
||||
Example.SimplePageCurl -> SimplePageCurlScreen()
|
||||
Example.SettingsPageCurl -> SettingsPageCurlScreen()
|
||||
Example.CustomPageCurl -> StatePageCurlScreen()
|
||||
Example.AnimatePageCurl -> AnimatePageCurlScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,48 +68,35 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsPopup(
|
||||
state: PageCurlState,
|
||||
interaction: InteractionConfig,
|
||||
onConfigChange: (InteractionConfig) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
SettingsPopup(
|
||||
interactionConfig = interaction,
|
||||
onAction = { action ->
|
||||
when (action) {
|
||||
SettingsAction.GoToFirst -> {
|
||||
scope.launch {
|
||||
state.snapTo(0)
|
||||
private fun RootScreen(onExampleClick: (Example) -> Unit) {
|
||||
Scaffold(
|
||||
topBar = { TopBar("Page Curl Demo") }
|
||||
) { padding ->
|
||||
LazyColumn(Modifier.padding(padding)) {
|
||||
items(Example.values()) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onExampleClick(it) }
|
||||
.padding(SpacingMedium)
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = it.label,
|
||||
style = MaterialTheme.typography.h6
|
||||
)
|
||||
Text(
|
||||
text = it.description,
|
||||
style = MaterialTheme.typography.body2
|
||||
)
|
||||
}
|
||||
}
|
||||
SettingsAction.GoToLast -> {
|
||||
scope.launch {
|
||||
state.snapTo(state.max - 1)
|
||||
}
|
||||
}
|
||||
is SettingsAction.ForwardDragEnabled -> {
|
||||
onConfigChange(interaction.copy(dragForwardEnabled = action.value))
|
||||
}
|
||||
is SettingsAction.BackwardDragEnabled -> {
|
||||
onConfigChange(interaction.copy(dragBackwardEnabled = action.value))
|
||||
}
|
||||
is SettingsAction.ForwardTapEnabled -> {
|
||||
onConfigChange(interaction.copy(tapForwardEnabled = action.value))
|
||||
}
|
||||
is SettingsAction.BackwardTapEnabled -> {
|
||||
onConfigChange(interaction.copy(tapBackwardEnabled = action.value))
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismiss = onDismiss
|
||||
)
|
||||
}
|
||||
|
||||
private object Data {
|
||||
val Lorem1 =
|
||||
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam erat volutpat. Phasellus enim erat, vestibulum vel, aliquam a, posuere eu, velit. Et harum quidem rerum facilis est et expedita distinctio. In sem justo, commodo ut, suscipit at, pharetra vitae, orci. Vestibulum fermentum tortor id mi. Sed elit dui, pellentesque a, faucibus vel, interdum nec, diam. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. Nunc tincidunt ante vitae massa. Maecenas fermentum, sem in pharetra pellentesque, velit turpis volutpat ante, in pharetra metus odio a lectus. Proin in tellus sit amet nibh dignissim sagittis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Etiam posuere lacus quis dolor. Class aptent taciti sociosqu ad litora."
|
||||
val Lorem2 =
|
||||
"Phasellus enim erat, vestibulum vel, aliquam a, posuere eu, velit. Duis ante orci, molestie vitae vehicula venenatis, tincidunt ac pede. Aliquam in lorem sit amet leo accumsan lacinia. Morbi imperdiet, mauris ac auctor dictum, nisl ligula egestas nulla, et sollicitudin sem purus in lacus. Ut tempus purus at lorem. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Integer malesuada. Sed vel lectus. Donec odio tempus molestie, porttitor ut, iaculis quis, sem. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Etiam egestas wisi a erat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Maecenas lorem. Mauris dolor felis, sagittis at, luctus sed, aliquam non, tellus. Aenean id metus id velit ullamcorper pulvinar. Integer malesuada."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,61 @@
|
||||
package eu.wewox.pagecurl.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.ui.SpacingLarge
|
||||
import eu.wewox.pagecurl.ui.SpacingMedium
|
||||
|
||||
@Composable
|
||||
fun HowToPage(
|
||||
index: Int,
|
||||
page: HowToPageData,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(SpacingMedium, Alignment.CenterVertically),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(SpacingLarge)
|
||||
) {
|
||||
Text(
|
||||
text = page.title,
|
||||
style = MaterialTheme.typography.h4,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Text(
|
||||
text = page.message,
|
||||
style = MaterialTheme.typography.body1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = index.toString(),
|
||||
color = MaterialTheme.colors.background,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.background(MaterialTheme.colors.onBackground, RoundedCornerShape(topStartPercent = 100))
|
||||
.padding(SpacingMedium)
|
||||
)
|
||||
}
|
||||
}
|
@@ -13,7 +13,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Switch
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -22,20 +21,12 @@ import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||
import eu.wewox.pagecurl.config.InteractionConfig
|
||||
|
||||
internal sealed interface SettingsAction {
|
||||
object GoToFirst : SettingsAction
|
||||
object GoToLast : SettingsAction
|
||||
class ForwardDragEnabled(val value: Boolean) : SettingsAction
|
||||
class BackwardDragEnabled(val value: Boolean) : SettingsAction
|
||||
class ForwardTapEnabled(val value: Boolean) : SettingsAction
|
||||
class BackwardTapEnabled(val value: Boolean) : SettingsAction
|
||||
}
|
||||
import eu.wewox.pagecurl.config.copy
|
||||
|
||||
@Composable
|
||||
internal fun SettingsPopup(
|
||||
interactionConfig: InteractionConfig,
|
||||
onAction: (SettingsAction) -> Unit,
|
||||
interaction: InteractionConfig,
|
||||
onConfigChange: (InteractionConfig) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
Popup(
|
||||
@@ -53,38 +44,31 @@ internal fun SettingsPopup(
|
||||
.width(IntrinsicSize.Max)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
TextButton(onClick = { onAction(SettingsAction.GoToFirst) }) {
|
||||
Text("Go to first")
|
||||
}
|
||||
TextButton(onClick = { onAction(SettingsAction.GoToLast) }) {
|
||||
Text("Go to last")
|
||||
}
|
||||
|
||||
val switchRowModifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp)
|
||||
SwitchRow(
|
||||
text = "Forward drag enabled",
|
||||
enabled = interactionConfig.drag.forward.enabled,
|
||||
onChanged = { onAction(SettingsAction.ForwardDragEnabled(it)) },
|
||||
enabled = interaction.drag.forward.enabled,
|
||||
onChanged = { onConfigChange(interaction.copy(dragForwardEnabled = it)) },
|
||||
modifier = switchRowModifier
|
||||
)
|
||||
SwitchRow(
|
||||
text = "Backward drag enabled",
|
||||
enabled = interactionConfig.drag.backward.enabled,
|
||||
onChanged = { onAction(SettingsAction.BackwardDragEnabled(it)) },
|
||||
enabled = interaction.drag.backward.enabled,
|
||||
onChanged = { onConfigChange(interaction.copy(dragBackwardEnabled = it)) },
|
||||
modifier = switchRowModifier
|
||||
)
|
||||
SwitchRow(
|
||||
text = "Forward tap enabled",
|
||||
enabled = interactionConfig.tap.forward.enabled,
|
||||
onChanged = { onAction(SettingsAction.ForwardTapEnabled(it)) },
|
||||
enabled = interaction.tap.forward.enabled,
|
||||
onChanged = { onConfigChange(interaction.copy(tapForwardEnabled = it)) },
|
||||
modifier = switchRowModifier
|
||||
)
|
||||
SwitchRow(
|
||||
text = "Backward tap enabled",
|
||||
enabled = interactionConfig.tap.backward.enabled,
|
||||
onChanged = { onAction(SettingsAction.BackwardTapEnabled(it)) },
|
||||
enabled = interaction.tap.backward.enabled,
|
||||
onChanged = { onConfigChange(interaction.copy(tapBackwardEnabled = it)) },
|
||||
modifier = switchRowModifier
|
||||
)
|
||||
}
|
||||
|
23
demo/src/main/kotlin/eu/wewox/pagecurl/components/TopBar.kt
Normal file
23
demo/src/main/kotlin/eu/wewox/pagecurl/components/TopBar.kt
Normal file
@@ -0,0 +1,23 @@
|
||||
package eu.wewox.pagecurl.components
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.wewox.pagecurl.ui.SpacingMedium
|
||||
|
||||
/**
|
||||
* The reusable component for top bar.
|
||||
*
|
||||
* @param title The text to show in top bar.
|
||||
*/
|
||||
@Composable
|
||||
fun TopBar(title: String) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.h4,
|
||||
modifier = Modifier
|
||||
.padding(SpacingMedium)
|
||||
)
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package eu.wewox.pagecurl.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandIn
|
||||
import androidx.compose.animation.shrinkOut
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
|
||||
@Composable
|
||||
fun ZoomOutLayout(
|
||||
zoomOut: Boolean,
|
||||
bottom: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Layout(
|
||||
modifier = modifier,
|
||||
content = {
|
||||
content()
|
||||
Box {
|
||||
AnimatedVisibility(
|
||||
visible = zoomOut,
|
||||
enter = expandIn(expandFrom = Alignment.Center, initialSize = { IntSize(it.width, 0) }),
|
||||
exit = shrinkOut(shrinkTowards = Alignment.Center, targetSize = { IntSize(it.width, 0) })
|
||||
) {
|
||||
bottom()
|
||||
}
|
||||
}
|
||||
},
|
||||
measurePolicy = { measurables, constraints ->
|
||||
val (contentMeasurable, bottomMeasurable) = measurables
|
||||
|
||||
val bottomPlaceable = bottomMeasurable.measure(constraints)
|
||||
val contentPlaceable = contentMeasurable.measure(constraints)
|
||||
|
||||
layout(constraints.maxWidth, constraints.maxHeight) {
|
||||
bottomPlaceable.place(x = 0, y = constraints.maxHeight - bottomPlaceable.height)
|
||||
|
||||
contentPlaceable.placeWithLayer(0, 0) {
|
||||
val height = constraints.maxHeight - 2 * bottomPlaceable.height
|
||||
val scale = height / contentPlaceable.height.toFloat()
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
fun AnimatePageCurlScreen() {
|
||||
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
@file:OptIn(ExperimentalPageCurlApi::class)
|
||||
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.center
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.components.HowToPage
|
||||
import eu.wewox.pagecurl.components.SettingsPopup
|
||||
import eu.wewox.pagecurl.config.InteractionConfig
|
||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
|
||||
@Composable
|
||||
fun SettingsPageCurlScreen() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val pages = remember { HowToPageData.interactionHowToPages }
|
||||
val state = rememberPageCurlState(max = pages.size)
|
||||
|
||||
var showPopup by remember { mutableStateOf(false) }
|
||||
|
||||
// Create a mutable interaction config with custom tap interaction
|
||||
// In SettingsPopup config is mutated
|
||||
var interaction by remember {
|
||||
mutableStateOf(
|
||||
InteractionConfig(
|
||||
tap = InteractionConfig.Tap(
|
||||
custom = InteractionConfig.Tap.CustomInteraction(true) { size, position ->
|
||||
// Detect tap somewhere in the center with 64 radius and show popup
|
||||
if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||
showPopup = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
PageCurl(
|
||||
state = state,
|
||||
config = PageCurlConfig(
|
||||
interaction = interaction
|
||||
)
|
||||
) { index ->
|
||||
HowToPage(index, pages[index])
|
||||
}
|
||||
|
||||
if (showPopup) {
|
||||
SettingsPopup(
|
||||
interaction = interaction,
|
||||
onConfigChange = {
|
||||
interaction = it
|
||||
},
|
||||
onDismiss = {
|
||||
showPopup = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
@file:OptIn(ExperimentalPageCurlApi::class)
|
||||
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.components.HowToPage
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
|
||||
@Composable
|
||||
fun SimplePageCurlScreen() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val pages = remember { HowToPageData.simpleHowToPages }
|
||||
val state = rememberPageCurlState(max = pages.size)
|
||||
|
||||
PageCurl(state = state) { index ->
|
||||
HowToPage(index, pages[index])
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
@file:OptIn(ExperimentalPageCurlApi::class)
|
||||
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.center
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.components.HowToPage
|
||||
import eu.wewox.pagecurl.components.ZoomOutLayout
|
||||
import eu.wewox.pagecurl.config.InteractionConfig
|
||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||
import eu.wewox.pagecurl.config.copy
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.PageCurlState
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
import eu.wewox.pagecurl.ui.SpacingLarge
|
||||
import eu.wewox.pagecurl.ui.SpacingMedium
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun StatePageCurlScreen() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val pages = remember { HowToPageData.interactionHowToPages }
|
||||
val state = rememberPageCurlState(max = pages.size)
|
||||
|
||||
var zoomOut by remember { mutableStateOf(false) }
|
||||
|
||||
val interactionConfig = remember {
|
||||
InteractionConfig(
|
||||
tap = InteractionConfig.Tap(
|
||||
custom = InteractionConfig.Tap.CustomInteraction(true) { size, position ->
|
||||
// When PageCurl is zoomed out then zoom back in
|
||||
// Else detect tap somewhere in the center with 64 radius and zoom out a PageCurl
|
||||
if (zoomOut) {
|
||||
zoomOut = false
|
||||
true
|
||||
} else if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||
zoomOut = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ZoomOutLayout(
|
||||
zoomOut = zoomOut,
|
||||
bottom = { SettingsRow(state) }
|
||||
) {
|
||||
// Animate radius and elevation with the same value, because we not :)
|
||||
val cornersAndElevation by animateDpAsState(if (zoomOut) 16.dp else 0.dp)
|
||||
|
||||
Card(
|
||||
shape = RoundedCornerShape(cornersAndElevation),
|
||||
elevation = cornersAndElevation,
|
||||
) {
|
||||
PageCurl(
|
||||
state = state,
|
||||
config = PageCurlConfig(
|
||||
interaction = interactionConfig.copy(
|
||||
dragForwardEnabled = !zoomOut,
|
||||
dragBackwardEnabled = !zoomOut,
|
||||
tapForwardEnabled = !zoomOut,
|
||||
tapBackwardEnabled = !zoomOut,
|
||||
)
|
||||
)
|
||||
) { index ->
|
||||
HowToPage(index, pages[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsRow(
|
||||
state: PageCurlState,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(SpacingMedium, Alignment.CenterHorizontally),
|
||||
modifier = modifier
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.fillMaxWidth()
|
||||
.padding(SpacingLarge)
|
||||
) {
|
||||
SettingsRowButton("Snap to first") {
|
||||
state.snapTo(0)
|
||||
}
|
||||
|
||||
SettingsRowButton("Snap to last") {
|
||||
state.snapTo(state.max)
|
||||
}
|
||||
|
||||
SettingsRowButton("Snap forward") {
|
||||
state.snapTo(state.current + 1)
|
||||
}
|
||||
|
||||
SettingsRowButton("Snap backward") {
|
||||
state.snapTo(state.current - 1)
|
||||
}
|
||||
|
||||
SettingsRowButton("Animate forward") {
|
||||
state.next()
|
||||
}
|
||||
|
||||
SettingsRowButton("Animate backward") {
|
||||
state.prev()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsRowButton(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: suspend () -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
Button(
|
||||
onClick = { scope.launch { onClick() } },
|
||||
modifier = modifier
|
||||
) {
|
||||
Text(text = text)
|
||||
}
|
||||
}
|
18
demo/src/main/kotlin/eu/wewox/pagecurl/ui/Spacing.kt
Normal file
18
demo/src/main/kotlin/eu/wewox/pagecurl/ui/Spacing.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package eu.wewox.pagecurl.ui
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* The small spacing.
|
||||
*/
|
||||
val SpacingSmall = 8.dp
|
||||
|
||||
/**
|
||||
* The medium spacing.
|
||||
*/
|
||||
val SpacingMedium = 16.dp
|
||||
|
||||
/**
|
||||
* The large spacing.
|
||||
*/
|
||||
val SpacingLarge = 32.dp
|
@@ -11,7 +11,7 @@ import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||
|
||||
/**
|
||||
* Shows the pages which may be rotated by drag or tap gestures.
|
||||
* Shows the pages which may be turned by drag or tap gestures.
|
||||
*
|
||||
* @param state The state of the PageCurl. Use this to programmatically change the current page or observe changes.
|
||||
* @param modifier The modifier for this composable.
|
||||
|
@@ -71,7 +71,7 @@ public class PageCurlState(
|
||||
internal set
|
||||
|
||||
/**
|
||||
* The observable progress as page is rotated.
|
||||
* The observable progress as page is turned.
|
||||
* When going forward it changes from 0 to 1, when going backward it is going from 0 to -1.
|
||||
*/
|
||||
public val progress: Float get() = internalState?.progress ?: 0f
|
||||
|
Reference in New Issue
Block a user