Kyle Banks

Partial Interface Implementations in Java, and Animation Complete Events on Android

Written by @kylewbanks on Aug 31, 2016.

The following trick actually works for any Java interface, Android or otherwise, but I find this to be particularly painful in animation-heavy Android applications, so I’m going to be using the AnimationListener interface as an example.

Listening for animation events on Android is quite simple, but I’m always a little annoyed that I need to implement all of the AnimationListener methods, when I usually only care about one. Empty interface implementations throughout my codebase are a pet-peeve of mine in general, so I thought I’d share how I get around this.

First of all, lets look at how to implement the AnimationListener:

public class MyActivity extends Activity implements Animation.AnimationListener {

    @Override
    public void onCreate(Bundle b) {
        super.onCreate(b)
        setContentView(R.layout.my_layout);

        // Load an animation and set the listener
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.my_animation);
        animation.setAnimationListener(this);
        
        // Load the view, and start the animation
        View viewToAnimate = findViewById(R.id.view_to_animate);
        viewToAnimate.startAnimation(animation);
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        
    }

    @Override
    public void onAnimationStart(Animation animation) {

    }

    @Override
    public void onAnimationEnd(Animation animation) {
        doSomething();
    }

}

Pretty simply, but you’ll notice that if all we care about is the onAnimationEnd event, for example, we have two other empty methods sitting there, cluttering our codebase. For a single Activity or a single interface it’s not a big deal, but in bigger projects, with many interfaces and implementers, things can start to become cluttered.

The way to resolve this is to use good old fashioned inheritance. We’ll create an abstract class that implements the AnimationListener, but leaves the single method we care about un-implemented:

public abstract class AnimationCompleteListener implements Animation.AnimationListener {

    @Override
    public void onAnimationRepeat(Animation animation) {

    }

    @Override
    public void onAnimationStart(Animation animation) {

    }

    @Override
    public abstract void onAnimationEnd(Animation animation);

}

You’ll see that the onAnimationRepeat and onAnimationStart methods have empty bodies, but they satisfy the requirement of implementing the AnimationListener interface. However, onAnimationEnd, the method we actually want to implement, is still abstract - meaning we haven’t satisfied the AnimationListener interface requirements. This is good, because we want instances of AnimationCompleteListener to fulfil this implementation.

Going back to our Activity above, here’s how we can cleanup the code:

public class MyActivity extends Activity {

    @Override
    public void onCreate(Bundle b) {
        super.onCreate(b)
        setContentView(R.layout.my_layout);

        // Load an animation and set the listener
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.my_animation);
        animation.setAnimationListener(listener);
        
        // Load the view, and start the animation
        View viewToAnimate = findViewById(R.id.view_to_animate);
        viewToAnimate.startAnimation(animation);
    }

    private AnimationCompleteListener listener = new AnimationCompleteListener() {
        @Override
        public void onAnimationEnd(Animation animation) {
            doSomething();
        }
    };
}

Now we pass our listener instance to the setAnimationListener, remove the implements AnimationListener from the class declaration, and remove the two empty method bodies. This works because the listener variable, an instance of AnimationCompleteListener, satisfies all requirements for implementing the AnimationListener interface - two methods are implemented by AnimationCompleteListener, and the final one is implemented by the listener instance.

Notes

This trick is really up to personal preference of the developer - neither way is inherently better. It’s just two ways to do the same thing.

Personally I only use this method if I frequently ignore multiple methods on an interface, and have them scattered throughout various parts of my application. If the animation example above was the only time in the entire codebase that I animate anything and need a completion listener, I probably wouldn’t bother as it does add a little complexity. However I find that the larger the application and/or codebase, the more likely this is to clean things up.

Let me know if this post was helpful on Twitter @kylewbanks or down below, and follow me to keep up with future posts!