Add sample navigation and Play/Pause sample

This commit is contained in:
Denis Andrasec 2024-07-03 18:50:01 +02:00
parent 5cae4737b5
commit 092d97ec13
13 changed files with 323 additions and 21 deletions

View File

@ -59,6 +59,7 @@ dependencies {
implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3) implementation(libs.androidx.material3)
implementation(libs.androidx.navigation.compose)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)

Binary file not shown.

View File

@ -0,0 +1,112 @@
dragon.png
size: 1024, 1024
filter: Linear, Linear
front-toe-a
bounds: 797, 381, 29, 50
front-toe-b
bounds: 942, 118, 56, 57
head
bounds: 647, 81, 296, 260
rotate: 90
left-front-leg
bounds: 942, 250, 84, 57
rotate: 90
left-front-thigh
bounds: 852, 7, 84, 72
left-wing01
bounds: 736, 433, 264, 589
right-rear-toe
bounds: 647, 2, 109, 77
right-wing01
bounds: 2, 379, 365, 643
right-wing02
bounds: 369, 379, 365, 643
right-wing03
bounds: 2, 12, 365, 643
rotate: 90
tail03
bounds: 758, 6, 73, 92
rotate: 90
tail04
bounds: 942, 177, 56, 71
tail05
bounds: 736, 379, 52, 59
rotate: 90
tail06
bounds: 942, 336, 95, 68
rotate: 90
thiagobrayner
bounds: 909, 81, 350, 31
rotate: 90
dragon_2.png
size: 1024, 1024
filter: Linear, Linear
back
bounds: 795, 32, 190, 185
chin
bounds: 647, 157, 214, 146
rotate: 90
left-rear-leg
bounds: 795, 219, 206, 177
rotate: 90
left-wing02
bounds: 736, 427, 264, 589
right-wing04
bounds: 2, 373, 365, 643
right-wing05
bounds: 369, 373, 365, 643
right-wing06
bounds: 2, 6, 365, 643
rotate: 90
tail01
bounds: 647, 2, 120, 153
dragon_3.png
size: 1024, 1024
filter: Linear, Linear
chest
bounds: 740, 299, 136, 122
left-rear-thigh
bounds: 647, 218, 91, 149
left-wing03
bounds: 736, 423, 264, 589
right-front-leg
bounds: 850, 196, 101, 89
rotate: 90
right-front-thigh
bounds: 740, 189, 108, 108
right-rear-leg
bounds: 878, 321, 116, 100
right-rear-thigh
bounds: 647, 67, 91, 149
right-wing07
bounds: 2, 369, 365, 643
right-wing08
bounds: 369, 369, 365, 643
right-wing09
bounds: 2, 2, 365, 643
rotate: 90
tail02
bounds: 740, 67, 95, 120
dragon_4.png
size: 1024, 1024
filter: Linear, Linear
left-wing04
bounds: 2, 268, 264, 589
left-wing05
bounds: 268, 268, 264, 589
left-wing06
bounds: 534, 268, 264, 589
left-wing07
bounds: 2, 2, 264, 589
rotate: 90
dragon_5.png
size: 1024, 1024
filter: Linear, Linear
left-wing08
bounds: 2, 2, 264, 589
left-wing09
bounds: 268, 2, 264, 589

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -3,15 +3,27 @@ package com.esotericsoftware.spine
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.unit.dp
import com.esotericsoftware.spine.android.SpineController import androidx.navigation.NavHostController
import com.esotericsoftware.spine.android.SpineView import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.esotericsoftware.spine.ui.theme.SpineAndroidExamplesTheme import com.esotericsoftware.spine.ui.theme.SpineAndroidExamplesTheme
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@ -23,34 +35,82 @@ class MainActivity : ComponentActivity() {
} }
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun AppContent() { fun AppContent() {
val navController = rememberNavController()
SpineAndroidExamplesTheme { SpineAndroidExamplesTheme {
Surface( Surface(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background color = MaterialTheme.colorScheme.background
) { ) {
Box { NavHost(
SpineViewComposable() navController = navController,
startDestination = Destination.Samples.route
) {
composable(
Destination.Samples.route
) {
Scaffold(
topBar = { TopAppBar(title = { Text(text = Destination.Samples.title) }) }
) { paddingValues ->
Samples(
navController,
listOf(
Destination.SimpleAnimation,
Destination.PlayPause
),
paddingValues
)
}
}
composable(
Destination.SimpleAnimation.route
) {
SimpleAnimation(navController)
}
composable(
Destination.PlayPause.route
) {
PlayPause(navController)
}
} }
} }
} }
} }
@Composable @Composable
fun SpineViewComposable(modifier: Modifier = Modifier.fillMaxSize()) { fun Samples(
AndroidView( nav: NavHostController,
factory = { ctx -> samples: List<Destination>,
SpineView(ctx).apply { paddingValues: PaddingValues
loadFromAsset( ) {
"spineboy.atlas", LazyColumn(
"spineboy-pro.json", verticalArrangement = Arrangement.spacedBy(8.dp),
SpineController { modifier = Modifier
it.animationState.setAnimation(0, "walk", true) .padding(8.dp)
} .padding(paddingValues)
) ) {
samples.forEach {
item {
Card(
Modifier
.fillMaxWidth()
.clickable(onClick = { nav.navigate(it.route) }),
shape = MaterialTheme.shapes.large
) {
Text(text = it.title, Modifier.padding(24.dp))
}
} }
}, }
modifier = modifier }
) }
sealed class Destination(val route: String, val title: String) {
data object Samples: Destination("samples", "Spine Android Examples")
data object SimpleAnimation : Destination("simpleAnimation", "Simple Animation")
data object PlayPause : Destination("playPause", "Play/Pause")
} }

View File

@ -0,0 +1,73 @@
package com.esotericsoftware.spine
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.navigation.NavHostController
import com.esotericsoftware.spine.android.SpineController
import com.esotericsoftware.spine.android.SpineView
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PlayPause(
nav: NavHostController
) {
val controller = remember {
SpineController {
it.animationState.setAnimation(0, "flying", true)
}
}
val isPlaying = remember { mutableStateOf(controller.isPlaying) }
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = Destination.PlayPause.title) },
navigationIcon = {
IconButton({ nav.navigateUp() }) {
Icon(
Icons.Rounded.ArrowBack,
null,
)
}
},
actions = {
Button(onClick = {
if (controller.isPlaying) controller.pause() else controller.resume()
isPlaying.value = controller.isPlaying
}) {
Text(text = if (isPlaying.value) "Pause" else "Play")
}
}
)
}
) { paddingValues ->
AndroidView(
factory = { ctx ->
SpineView(ctx).apply {
loadFromAsset(
"dragon.atlas",
"dragon-ess.skel",
controller
)
}
},
modifier = Modifier.padding(paddingValues)
)
}
}

View File

@ -0,0 +1,52 @@
package com.esotericsoftware.spine
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.navigation.NavHostController
import com.esotericsoftware.spine.android.SpineController
import com.esotericsoftware.spine.android.SpineView
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SimpleAnimation(nav: NavHostController) {
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = Destination.SimpleAnimation.title) },
navigationIcon = {
IconButton({ nav.navigateUp() }) {
Icon(
Icons.Rounded.ArrowBack,
null,
)
}
}
)
}
) { paddingValues ->
AndroidView(
factory = { ctx ->
SpineView(ctx).apply {
loadFromAsset(
"spineboy.atlas",
"spineboy-pro.json",
SpineController {
it.animationState.setAnimation(0, "walk", true)
}
)
}
},
modifier = Modifier.padding(paddingValues)
)
}
}

View File

@ -10,6 +10,7 @@ activityCompose = "1.7.0"
composeBom = "2023.08.00" composeBom = "2023.08.00"
appcompat = "1.6.1" appcompat = "1.6.1"
material = "1.10.0" material = "1.10.0"
navigationCompose = "2.7.7"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -28,6 +29,7 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit
androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" } material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
[plugins] [plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" } androidApplication = { id = "com.android.application", version.ref = "agp" }

View File

@ -116,7 +116,9 @@ public class SpineView extends View implements Choreographer.FrameCallback {
return; return;
} }
controller.getDrawable().update(delta); if (controller.isPlaying()) {
controller.getDrawable().update(delta);
}
canvas.save(); canvas.save();
canvas.translate(offsetX, offsetY); canvas.translate(offsetX, offsetY);