mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 01:36:02 +08:00
Add sample navigation and Play/Pause sample
This commit is contained in:
parent
5cae4737b5
commit
092d97ec13
@ -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)
|
||||||
|
|||||||
BIN
spine-android/app/src/main/assets/dragon-ess.skel
Normal file
BIN
spine-android/app/src/main/assets/dragon-ess.skel
Normal file
Binary file not shown.
112
spine-android/app/src/main/assets/dragon.atlas
Normal file
112
spine-android/app/src/main/assets/dragon.atlas
Normal 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
|
||||||
BIN
spine-android/app/src/main/assets/dragon.png
Normal file
BIN
spine-android/app/src/main/assets/dragon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 292 KiB |
BIN
spine-android/app/src/main/assets/dragon_2.png
Normal file
BIN
spine-android/app/src/main/assets/dragon_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 193 KiB |
BIN
spine-android/app/src/main/assets/dragon_3.png
Normal file
BIN
spine-android/app/src/main/assets/dragon_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
BIN
spine-android/app/src/main/assets/dragon_4.png
Normal file
BIN
spine-android/app/src/main/assets/dragon_4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
spine-android/app/src/main/assets/dragon_5.png
Normal file
BIN
spine-android/app/src/main/assets/dragon_5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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" }
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user