heihei blog

書けるときに書く。📝

Android TransitionにListenerを追加する

業務で少し調べたことのメモ程度に。

AndroidでViewをアニメーションさせたいときの実装方法の一つとして、Transitionがあります。

Transition

Transitionの概要については、公式ドキュメント、もしくはyarakiさんによる下記のスライドが参考になります。

yaraki.github.io

Transitionは、アニメーションの前後のViewの状態を保存、比較をして、それに応じたAnimatorを生成し実行してくれる便利なAPIです。

例えば下記のようなコードを実行すると、binding.target Viewの表示・非表示がフェードしながら切り替わります。

TransitionManager.beginDelayedTransition(binding.root) // rootとなるViewGroupを指定
binding.target.isVisible = !binding.target.isVisible // アニメーションしたいViewの描画状態を更新

beginDelayedTransition関数は魔法のような関数となっており、これを実行後にViewの表示切り替えを行うことで、よしなにアニメーションをしてくれます。

上記の例では、内部でデフォルトとして設定されているTransitionクラスを利用して動きのアニメーションを実現します。もちろんデフォルト以外の動きを実現するために、自分で独自のTransitionクラスを定義し渡すこともできます。

val myTransition = MyTransition()
TransitionManager.beginDelayedTransition(binding.root, myTransition) // 第2引数にTransitionを渡す

Listenerを追加する

よくあるパターンとして、アニメーションの終了後に何らかの処理を実行したい場合があります。

Transitionが終了したタイミングでよしなに処理を行いたい場合には、TransitionクラスのaddListener関数を使って、Listenerを追加します。

以下はTransitionクラスのaddListener関数の内部実装です。

/**
 * Adds a listener to the set of listeners that are sent events through the
 * life of an animation, such as start, repeat, and end.
 *
 * @param listener the listener to be added to the current set of listeners
 *                 for this animation.
 * @return This transition object.
 */
 @NonNull
 public Transition addListener(@NonNull TransitionListener listener) {
   if (mListeners == null) {
     mListeners = new ArrayList<>();
   }
   mListeners.add(listener);
   return this;
 }

カスタムTransitionを定義している場合はTransition初期化時にaddListener関数を呼び出すことで問題なくListenerを追加できます。

val myTransition = MyTransition().addListener(object : Transition.TransitionListener() {
  ... // 関数をoverride
})
TransitionManager.beginDelayedTransition(binding.root, myTransition)

では、カスタムTransitionを定義していない場合(=beginDelayedTransition関数の引数にTransitionクラスを渡さない場合)はどうすればいいのでしょうか?

そのような場合には、Transitionクラスが内部で保持している、デフォルトのTransitionクラスを初期化し、このクラスにListenerを追加できます。

デフォルトのTransitionクラスというのは、AutoTransitionというクラスです。内部実装を読むと、TransitionManagerクラスに、private staticフィールドとして定義されていることがわかります。

public class TransitionManager {
  ...
  private static Transition sDefaultTransition = new AutoTransition();

なのでこのAutoTransitionクラスを初期化して利用をすれば、問題ないです。

val transition = AutoTransition().addListener(...)
TransitionManager.beginDelayedTransition(binding.root, transition)

Tips

addListener関数には素直にTransitionListenerクラス自体を渡すこともできますが、代わりにこのListenerクラスを実装したTransitionListenerAdapterというクラスを渡すこともできます。AdapterクラスはAndroidのAPIにあるinterfaceクラスをただoverrideしただけ(独自の処理はない)のclassです。TransitionListener以外のListenerなどに対しても定義されていることがあります。

Adapterクラスがintefaceに定義されている関数をすべて空で実装しているため、このAdapterクラスを代わりに利用することで、Listenerに定義されている関数の中で、開発者がoverrideしたい関数のみをAdapterクラスを介してoverrideして利用することができます。

最終的には以下のようなコードとなりました。

val transition = AutoTransition()
    .addListener(object : TransitionListenerAdapter() {
      override fun onTransitionEnd(transition: Transition) {
        // do something
      }
    })
TransitionManager.beginDelayedTransition(binding.root, transition)
binding.target.isVisible = !binding.target.isVisible

まとめ

  • AndroidでViewのアニメーションを行う際にはTransition APIも選択肢の一つ
  • Transition APIを使ったアニメーションで、所定のタイミングで処理を実行したい場合にはTransitionクラスのaddListener関数を利用する
  • デフォルトで用意されているTransitionクラスはAutoTransition
  • TransitionListener interfaceにはTransitionListenerAdapterクラスが用意されている