diff --git a/spine-android/app/src/main/java/com/esotericsoftware/spine/IKFollowing.kt b/spine-android/app/src/main/java/com/esotericsoftware/spine/IKFollowing.kt index b1810b6d9..0b09b2631 100644 --- a/spine-android/app/src/main/java/com/esotericsoftware/spine/IKFollowing.kt +++ b/spine-android/app/src/main/java/com/esotericsoftware/spine/IKFollowing.kt @@ -25,6 +25,7 @@ import androidx.navigation.NavHostController import com.badlogic.gdx.math.Vector2 import com.esotericsoftware.spine.android.SpineController import com.esotericsoftware.spine.android.SpineView +import com.esotericsoftware.spine.android.bounds.Alignment @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -100,7 +101,9 @@ fun IKFollowing(nav: NavHostController) { "spineboy-pro.json", context, controller - ) + ).apply { + alignment = Alignment.CENTER_LEFT + } } ) } diff --git a/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SpineView.java b/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SpineView.java index db159f223..77fb0c6ca 100644 --- a/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SpineView.java +++ b/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SpineView.java @@ -33,6 +33,7 @@ import com.badlogic.gdx.utils.Array; import com.esotericsoftware.spine.android.bounds.Alignment; import com.esotericsoftware.spine.android.bounds.Bounds; import com.esotericsoftware.spine.android.bounds.BoundsProvider; +import com.esotericsoftware.spine.android.bounds.ContentMode; import com.esotericsoftware.spine.android.bounds.SetupPoseBounds; import com.esotericsoftware.spine.android.utils.AndroidSkeletonDrawableLoader; @@ -58,8 +59,13 @@ public class SpineView extends View implements Choreographer.FrameCallback { private String skeletonFileName; private File atlasFile; private File skeletonFile; + private URL atlasUrl; + private URL skeletonUrl; + private File targetDirectory; + private AndroidSkeletonDrawable drawable; private BoundsProvider boundsProvider = new SetupPoseBounds(); private Alignment alignment = Alignment.CENTER; + private ContentMode contentMode = ContentMode.FIT; public Builder(Context context, SpineController controller) { this.context = context; @@ -78,11 +84,28 @@ public class SpineView extends View implements Choreographer.FrameCallback { return this; } + public Builder setLoadFromHttp(URL atlasUrl, URL skeletonUrl, File targetDirectory) { + this.atlasUrl = atlasUrl; + this.skeletonUrl = skeletonUrl; + this.targetDirectory = targetDirectory; + return this; + } + + public Builder setLoadFromDrawable(AndroidSkeletonDrawable drawable) { + this.drawable = drawable; + return this; + } + public Builder setBoundsProvider(BoundsProvider boundsProvider) { this.boundsProvider = boundsProvider; return this; } + public Builder setContentMode(ContentMode contentMode) { + this.contentMode = contentMode; + return this; + } + public Builder setAlignment(Alignment alignment) { this.alignment = alignment; return this; @@ -92,10 +115,15 @@ public class SpineView extends View implements Choreographer.FrameCallback { SpineView spineView = new SpineView(context, controller); spineView.boundsProvider = boundsProvider; spineView.alignment = alignment; + spineView.contentMode = contentMode; if (atlasFileName != null && skeletonFileName != null) { spineView.loadFromAsset(atlasFileName, skeletonFileName); } else if (atlasFile != null && skeletonFile != null) { spineView.loadFromFile(atlasFile, skeletonFile); + } else if (atlasUrl != null && skeletonUrl != null && targetDirectory != null) { + spineView.loadFromHttp(atlasUrl, skeletonUrl, targetDirectory); + } else if (drawable != null) { + spineView.loadFromDrawable(drawable); } return spineView; } @@ -109,15 +137,13 @@ public class SpineView extends View implements Choreographer.FrameCallback { private float scaleY = 1; private float x = 0; private float y = 0; - private final SkeletonRenderer renderer = new SkeletonRenderer(); private Bounds computedBounds = new Bounds(); - SpineController controller; - - BoundsProvider boundsProvider = new SetupPoseBounds(); - - Alignment alignment = Alignment.CENTER; + private SpineController controller; + private BoundsProvider boundsProvider = new SetupPoseBounds(); + private Alignment alignment = Alignment.CENTER; + private ContentMode contentMode = ContentMode.FIT; public SpineView (Context context, SpineController controller) { super(context); @@ -158,10 +184,6 @@ public class SpineView extends View implements Choreographer.FrameCallback { return spineView; } - public void setController(SpineController controller) { - this.controller = controller; - } - public void loadFromAsset(String atlasFileName, String skeletonFileName) { loadFrom(() -> AndroidSkeletonDrawable.fromAsset(atlasFileName, skeletonFileName, getContext())); } @@ -178,6 +200,41 @@ public class SpineView extends View implements Choreographer.FrameCallback { loadFrom(() -> drawable); } + public SpineController getController() { + return controller; + } + + public void setController(SpineController controller) { + this.controller = controller; + } + + public Alignment getAlignment() { + return alignment; + } + + public void setAlignment(Alignment alignment) { + this.alignment = alignment; + updateCanvasTransform(); + } + + public ContentMode getContentMode() { + return contentMode; + } + + public void setContentMode(ContentMode contentMode) { + this.contentMode = contentMode; + updateCanvasTransform(); + } + + public BoundsProvider getBoundsProvider() { + return boundsProvider; + } + + public void setBoundsProvider(BoundsProvider boundsProvider) { + this.boundsProvider = boundsProvider; + updateCanvasTransform(); + } + private void loadFrom(AndroidSkeletonDrawableLoader loader) { Handler mainHandler = new Handler(Looper.getMainLooper()); Thread backgroundThread = new Thread(() -> { @@ -230,9 +287,14 @@ public class SpineView extends View implements Choreographer.FrameCallback { x = (float) (-computedBounds.getX() - computedBounds.getWidth() / 2.0 - (alignment.getX() * computedBounds.getWidth() / 2.0)); y = (float) (-computedBounds.getY() - computedBounds.getHeight() / 2.0 - (alignment.getY() * computedBounds.getHeight() / 2.0)); - // contain - scaleX = scaleY = (float) Math.min(getWidth() / computedBounds.getWidth(), getHeight() / computedBounds.getHeight()); - + switch (contentMode) { + case FIT: + scaleX = scaleY = (float) Math.min(getWidth() / computedBounds.getWidth(), getHeight() / computedBounds.getHeight()); + break; + case FILL: + scaleX = scaleY = (float) Math.max(getWidth() / computedBounds.getWidth(), getHeight() / computedBounds.getHeight()); + break; + } offsetX = (float) (getWidth() / 2.0 + (alignment.getX() * getWidth() / 2.0)); offsetY = (float) (getHeight() / 2.0 + (alignment.getY() * getHeight() / 2.0)); diff --git a/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/ContentMode.java b/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/ContentMode.java new file mode 100644 index 000000000..f0ccbf269 --- /dev/null +++ b/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/ContentMode.java @@ -0,0 +1,6 @@ +package com.esotericsoftware.spine.android.bounds; + +public enum ContentMode { + FIT, + FILL; +} diff --git a/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/utils/SpineHttpUtils.java b/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/utils/SpineHttpUtils.java index 0a3eaf99a..ca9c0ffc1 100644 --- a/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/utils/SpineHttpUtils.java +++ b/spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/utils/SpineHttpUtils.java @@ -2,8 +2,6 @@ package com.esotericsoftware.spine.android.utils; import android.os.Build; -import com.esotericsoftware.spine.android.AndroidTextureAtlas; - import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream;