UPDATE: The code for this is open-sourced.
This is heavy. So the Android API itself simply want to stop you to do this.
But do we have to stop here? Not really, we still need some sort of animation happens in our widget, with standard home screen supported (ie no need custom home).
Recently I pop out with an idea of making a Mario Coin Block Widget, if you click (or should I say, TAP?) on it, a coin will come out. Without animation, this is nothing. Although it sounds easy (move the block up and down, shows a moving & rotating coin) and hey, it’s only 80s NES animation, there’s no help from the android system. The remote view (setInt) not even let me change the layout setting on my views! Correction: While SetInt cannot, there something called SetBundle in RemoteViews that accepts multiple parameter and thus, you can call setPadding() of view from there.
Since you have the only way to interact/control your widget is with RemoteViews, we have to consider what can be done there, and the entry point of this, will be the SetImageViewBitmap(int, Bitmap). This function allows you to put a Bitmap class object to it and together with simple Handler, you can make 80s animation back to life!
This is how I do this. (NB: I am much more comfortable to use C# like naming convention for classes/objects)
public interface IAnimatable { public abstract boolean AnimationFinished(); public abstract void Draw(Bitmap canvas); }
IAnimatable is the interface that let your custom animation do the work. Note that the AnimationFinished() is here for the parent to know if it can still need to poll the redraw regularly. Let me remind you that the redraw is so heavy that you shouldn’t do too much, so it is stopped immediately after it is done. Next will be my concrete implementation on the animating coin.
public class coinAnimation implements IAnimatable { private static int[][] coinSprite; private static int swidth; private static int sheight; private int currentSprite = 0; private int step; // We need the context to get the resource to Bitmap public coinAnimation(Context context, CoinBlockView parent) { step = 10; currentSprite = 0; prepareSprite(context, R.drawable.money_sprites); } // Read the sprite from res folder, strip it into sprites, // we are saving int[] here as it is most native to Bitmap.setPixels private void prepareSprite(Context context, int srpiteResId) { if (coinSprite !=null) return; BitmapFactory.Options options = new BitmapFactory.Options(); options.inScaled = false; Bitmap coin = BitmapFactory.decodeResource(context.getResources(), srpiteResId, options); swidth = coin.getWidth()/4; sheight = coin.getHeight(); coinSprite = new int[4][swidth*sheight]; for(int i=0; i<4; i++) { coin.getPixels(coinSprite[i], 0, swidth, swidth * i, 0, swidth, sheight); } } // Is animation done? public boolean AnimationFinished() { return step==0; } // Parent passing the Bitmap as the canvas to draw on it (it's pretty much like most GUI APIs). public void Draw(Bitmap canvas) { // Draw sprite canvas.setPixels(coinSprite[currentSprite], 0, swidth, (canvas.getWidth() - swidth) /2 , step * 2, swidth, sheight); step --; currentSprite++; currentSprite %= 4; } }
So, everytime the parent call draw, it will try to draw different sprite on the “canvas” Bitmap. And you can even have multiple children drawing different things! Finally, how I do the parent.
// warning: code snippet only! // This is called when parent need to redraw. public void onRedraw(Context context) { // Look for the widget in RemoteViews and create a Blank canvas RemoteViews rviews = new RemoteViews(context.getPackageName(), R.layout.coin_block_widget); Bitmap canvas = Bitmap.createBitmap(72, 72, Bitmap.Config.ARGB_8888); // Loop through child objects. // Since we need to remove from the set, we have to convert it to array first IAnimatable [] child = new IAnimatable[Children.size()]; Children.toArray(child); for(int i=0; i<child.length; i++) { child[i].Draw(canvas); // Remove or not, up to your parent implementation if (child[i].AnimationFinished()) Children.remove(child[i]); } state.Draw(canvas); rviews.setImageViewBitmap(R.id.block, canvas); AppWidgetManager.getInstance(context).updateAppWidget(id, rviews); // This is important! Your owner view should consider (and think careful) // That whether it still need to redraw on regular basis if (state.NeedRedraw() || Children.size() > 0) setTimedRedraw(REFRESH_RATE); } // Poll a handler timer to redraw private void setTimedRedraw(int timeMillis) { Handler timer = new Handler(); timer.postDelayed( new Runnable(){ @Override public void run() { onRedraw(CoinBlockWidgetApp.getApplication()); } } , timeMillis); }
As a conclusion, what you could do is some old-school sprite-based animation, with some simple movements of objects. The bottom line for this would be, keep your animation small and short enough!
Do you happen to have a eclipse source build of this? I am trying to do something similar but would love some help on getting started
You mean the Mario Coin Block?
yes, there seems to be no good android widget examples of animations, and I can’t seem to piece yours together from the code examples
It’s ok, I can send you those (although I don’t have time to document it). Maybe you can leave me your email?
That would be awesome
gcp@mortyg.com
Hey just to make sure I don’t miss it and it gets thrown into my spam what is your email it will be coming from?
Thanks again should be very insighful!
I decided to put in network drive. Here’s the link:
http://www.mediafire.com/?w9prc16xprrzmxw
Please forgive about the poor coding there, I don’t have time to refactor or document those stuff
Is there a variable to control the speed of the frames? The animations seem to go at a very high speed and I’d like to slow it down. Thanks.
What machine you are running? You are the first one to say the animation is too fast!
Hello I am trying to change padding on a widget using the setBundle and setPadding methods, but I cannot. did you get this to actually work or am I barking up the wrong tree?
By quickly looking again on the RemoteViews API, it doesn’t seems it’s possible. The setBundle assumes the View has a method which taking one Bundle as a parameter, i.e., if you call setBundle(id, “setPadding”, bundle), that means you have a method: view.setPadding(Bundle bundle), but since only setPadding(int, int, int, int) is available, your call ends up nothing.
That’s what I thought, so I was confused by your remark that it could be done. Thanks for the reply.
MY question is that when i open the emulator the google and music widget is by default already on home screen…so i also want that same thing for my widget that when i open my emulator i can see my widget like google and music…
plzzz help me and reply…
i searched every where but i found nothing…
There’s no way to do. Widgets are supposed to be installed by user, you cannot programatically force the widget to appear on screen.
For a simple conclusion, can we add animations on android widgets? Or it is done on the hardcode itself. We are looking for animating a weather widget on the screen and having troubles to decide with developers in what way to create animations. Our designer will create animations but can we integrate them and run them on widget?
Thanks..
Simple answer, Yes. As you can see the demo project (that mario block widget, which is open sourced), it’s working. Basically it is rendering in off screen Bitmap and “set” the bitmap to image view. The drawback is it is power demanding, and possibly limited to a relative small size of rendering.
Hi, I have been making widget that is animated and does some animations on its own, periodically (every few seconds and animation occurs). I found your advices very helpful, thank you! I was hoping you have an idea for following problem: Since animations are pretty expensive, I would my widget to make animations only when user is on home screen. User is not on home screen in two cases: screen is turned off or another application is running. Finding out when screen is off is not hard, but how can I recognise if there is an application running (in that case I want widget to stop doing animations).
Thank you!
See my new post: http://wp.me/pVVRP-7N
Thanks!