B4A Library [Library] Universal Tween Engine - Animate your views

tween-engine-big-logo.jpg

Picture copyright Aurelien Ribon​

The Universal Tween Engine allows you to create smooth interpolations on every attribute from every object in your project !

I would like to thank the author of the official version, Aurelien Ribon, a lot for helping me porting the project.

You can take a look at an example here:
Universal Tween Engine demo

or Android Application:
https://play.google.com/store/apps/details?id=aurelienribon.tweenengine.demo

And amazing sample made with b4a?
Check out post 6 by Sorex!

I ported almost all functions from this engine to Basic4Android.
So you can make simple tweens with callbacks to powerful animation sequences.

Examples to make with this library:
- Games: Imagine Jewelquest where the sprites have to move from the game (with an elastic animation, bounce, etc)
- Livewallpaper: imagine what you can do ;)
- Menu's
- Intro's
- And a lot more!

I will try to explain this library as good as possible.
There are 6 classes available to use in the TweenEngine:

B4X:
Dim Tween As Tween
Dim Manager As TweenManager
Dim Constants As TweenConstants
Dim Callback As TweenCallback
Dim Path1 As TweenPath
Dim TimeLine As TweenTimeline

A Tween is an animation of an object, view, ...
Currently you can animate a Label, ImageView and Panel. (Due B4A uses wrappers for the views, we needed to create seperate methods for every view. We also couldn't cast from an Object to a viewwrapper.) But don't worry! You can still add your view to animate to a panel and tween the panel.

Let's go over a sample of animating a view.

B4X:
'Load Activity stuff
Activity.LoadLayout("Main.bal")
Activity.AddMenuItem("Start Tween", "mnuTween")

'Initialize the TweenManager
Manager.Initialize
   
'Initialize the tween
Tween1.Initialize("Tween")

'A callback is used when you want to monitor a tween like tween completion, etc
Callback.Initialize("TweenCallback")

'We will use a timer to update our animation.
'Normally, we should use a thread to remain maximum fps.
tmrUpdate.Initialize("TweenUpdate", 10)
   

'Next, we register a Tween Accessor if the object you will use is a panel.
'If the tween you will use is an imageview, you'll use registerImageAccessor, etc
Tween1.registerPanelAccessor

'Let's create our tween.
'First we need to call one of the 4 'initializers'. These will construct your tweens otherwise you'll receive a nullpointer.
'These are: - To, From, Set and Call - they are explained in the methods in the IDE.
'Our first parameter is the object/view we will add.
'Second param is a tweenConstants. These are X, Y or XY
'Last parameter is our duration in secs or ms.
'This is the unit you want, the engine doesn't care  
'However, if you choose seconds for instance, you need to use the same unit in every call to the engine. 
'That means that the deltas in the manager.update() method will need to be in seconds too. 
'As long as you use the same unit everywhere, you can use the unit you want.
Tween1.PanelTo(pnlTween, Constants.POSITION_XY, 30)

'Then we set the target's x and y position to go to.
Tween1.setTarget(105,200)

'Next we create an ease from the constants. A dozen are available to use.
Tween1.Ease(Constants.ELASTIC_IN)

'Sets a listener before you set the callback!
Callback.Listener = True

'A callback is used when you can to know what happened with the Tween. OnEvent is called with the TweenCallback eventname you initialized before
Tween1.Callback = Callback.TweenCallback
   
'Adds the tween to the TweenManager.
'Calling TweenManager.start will start all tweens associated with it.
Manager.addTween(Tween1(0).Tween)
Manager.addTween(Tween1(1).Tween)

Sub mnuTween_Click

   'Starts the tween and enables the timer.
   Manager.StartTween
   tmrUpdate.Enabled = True
   
End Sub

Sub TweenUpdate_Tick
   'Updates the tween.
   Manager.Update(0.1)

End Sub

Sub TweenCallback_OnEvent (Event As Int)
   Log("Event: " & Event)

End Sub

A lot of other methods are available to the tweens like resuming, pausing, getting information, repeating, delays, waypoints, etc.

A sample for TweenTimelines isn't available yet, but you can find more information here:
java-universal-tween-engine - The Universal Tween Engine allows you to create smooth interpolations on every attribute from every object in your project ! - Google Project Hosting

TweenConstants are the constants used for the Tweens.
All constants (except 3 Position_x, y and xy) are used for the easing of the tween.

TweenPath isn't really tested.
It has 1 method:

B4X:
/**
         * Computes the next value of the interpolation, based on its waypoints and
         * the current progress.
         *
         * @param t The progress of the interpolation, between 0 and 1. May be out
         * of these bounds if the easing equation involves some kind of rebounds.
         * @param points The waypoints of the tween, from start to target values.
         * @param pointsCnt The number of valid points in the array.
         * @return The next value of the interpolation.
         */

TweenCallback is the callback from your methods. OnEvent is called with the event as an int.
You can use this to start new tweens, or to open an activity when a tween ended.

It would also be great if people could make nice examples to share with other users. The intro of the Android app is available on the universal Tween Engine google code: / - java-universal-tween-engine - The Universal Tween Engine allows you to create smooth interpolations on every attribute from every object in your project ! - Google Project Hosting

As last: the credits.
The Universal Tween Engine is licensed under Apache License 2.0.
This means you'll have to credit the author(Aurelien Ribon) and a link to their website (Aurelien Ribon's Dev Blog)
Users are free to use the lib in any projects, commercial or not. If you distribute a modified version of the library, you need to retain the original attribution.
In every licensed file, any attribution notices in redistributed code must be preserved; and, in every licensed file changed, a notification must be added stating that changes have been made to that file.

Javadocs are available here:

Universal Tween Engine API

If you don't know what a function does, or arguments, visit that link!
The javadocs are extremely good documented and if you would like a new feature from the Javadocs, reply on this topic. Do not pm me about this.

I would also like to thank Sorex for beta testing and the usual bunch on the chat: tds, NJDude, HotShoe, Barx.
(feel free to join us and talk about b4a! We are there 24/7 :p
Basic4Android Chat

In the attachments, you can find the usual stuff. A sample, lib files and readme

If you have any questions, please ask them in this topic.

Have fun!
Tomas


Note 1. It is best to associate all tweens with a manager instead of calling them manually.

Note 2. You can not call methods like to(...).delay(...).repeat(...).start becaus e Basic4Android handles only known types.

note 3. You can also declare an array of tweens like:
B4X:
dim tween(2) as Tween
tween(0).do
tween(1).do

note 4. If you receive some kind of error: "No TweenAccessor found or to this object." means that your accessor is incorrect. Probably, you will have called the wrong Accessor (registerPanelAccessor, etc) to the wrong type (e.g imageview)
 

Attachments

  • TweenEngine.zip
    120.4 KB · Views: 1,092
  • TweenEngine1.1.zip
    121 KB · Views: 1,948
Last edited:

sorex

Expert
Licensed User
Longtime User
Nice Dude.

Didn't know it already supported all the types, xVerhelstx said only those I've added back then.

And as said in a previous post that tweentype parameter is not really usefull with the current sub.

If you keep it you'll need something like this at the end...

tweentype=lcase(tweentype)
select case tweentype
case "position_x" MyTween(ID).setTarget(xPos) 'not sure this is the right command for a single offset tween tho
case "position_y" MyTween(ID).setTarget(yPos)
case "position_xy" MyTween(ID).setTarget(xPos, yPos)
end select

but I don't really see a benefit in the tweening engine, I doubt it would take less resources. you you better do XY and keep the same position if it doesn't change.

maybe it's better to remove that parameter to prevent confusion UNLESS someone is willing to adapt the lib code to support scaling/resizing of objects (without the need of my dirty methods) ;)
 

NJDude

Expert
Licensed User
Longtime User
The code is still evolving, some changes and modifications will be done as we better understand how this lib works, so far, it's working fine.

I'll try to find out a better solution if possible, I like to keep things simple. :D

You guys did a nice job porting that lib.
 

AurelienRibon

Member
Licensed User
Longtime User
Feel free to ask questions about the lib inner mechanisms and best practices. I'll be glad to help you make the best possible port.
 

XverhelstX

Well-Known Member
Licensed User
Longtime User
Hey Aurelien!

Welcome to the B4A forums! ;)
Here's the TweenAccessor class of a view (e.g imageview)

B4X:
package com.rootsoft.tweenengine;

import anywheresoftware.b4a.objects.ImageViewWrapper;
import aurelienribon.tweenengine.TweenAccessor;

// We implement TweenAccessor<Particle>. Note the use of a generic.

public class ImageViewAccessor implements TweenAccessor<ImageViewWrapper> {

    // The following lines define the different possible tween types.
    // It's up to you to define what you need :-)

    public static final int POSITION_X = 1;
    public static final int POSITION_Y = 2;
    public static final int POSITION_XY = 3;

    // TweenAccessor implementation

    @Override
    public int getValues(ImageViewWrapper target, int tweenType, float[] returnValues) {
        switch (tweenType) {
            case POSITION_X: returnValues[0] = target.getLeft(); return 1;
            case POSITION_Y: returnValues[0] = target.getTop(); return 1;
            case POSITION_XY:
                returnValues[0] = target.getLeft();
                returnValues[1] = target.getTop();
                return 2;
            default: assert false; return -1;
        }
    }
    
