Animation / DreamService / Text Clock

TextClock Version 2 – Part 2

In the previous article in this series we began the update of the TextClock app widget, lock screen widget, and daydream by selectively, according to OS version, updating the colours to match the new KitKat theme. In this article we’ll look at adding some animations.

The first thing that we need to do is define a couple of animations. We want to slide the existing text out to the left, so we’ll create an animation in res/anim/out_left:






We want to slide the new text in from the right, so well create another in res/anim/in_left:






I’m not going to give an explanation of view animations here, please take a look at the Simple Animation articles if you are unfamiliar with them.

We will use these animations throughout the app, so it makes sense to get them created up front.

We’ll begin by animating the daydream because this is the simplest! We are dealing with TextViews only, so the simplest way of doing this is to use a TextSwitcher which will do a lot of the heavy lifting for us.

What we we’ll do is replace the TextViews in the layout with TextSwitchers. We’ll use the same layout weighting strategy as before:





	

	

	

We could create a couple of static TextViews as children of the TextSwitcher, but we’ll be forced to take that approach later on so, for the purposes of showing different techniques, well use the factory mechanism for creating the child Views.

TextSwitcher extends ViewSwitcher, which extends ViewAnimator. ViewSwitcher supports the concept of a factory to create new views. Whenever an animation occurs, a new view is created using the factory, the old view is animated out while the new view is animated in, and then the old view is removed from the ViewSwitcher and can be GC’d.

TextSwitcher basically does this automatically whenever we call its setText() method, all we need to do is proved the factory to creature the TextView objects that it requires to do so. This actually works quite nicely for our requirements because we need different styles applied to to hours TextView and the two minutes ones:

private TextSwitcher hours;
private TextSwitcher tens;
private TextSwitcher minutes;

@Override
public void onAttachedToWindow()
{
	super.onAttachedToWindow();
	setInteractive( false );
	setFullscreen( true );
	setScreenBright( false );
	setContentView( R.layout.daydream );
	int animTime = getResources().getInteger(
		android.R.integer.config_shortAnimTime);
	hours = (TextSwitcher) findViewById(R.id.hours);
	hours.setFactory(new ViewSwitcher.ViewFactory() {
		@Override
		public View makeView() {
			TextView tv = new TextView(
				new ContextThemeWrapper(
					TextClockDaydream.this, 
					R.style.hoursTextDaydream));
				tv.setLayoutParams(new FrameLayout.LayoutParams(
					FrameLayout.LayoutParams.MATCH_PARENT,
					FrameLayout.LayoutParams.MATCH_PARENT, 
					Gravity.BOTTOM));
				tv.setGravity(Gravity.BOTTOM);
				tv.setTextAppearance(TextClockDaydream.this, 
					R.style.hoursTextDaydream);
				return tv;
			}
		});
		tens = (TextSwitcher) findViewById(R.id.tens);
		tens.getInAnimation().setStartOffset(animTime);
		tens.getOutAnimation().setStartOffset(animTime);
		ViewSwitcher.ViewFactory minutesFactory = 
			new ViewSwitcher.ViewFactory() {
			@Override
			public View makeView() {
				TextView tv = new TextView(
					new ContextThemeWrapper(
						TextClockDaydream.this, 
						R.style.minutesTextDaydream));
				tv.setLayoutParams(new FrameLayout.LayoutParams(
					FrameLayout.LayoutParams.MATCH_PARENT, 
					FrameLayout.LayoutParams.MATCH_PARENT, 
					Gravity.CENTER_VERTICAL));
				tv.setTextAppearance(TextClockDaydream.this, 
					R.style.minutesTextDaydream);
				return tv;
			}
		};
		tens.setFactory(minutesFactory);
		minutes = (TextSwitcher) findViewById(R.id.minutes);
		minutes.getInAnimation().setStartOffset(animTime * 2);
		minutes.getOutAnimation().setStartOffset(animTime * 2);
		minutes.setFactory(minutesFactory);
	}

You may notice that we programatically set start offsets to the summations for the different TextView widgets. The reason for this is just to break things up a little visually. If all of the animations were to start simultaneously it may appear a little dull and regimented, but staggering the starts makes things a little more fun!

We also need to modify our time update logic slightly to only update when each of the fields actually changes. If we don’t do this check each time update will cause all of the fields to animate even if they don’t change:

private void updateTime( Calendar date )
{
	String[] words = TimeToWords.timeToWords( date );

	if (hasTextChanged(hours, words[0])) {
		hours.setText(words[0]);
	}
	if (hasTextChanged(tens, words[1])) {
		tens.setText(words[1]);
	}
	String mins = words.length == 2 ? " " : words[2];
	if (hasTextChanged(minutes, mins)) {
		minutes.setText(mins);
	}
}

private boolean hasTextChanged(TextSwitcher switcher, 
		String newText) {
	boolean changed = newText != null;
	if (switcher != null && switcher.getCurrentView() != null) {
		View current = switcher.getCurrentView();
		if (current instanceof TextView) {
			CharSequence currentText = 
				((TextView) current).getText();
			if (currentText != null) {
				changed = !currentText.toString().equals(newText);
			}
		}
	}
	return changed;
}

If we run this we can see how the animations look when the time changes:

I mentioned earlier that animating the daydream was quite easy, but when we come to animating the app widget and lock screen widget things are a little more difficult because we do not have direct access to the views within our layout and we need to go through RemoteViews. Also TextSwitcher is not supported in app and lock screen widgets, so we’ll need to take an alternative approach. In the next article we’ll look at how we can animate the time transitions on these, as well.

The source code for this article can be found here, and TextClock is available from Google Play. Version 2.0.1 has recently been published which contains the changes in this article, but there will be a number of minor updates over the coming weeks as we add additional functionality to the app.

© 2014, Mark Allison. All rights reserved.

Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.