Titanium Android Animation: An Alternative Module

by Bill Dawson on 22 March 2013

[UPDATE Feb 2014: I stopped working with Titanium some time ago and so this module is not supported, though I’ll keep it up on Github of course in case others are interested in forking it.]

I’m a fan of using external modules to get access to native platform features that might sometimes be difficult to expose in the core Titanium Mobile SDK because of its requirement that the API be in parity across platforms. I think I read or heard somewhere that 70:30 is a good ratio of common versus platform-specific code in a Titanium Mobile application. That ratio sounds suspiciously as though it was pulled out of a hat, but I think it makes some sense: 50:50 would be a bit disappointing, while 90:10 (or higher) seems perhaps unrealistic (though a great goal nonetheless!)

The tl;dr version: I created an Animation module for Titanium Android so as to use the newer Android animation system introduced in Honeycomb. The source is open and licensed under the Apache License, Version 2.0. Details at the linked page.

I think there was a time when that 30% of platform-specific code was pretty much only used to overcome annoyances such as parity bugs. As the engineering folks at Appcelerator continue to make huge strides in overcoming parity differences, I’m hoping that the 30% can be used more “positively”: not so much for getting around bugs but rather to use cool platform-specific features found in (for example) external modules.

That brings me to animation, particularly Android animation in Titanium. In the non-Titanium (“pure”) Android world, animation has changed greatly since the release of Honeycomb. Because Titanium must support back to Gingerbread, the newer Honeycomb+ animation system has never been used in the Titanium SDK. (Appcelerator might be thinking of incorporating the newer functionality — I simply don’t know.) And the pre-Honeycomb functionality had some major shortcomings. One of the most annoying is described by Chet Haase of Google in his blog post announcing the new Honeycomb animation system in February 2011:

[T]he previous animations changed the visual appearance of the target objects… but they didn’t actually change the objects themselves. You may have run into this problem. Let’s say you want to move a Button from one side of the screen to the other. You can use a TranslateAnimation to do so, and the button will happily glide along to the other side of the screen. And when the animation is done, it will gladly snap back into its original location. So you find the setFillAfter(true) method on Animation and try it again. This time the button stays in place at the location to which it was animated. And you can verify that by clicking on it – Hey! How come the button isn’t clicking? The problem is that the animation changes where the button is drawn, but not where the button physically exists within the container. If you want to click on the button, you’ll have to click the location that it used to live in. Or, as a more effective solution (and one just a tad more useful to your users), you’ll have to write your code to actually change the location of the button in the layout when the animation finishes.

It would, of course, be nice to have access to the newer, better behaving system. To that end, I’ve created an open source external Titanium module to use the newer Android animations in Titanium Android. Specifically, I’ve wrapped Jake Wharton’s excellent NineOldAndroids library, which makes the Honeycomb animation API available also on Gingerbread (and lower) devices. That way, if you choose to use my module, you won’t have to have different animation code for Gingerbread versus Honeycomb, which would be rather annoying.

(Note however that even with NineOldAndroids, you still face the condition described by Chet Haase above: the touch target of an animated view remains back at its originally layed-out position. There’s nothing we can do about this. You might notice other annoyances too on pre-Honeycomb devices. For example, my Rotate and Bounce sample is not behaving properly on an old 2.2 (Froyo) device that I have.)

Head on over to the module’s project page if you’re interested in checking it out. Feel free to use the Github Issues feature to submit bug reports or feature requests, but do keep in mind that this is a hobby project for me and I might not respond quickly.

At the time of this writing, I haven’t finished up the documentation, but I think the code samples will give you a good idea of how to use it. Here’s one snippet just to get you started:

var mod = require("com.billdawson.timodules.animation"),
       view = Ti.UI.createView();

   // setup the window and view, etc....
   // ... then later ...

   .withEndAction(function() {
       Ti.API.info("Animation done");
Bill Dawson February 14, 2014 at 6:43 pm

Sorry, no. I stopped working with Titanium some time ago. Hopefully you’ve found a solution in these several months that have passed.

Lachlan February 14, 2014 at 4:54 am

Never mind, I found the solution eventually:

The module error is because of the double reference to the nineoldandroids.jar. Just delete this package from the module lib dir of com.billdawson.timodules.animation.

Lachlan February 14, 2014 at 4:50 am

Hi Bill,

Nice idea. I’d love to use the module myself but am having trouble getting it to work. I used your app.js in the example dir and when I attemt to build for an android device I get this:

[INFO] : Packaging application: /Users/lachlan/development/adt-bundle-mac-x86_64-20130219/sdk/build-tools/19.0.1/aapt “package” “-f” “-m” “-J” “/Users/lachlan/Documents/Titanium_Studio_Workspace/countdown/build/android/gen” “-M”
[ERROR] : Failed to run dexer:
[ERROR] : java.lang.IllegalArgumentException: already added: Lcom/nineoldandroids/animation/Animator$AnimatorListener;
[ERROR] : at com.android.dx.dex.file.ClassDefsSection.add(ClassDefsSection.java:122)
[ERROR] : at com.android.dx.dex.file.DexFile.add(DexFile.java:161)
[ERROR] : at com.android.dx.command.dexer.Main.processClass(Main.java:685)
[ERROR] : at com.android.dx.command.dexer.Main.processFileBytes(Main.java:634)
[ERROR] : at com.android.dx.command.dexer.Main.access$600(Main.java:78)
[ERROR] : at com.android.dx.command.dexer.Main$1.processFileBytes(Main.java:572)
[ERROR] : at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)
[ERROR] : at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
[ERROR] : at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
[ERROR] : at com.android.dx.command.dexer.Main.processOne(Main.java:596)
[ERROR] : at com.android.dx.command.dexer.Main.processAllFiles(Main.java:498)
[ERROR] : at com.android.dx.command.dexer.Main.runMonoDex(Main.java:264)
[ERROR] : at com.android.dx.command.dexer.Main.run(Main.java:230)
[ERROR] : at com.android.dx.command.dexer.Main.main(Main.java:199)
[ERROR] : at com.android.dx.command.Main.main(Main.java:103)
[ERROR] : 1 error; aborting
[ERROR] Application Installer abnormal process termination. Process exit value was 1

Any thoughts on how I can overcome this problem?


Young October 7, 2013 at 3:31 pm

Hi Bill,

Wondering if you had time to review issue submitted to github?
We really like your module and are using it in our project and under a little time pressure to fix the issue with Android v2.3.x.

Would appreciate your advice on this. Cheers.

Young September 28, 2013 at 3:08 am
Bill Dawson September 3, 2013 at 2:16 am

Hi, thank you for the comment. I’d rather not look at full source code. Instead, please isolate the problem code to a simple fail case and use the Issues feature at Github.

Thank you,

Andrea September 1, 2013 at 3:19 am

Very useful module! Thank you!

Young August 29, 2013 at 6:48 pm

Hi Bill,
First of all, thanks for a really great module, something that we have been looking around for a while.

We’ve started writing some test codes with it and encountered some compatibility issues with devices running Android v2.3.3.

Could I send you the source code for your feedback?


Comments on this entry are closed.

Previous post:

Next post: