Transitions APIを使用したアニメーション

こんにちは



4.4(KitKat)からAndroidに登場し、5.0(Lollipop)で開発を継続したアニメーションのTransitions APIのトピックは、まだハブで取り上げられていません。 私の記事では、アニメーションを使用してアニメーションを単純化する方法と、 Android 4.0以降搭載した任意のデバイスに適用する方法について説明します。







Android 4.4とともに、レイアウトの変更をアニメーション化する新しいメカニズムが導入されました。 バージョン4.0でも、この問題の最初の解決策が表示されました-ViewGroupのanimateLayoutChangeフラグです。 しかし、getLayoutTransition()メソッドを呼び出してそのパラメーターを変更しても、十分な柔軟性がなく、変更のアニメーション化(遷移)を完全に制御することはできませんでした。



KitKat Transition APIは、シーンの概念、 シーン 、およびシーン間のいくつかの変更をもたらします。 それらに加えて、 シーンルートの概念を導入して、シーンの変更が発生するルートレイアウトを決定します。 また、シーン自体はViewGroupに対する一種のラッパーであり、シーンの特定の状態とそれに含まれるすべてのViewオブジェクトを記述します。



次に、 トランジション自体について説明します。 これは、ビューから必要なパラメーター値を読み取るためのメカニズムであり、シーンを変更し、アニメーションを生成するとこれらの状態がスムーズに変更されるときに変更されます。



Transitions APIの最も単純なユースケースから始めましょう。

レイアウトの現在の状態が最初のシーンであると想像してください。 正方形があるとしましょう。

xmlを使用してレイアウトを説明しましょう。



<FrameLayout android:id="@+id/container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:id="@+id/transition_square" android:layout_width="@dimen/square_size_normal" android:layout_height="@dimen/square_size_normal" android:background="@color/square_bg"/> </FrameLayout>
      
      





ここで、正方形のサイズをアニメーション化したいだけです。 シーンの概念に慣れ始めるために、これを2番目のシーンへの移行と呼びます。



  ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.container); View square = mSceneRoot.findViewById(R.id.transition_square); int newSquareSize = getResources().getDimensionPixelSize(R.dimen.square_size_expanded); //  ,   ,        sceneRoot TransitionManager.beginDelayedTransition(sceneRoot); //     ViewGroup.LayoutParams params = square.getLayoutParams(); params.width = newSquareSize; params.height = newSquareSize; square.setLayoutParams(params);
      
      





結果:

画像



悪くない、ただ一行のアニメーション。 また、レイアウト内にはこのような変更がいくつあってもかまいません。

それでは、いくつかのアニメーションパラメータを設定してみましょう。 これを行うには、実行する特定の遷移を指定する必要があります。 beginDelayedTransitionメソッドは、Transitionクラスの子孫の2番目のパラメーターを取ることができます。 今から話をするのは彼らについてです。



単純な遷移タイプ





シーン



シーンに戻ります。 最初の例では、APIを使用する最も簡単な方法を見ました。 ただし、シーンをより形式的に記述すると、たとえば、シーンを切り替えるのがより便利になります。



アクティビティを変更するためのボタンと、シーンのルートとなるFrameLayoutがあるように、アクティビティのレイアウトを説明します。 その中に、最初のシーンであるレイアウトをすぐに配置しますが、別のファイルに配置します。



 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <Button android:id="@+id/btn_change_scene" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Change scene" android:layout_gravity="center_horizontal" /> <FrameLayout android:id="@+id/scene_root" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/scene1"/> </FrameLayout> </LinearLayout>
      
      





scene1のコンテンツ。 そこに同じ正方形を置きますが、左上隅に:



 <?xml version="1.0" encoding="utf-8"?> <FrameLayout android:id="@+id/container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:id="@+id/transition_square" android:layout_width="@dimen/square_size_normal" android:layout_height="@dimen/square_size_normal" android:background="@color/square_bg" android:layout_gravity="top|left"/> </FrameLayout>
      
      





そしてscene2ファイルを作成します。 四角形を右上隅に移動し、サイズを大きくします。 また、最初のシーンになかったTextViewを追加します。



 <FrameLayout android:id="@+id/container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:id="@+id/transition_square" android:layout_width="@dimen/square_size_expanded" android:layout_height="@dimen/square_size_expanded" android:background="@color/square_bg" android:layout_gravity="top|right"/> <TextView android:id="@+id/transition_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="This text fades in." android:textAppearance="?android:attr/textAppearanceLarge"/>/> </FrameLayout>
      
      





onCreateでは、アクティビティは次のことを説明します。



  ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.scene_root); // You can also inflate a generate a Scene from a layout resource file. final Scene scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.scene3, this); findViewById(R.id.btn_change_scene).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //    AutoTransition TransitionSet set = new TransitionSet(); set.addTransition(new Fade()); set.addTransition(new ChangeBounds()); //     set.setOrdering(TransitionSet.ORDERING_TOGETHER); //     set.setDuration(500); //   Interpolator set.setInterpolator(new AccelerateInterpolator()); TransitionManager.go(scene2, set); } });
      
      





結果:

画像



そして、主なものについて。 バックポート



このAPIを使用する人が少ない理由と、このAPIを知っている人が少ない理由を簡単に理解できます。 開発者はAndroid 2.xのサポートを拒否する道徳的権利を最近認識したばかりであり、ほとんどの新しいプロジェクトはついにminSdk Android 4.0のサポートから始まります。 しかし、これまでのところ、4.4以降でのみ動作するAPIを使用することはほとんど意味がありません。これは、ほとんどのデバイスがまだ古いバージョンであるためです。

しかし! Transition APIは既にAndroid 4.0以降で使用でき、アニメーションはどこでも同じように機能します。 これを行うために、私はTransitions Everywhereというライブラリを開発しています。 これは、古いバージョンのAndroidへのTransition APIのバックポートです。 プロジェクトに統合するには、ライブラリをgradle依存関係として接続する必要があります。



 dependencies { compile "com.andkulikov:transitionseverywhere:1.7.4" }
      
      





また、このAPIに関連付けられているすべてのクラスについて、インポートをパッケージcom.transitionseverywhereに置き換えます* android.transitionの代わりに*



図書館について



このトピックに興味を持ち始めたとき、2つの類似したライブラリを見つけました。

github.com/guerwan/TransitionsBackport

github.com/pardom/TransitionSupportLibrary

彼らを結びつけているのは、彼らの開発者が明らかに彼らの開発で得点し、彼らの両方がAndroidの古いバージョンのいくつかの機能の後方互換性のために実装できるすべてを完全に実現しなかったということです。 私はそれらを基礎として、多くの新しいものを追加しました。 たとえば、4.3より前のバージョンで発生した、いくつかの誤った動作のさまざまな修正。 さらに、私のライブラリAPIはAndroid 2.2以降のバージョンと互換性がありますが、4.0より前のバージョンでは、アニメーションなしでシーンの変更(レイアウト)が実行されます。 また、私は最新の状態に保つようにします。 最近、Android 5.0(Lollipop)の新しいバージョンがリリースされ、その中のTransitions APIパッケージ全体のコードも大きく変更されました。 すべての変更をライブラリに投稿しました。



5.0のAPIの新機能











対象



どのトランジションでも、多くの要素、つまり適用される目標を設定できます。

これは、Transitionクラスのメソッドを使用して行われます。



以前に追加された目標を削除する方法:



「すべてを除く」アクションを適用できる方法:



特定のレイアウトのすべての子を除外できるメソッド:



xmlを介して遷移を設定する



遷移自体は、xmlファイルからロードできます。 res / animフォルダーに配置します。 例



 <?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:app="http://schemas.android.com/apk/res-auto" app:transitionOrdering="together" app:duration="400"> <changeBounds/> <changeImageTransform/> <fade app:fadingMode="fade_in" app:startDelay="200"> <targets> <target app:targetId="@id/transition_title"/> </targets> </fade> </transitionSet>
      
      





他の3つの遷移で構成される操作のTransitionSetセットを作成しました。 同時に、以前は考慮されていなかったstartDelayプロパティ(アニメーションの開始までの遅延時間)をここで適用しました。 コードから呼び出すためのアナログは、 Transition.setStartDelay (long startDelay)です。



TransitionManager全体をxmlファイルから読み込むことができます。このファイルには、発生する順序に応じてシーンを変更するためのさまざまなルールを記述できます。 簡単な例:



 <?xml version="1.0" encoding="utf-8"?> <transitionManager xmlns:app="http://schemas.android.com/apk/res-auto"> <transition app:toScene="@layout/scene3" app:transition="@anim/scene3_transition"/> </transitionManager>
      
      





ご注意

上記の2つの例は、私のTransitions Everywhereライブラリで動作するように調整されています。 システムTransition APIを使用する場合、xmlファイルをres / transitionディレクトリに配置する必要があり、すべての属性について、 appの代わりにandroid:ネームスペースを使用する必要があります



カスタムトランジション



これらのトランジションがどれだけクールかを見るために、独自のトランジションを書きましょう。

TextViewのフォントサイズをアニメーション化するとします。 (コメントで提案されているように、この例は、アニメーション中にrequestLayout()が発生するプロパティをアニメーション化するべきではないため、現実から少し引き裂かれています。)

captureStartValues、captureEndValues、およびcreateAnimatorメソッドをオーバーライドする必要があります。 最初の2つは、シーンを変更する前後に必要なビューパラメーターの値を取得する役割を果たします。 実際、createAnimatorメソッドは、このパラメーター変更用のアニメーターを作成します。



 private class TextSizeTransition extends Transition { //  ,     HashMap  private static final String PROPNAME_TEXT_SIZE = "textSizeTransition:textSize"; private void captureValues(TransitionValues transitionValues) { //    TextView if (transitionValues.view instanceof TextView) { TextView textView = ((TextView) transitionValues.view); //    ,     dip transitionValues.values.put(PROPNAME_TEXT_SIZE, textView.getTextSize() / getResources().getDisplayMetrics().density); } } @Override public void captureStartValues(TransitionValues transitionValues) { captureValues(transitionValues); } @Override public void captureEndValues(TransitionValues transitionValues) { captureValues(transitionValues); } @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { //    ,           //        TextView. // endValues   null, ,      View  if (startValues != null && endValues != null && endValues.view instanceof TextView) { TextView textView = (TextView) endValues.view; //   float start = (Float) startValues.values.get(PROPNAME_TEXT_SIZE); float end = (Float) endValues.values.get(PROPNAME_TEXT_SIZE); if (start != end) { //    View   textView.setTextSize(start); //      return ObjectAnimator.ofFloat(textView, "textSize", start, end); } } return null; } }
      
      





結果:

画像



PS



Androidにスムーズな移行を追加してください!

もう一度、ライブラリへのリンク:

github.com/andkulikov/transitions-everywhere



All Articles