+ Integer.toHexString(to.hashCode()) + ")"); } CrossFadeDrawable crossFadeDrawable = newCrossFadeDrawable(from, to); ObjectAnimator animator = crossFadeDrawable.getAnimator(); imageView.setImageDrawable(crossFadeDrawable); animator.setDuration(ANIMATION_DURATION); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { if (FADE_DBG) { log("cross-fade animation start (" + Integer.toHexString(from.hashCode()) + " -> " + Integer.toHexString(to.hashCode()) + ")"); } } @Override public void onAnimationEnd(Animator animation) { if (FADE_DBG) { log("cross-fade animation ended (" + Integer.toHexString(from.hashCode()) + " -> " + Integer.toHexString(to.hashCode()) + ")"); } animation.removeAllListeners(); // Workaround for issue 6300562; this will force the drawable to the // resultant one regardless of animation glitch. imageView.setImageDrawable(to); } }); animator.start(); /* We could use TransitionDrawable here, but it may cause some weird animation in * some corner cases. See issue 6300562 * TODO: decide which to be used in the long run. TransitionDrawable is old but system * one. Ours uses new animation framework and thus have callback (great for testing), * while no framework support for the exact class. Drawable[] layers = new Drawable[2]; layers[0] = from; layers[1] = to; TransitionDrawable transitionDrawable = new TransitionDrawable(layers); imageView.setImageDrawable(transitionDrawable); transitionDrawable.startTransition(ANIMATION_DURATION); */ imageView.setTag(to); } else { if (FADE_DBG) { log("*Not* start cross-fade. " + imageView); } }