    @Override
    public void setValues(ImageViewWrapper target, int tweenType, float[] newValues) {
        switch (tweenType) {
            case POSITION_X: target.setLeft((int) newValues[0]); break;
            case POSITION_Y: target.setTop((int) newValues[0]); break;
            case POSITION_XY:
                target.setLeft((int) newValues[0]);
                target.setTop((int) newValues[1]);
                break;
            default: assert false; break;
        }
    }


}

Perhaps you might know some modifications to it to support alpha, colors, etc?

Tomas
 

sorex

Expert
Licensed User
Longtime User
I don't have all the source but for the scaling/resizing you can prolly duplicate all the x/y code and just mod it to .width .height instead of .left .top

B4X:
    public static final int POSITION_X = 1;
    public static final int POSITION_Y = 2;
    public static final int POSITION_XY = 3;
    public static final int RESIZE_XY = 4;

    // TweenAccessor implementation

    @Override
    public int getValues(ImageViewWrapper target, int tweenType, float[] returnValues) {
        switch (tweenType) {
            case RESIZE_X: returnValues[0] = target.getWidth(); return 1;
            case RESIZE_Y: returnValues[0] = target.getHeight(); return 1;
            case RESIZE_XY:
                returnValues[0] = target.getWidth();
                returnValues[1] = target.getHeight();
                return 2;
            default: assert false; return -1;
        }
    }

resizeTo() could be the tween command (like in mcTween)
 

AurelienRibon

Member
Licensed User
Longtime User
To add support for alpha, scale, etc, you just need to know how to change these parameters on a ImageViewWrapper object. I'm no expert in Android UIs (never used them yet, I only do opengl games), and I'm even less confident with ImageViewWrapper since I don't have access to B4A. However, if B4A wrappers provide the same method than official android components, then by reading the official documentation of View [1], I can build the following accessor. It supports tweening of position, scale, rotation on the 3 axes and opacity.

If you want to add support for more stuff, you get the idea on how to add them, it's dead simple. Your only limit is your imagination, and what the original framework (here the android API) can let you do.

[1] developer.android.com/reference/android/view/View.html

B4X:
package com.rootsoft.tweenengine;

import anywheresoftware.b4a.objects.ImageViewWrapper;
import aurelienribon.tweenengine.TweenAccessor;

public class ImageViewAccessor implements TweenAccessor<ImageViewWrapper> {
    public static final int POS_X = 1;
    public static final int POS_Y = 2;
    public static final int POS_XY = 3;
    public static final int SCALE_X = 4;
    public static final int SCALE_Y = 5;
    public static final int SCALE_XY = 6;
    public static final int ROT_X = 7;
    public static final int ROT_Y = 8;
    public static final int ROT_Z = 9;
    public static final int ALPHA = 10;

    @Override
    public int getValues(ImageViewWrapper target, int tweenType, float[] returnValues) {
        switch (tweenType) {
            case POS_X: returnValues[0] = target.getLeft(); return 1;
            case POS_Y: returnValues[0] = target.getTop(); return 1;
            case POS_XY: returnValues[0] = target.getLeft(); returnValues[1] = target.getTop(); return 2;
         case SCALE_X: returnValues[0] = target.getScaleX(); return 1;
         case SCALE_Y: returnValues[0] = target.getScaleY(); return 1;
         case SCALE_XY: returnValues[0] = target.getScaleX(); returnValues[1] = target.getScaleY(); return 2;
         case ROT_X: returnValues[0] = target.getRotationX(); return 1;
         case ROT_Y: returnValues[0] = target.getRotationY(); return 1;
         case ROT_Z: returnValues[0] = target.getRotation(); return 1;
         case ALPHA: returnValues[0] = target.getAlpha(); return 1;
            default: assert false; return -1;
        }
    }
    
    @Override
    public void setValues(ImageViewWrapper target, int tweenType, float[] newValues) {
        switch (tweenType) {
            case POS_X: target.setLeft((int) newValues[0]); break;
            case POS_Y: target.setTop((int) newValues[0]); break;
            case POS_XY: target.setLeft((int) newValues[0]); target.setTop((int) newValues[1]); break;
         case SCALE_X: target.setScaleX(newValues[0]); break;
         case SCALE_Y: target.setScaleY(newValues[0]); break;
         case SCALE_XY: target.setScaleX(newValues[0]); target.setScaleY(newValues[1]); break;
         case ROT_X: target.setRotationX(newValues[0]); break;
         case ROT_Y: target.setRotationY(newValues[0]); break;
         case ROT_Z: target.setRotation(newValues[0]); break;
         case ALPHA: target.setAlpha(newValues[0]); break;
            default: assert false; break;
        }
    }
}
 

XverhelstX

Well-Known Member
Licensed User
Longtime User
TweenEngine update beta!

Universal Tween Engine has been updated to 1.1 (beta)!
It now supports all following constants:

POS_X
POS_Y
POS_XY
SCALE_X
SCALE_Y
SCALE_XY
ROT_X
ROT_Y
ROT_Z
ALPHA

I might bring the official 1.1 release out soon with more methods.

Tomas

Edit: If you people want to see more functions:
Take a look at: developer.android.com/reference/android/view/View.html
and tell me which ones.
 
Last edited:

walterf25

Expert
Licensed User
Longtime User
Objects

well, i still have not figured out how to use this in a live wallpaper yet, but as you know, the Rect object is used to draw your images on a canvas, i have been playing with it, but i get errors, if there's a way to animate the rect which contains the bitmap that would be awesome!

Not sue if that's possible?

thanks, and great job with this library!

:sign0098:
 

walterf25

Expert
Licensed User
Longtime User
Tween Library

Hi X, i've been trying to implement the new methods you just added to versions 1.1, but i don't see a target.setrotation for the Rotate method!

i think this is needed to be able to use it!

thanks,
Walter
 

sorex

Expert
Licensed User
Longtime User
Tomas,

Did you actually test the modifications?

when I use the scale_xy, scale_x or scale_y tweentypes the app just crashes.
 

NJDude

Expert
Licensed User
Longtime User
I'm able to rotate the tween but yes, I believe somethng is missing, you cannot set the angle of rotation, and two, you cannot reset the rotation, in oter words, once you rotate a view stays rotated and won't rotate again.

I use this code to rotate a view and it works.
B4X:
Sub RotateTween

    TweenUpdate.Initialize("TweenUpdate", 10)
    TweenUpdate.Enabled = True
            
    Manager.Initialize

    MyTween(3).Initialize("Tween")

    MyTween(3).registerImageAccessor

    MyTween(3).ImageTo(tB4ALogo, Constants.ROT_Z, 1)   
            
    Manager.addTween(MyTween(3).Tween)
            
    Manager.StartTween

End Sub

Also, the SCALE_X, SCALE_Y and SCALE_XY have the same problem.

I have other questions but I think will discuss them them later.
 

sorex

Expert
Licensed User
Longtime User
It seems that you forgot to set the target value, but that's also something I have issues with. I can only see the setTarget() and that needs 2 values. not sure how to define just 1 single target.

That it doesn't rotate again is normal since it's already at it's target.
(it's rotate to ... degrees, not ratate by ... degrees)


here is my crash log...

B4X:
** Activity (main) Create, isFirst = true **


main_tween (java line: 403)


java.lang.NoSuchMethodError: android.widget.ImageView.getScaleY


   at com.rootsoft.tweenengine.ImageViewAccessor.getValues(ImageViewAccessor.java:33)
   at com.rootsoft.tweenengine.ImageViewAccessor.getValues(ImageViewAccessor.java:1)
   at aurelienribon.tweenengine.Tween.build(Tween.java:786)
   at aurelienribon.tweenengine.Tween.build(Tween.java:79)
   at aurelienribon.tweenengine.BaseTween.start(BaseTween.java:85)
   at aurelienribon.tweenengine.TweenManager.add(TweenManager.java:61)
   at com.rootsoft.tweenengine.RSTweenManager.addTween(RSTweenManager.java:61)
   at com.tween.sample.main._tween(main.java:403)
   at com.tween.sample.main._runanimation(main.java:309)
   at com.tween.sample.main._activity_create(main.java:222)
   at java.lang.reflect.Method.invokeNative(Native Method)
   at java.lang.reflect.Method.invoke(Method.java:521)
   at anywheresoftware.b4a.BA.raiseEvent2(BA.java:170)
   at com.tween.sample.main.afterFirstLayout(main.java:84)
   at com.tween.sample.main.access$100(main.java:16)
   at com.tween.sample.main$WaitForLayout.run(main.java:72)
   at android.os.Handler.handleCallback(Handler.java:587)
   at android.os.Handler.dispatchMessage(Handler.java:92)
   at android.os.Looper.loop(Looper.java:123)
   at android.app.ActivityThread.main(ActivityThread.java:4363)
   at java.lang.reflect.Method.invokeNative(Native Method)
   at java.lang.reflect.Method.invoke(Method.java:521)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:862)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
   at dalvik.system.NativeStart.main(Native Method)


Edit: just copy pasted Dude's code and the same crash happends, can it be that this is due to the fact that I'm still at v2.1 of the AndroidOS ?
 
Last edited:

sorex

Expert
Licensed User
Longtime User
ok, upgraded the OS to 2.2 but that didn't resolve anything :(
 

vb1992

Well-Known Member
Licensed User
Longtime User
Has anyone done anything with "Menu"' animations (See first post) yet with this?

Also, this library doesn't work with Froyo 2.2? or
just your demo NJDUDE?

thanks
 
Top