10

アプリケーションでを作成しAnimationましたFragmentsAnimationアニメーションはタブ間を移動します。そのためには、必要なものが反対側からスライドインしている間、電流をアニメーション化して完全にFragment画面外に移動する必要があります。用途のFragmentようなものです。AnimationViewPager

の絶対的な開始位置と終了位置を指定する必要があり、Viewデバイスによって寸法が異なるため、Animationすべてのデバイスに適合する XML を定義することはできません。より大きなデバイスではView、画面が完全にスライドしない可能性があり、小さなデバイスでは、View画面から離れても移動し続けます。

だから私の質問は次のとおりだと思います: XML でアニメーションを定義して、Fragment画面外にスライドさせながら同時にすべてのデバイスに適合させるにはどうすればよいでしょうか?

私のアニメーション:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">      

  <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator"
    android:propertyName="x" 
    android:valueType="floatType"
    android:valueTo="720" 
    android:valueFrom="0"
    android:duration="300"/>  

</set>
4

3 に答える 3

21

1) 報奨金について:

SOに関する既存の回答は、FrameLayoutをサブクラス化することを提案しています...

を使用したい場合は、アニメートしたい をObjectAnimatorサブクラス化するしかありません。基本的に を実行するために getter メソッドと setter メソッドを呼び出すだけなので、必要な getter メソッドと setter メソッドをViewに提供する必要があります。ObjectAnimatorAnimation

リンクしている質問(フラグメント間の遷移をアニメーション化する)は、サブクラス化してメソッドFrameLayoutを追加します。これらは、 の幅に相対的な x 値を設定する方法で実装されます。これを行うことによってのみ、絶対値間のアニメーション化以外のことができます。setXFraction()getXFraction()FrameLayoutObjectAnimator

要約すると、ObjectAnimatorそれ自体は実際にはアニメーション化をあまり行わず、リフレクションを通じてゲッター メソッドとセッター メソッドを呼び出すだけです。

実際の画面ピクセル寸法 (dp だけでなく) を xml ファイルに取得する方法は本当にありませんか?

でそれを達成するObjectAnimator方法はありません。ObjectAnimators開始値から終了値まで補間するだけです。上で説明したように、setter メソッドは実際に何が起こるかを定義します。

たとえば、幅を返すカスタム関数を呼び出すか、コードで設定できる定数を定義して、コードでその定数を画面の幅と等しくなるように設定してから、xml からその定数にアクセスするのも同様に便利です。

コードから任意の xml リソースに値を挿入することはできません。xml ファイルに含まれているものはすべて、ビルド時に APK にコンパイルされ、実行時に変更することはできません。そして、それはあなたの他の質問に対する答えでもあります.現在の画面サイズを含むxmlでアクセスできるリソースや定数などはありません。アプリがインストールされているデバイスの画面サイズなどの動的な値は、これらのリソースの一部にすることはできません。これは、それらのすべてがビルド時にアプリにコンパイルされるためです。


2) 考えられる解決策: アニメーションを表示する

考えられる解決策の 1 つは、 の代わりにビュー アニメーションを使用することObjectAnimatorです。ビュー アニメーションを使用すると、絶対値だけでなく分数を指定できます。

<set xmlns:android="http://schemas.android.com/apk/res/android">  
    <translate android:fromYDelta="-100%" android:toYDelta="0%" android:duration="1000"/>
</set>

これは、画面の高さのViewから-100%までをアニメーション化します。0%つまり完全に画面から の位置までView。次のように使用できます。

Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_down);
view.startAnimation(animation);

これがあなたにとって何の助けにもならないかもしれないことを私は理解しています。ObjectAnimatorsビュー アニメーションを使用しなければならない場合と使用できない場合があります。しかし、ビュー アニメーションを使用できる限り、これで問題はほとんど解決するはずです。


3) ベストプラクティス

ここで本当に取り上げるべきことは、あなたの誤解だと私が思うことです。xml で定義するすべてのアニメーションには、絶対的な開始値と終了値があることに既にお気づきでしょう。これらの値は静的であり、アプリのコンパイル後に変更することはできません。

リソースが多くの利用可能なセレクターと dp、sp... でどのように機能するかを見ると、1 つのことを行うように設計されていることがわかります。たとえば、aを 100dp
移動するアニメーションを定義できます。ViewDp は物理サイズの測定値です。100 dp は、任意のピクセル密度の画面でまったく同じ物理サイズになります。セレクターを使用して、アニメーションの動きがView大きすぎたり小さすぎたりする可能性がある、小さいまたは大きい画面のデバイス用にこのアニメーションを変更できます。ただし、入力できるのは静的な値のみです。セレクターを操作する以外に、各デバイスのアニメーションをカスタマイズすることはできません。

ご覧のとおり、リソースは実際には、静的で変更されないものすべてのために設計されています。寸法や文字列の変換には最適ですが、アニメーションでは少し面倒な場合があります。上で述べたように、絶対値の代わりに分数を指定するオプションを提供することで、静的な性質を回避する方法を提供するのはビュー アニメーションのみです。しかし、一般に、xml で動的なものを定義することはできません。

この議論をさらに裏付けるには、アニメーションに関する Google の優れた DevBytes ビデオをご覧ください。

次に、これらの例のアニメーションが 1 つも xml で定義されていないことに気付くでしょう。確かに、コードを簡単に説明して表示したいので、xml で定義しないと主張することができますが、私の意見では、これも 1 つのポイントを証明しています。

純粋に動的な値に依存する静的リソースでアニメーションを定義することはできません

画面の幅/高さはデバイスごとに異なるため、デバイスごとに異なるアニメーションが必要です。絶対値の代わりに分数を定義できるビュー アニメーションだけが、これを回避する方法を提供します。それ以外の場合は、アニメーションをプログラムで定義する必要があります。幸いなことに、次の場合は難しくありませんObjectAnimators

Animator animator = ObjectAnimator.ofFloat(view, View.X, startValue, endValue);
animator.start(); 

Viewこれにより、開始から終了までの x 位置がピクセル単位でアニメーション化されます。の幅を渡して、View必要に応じてアニメーション化できます。

Animator animator = ObjectAnimator.ofFloat(view, View.X, -view.getWidth(), 0.0f);
animator.start();

Viewこれにより、 が左からスライドインするようにアニメーション化されます。画面から完全に外れて開始し、最終的な位置で停止します。これを行うこともできます:

view.animate().x(targetValue);

Viewこれにより、現在の x 位置からターゲットの x 値までアニメーション化されます。

ただし、誤解しないでください。アニメーションであろうと他のものであろうと、リソースで可能な限り多くのことを定義するようにしてください。あなたの場合のように必要でない限り、アニメーションやその他の値をハードコーディングすることはできるだけ避けてください。


4) まとめ

要約すると:

  • ObjectAnimators必要な getter メソッドと setter メソッドViewをアニメーション化する必要があるものに追加せずに、やりたいことを実行することはできません。
  • 可能であればビュー アニメーションを使用してください。これらを使用すると、 の幅または高さの分数に基づいてアニメーションを定義できますView
  • 上記が機能しない場合、または状況に当てはまらない場合は、プログラムでアニメーションを定義してください。これはどのような場合でも機能します。

ご不明な点がございましたら、お気軽にお問い合わせください。

于 2014-06-30T04:18:40.333 に答える
0

誰かが私の実用的なソリューションを求めていました。ここにあります。Mono経由のC#コードです。願わくば、Java の作成者がその言語でどうあるべきかを理解できることを願っています。

public class MyFragment: Fragment  {

    public int TransitDuration {
      get {
        return 500;
      }
    }

    public override Animator OnCreateAnimator(FragmentTransit transit, bool enter, int nextAnim) {
      switch (transit) {
        case FragmentTransit.FragmentOpen:
          {
            if (enter) {
              return this.EnterFromLeftAnimator;
            } else {
              return this.ExitToRightAnimator;
            }
          }
        case FragmentTransit.FragmentClose:
          {
            if (enter) {
              return this.EnterFromRightAnimator;
            } else {
              return this.ExitToLeftAnimator;
            }
          }
        default:
          Animator r = base.OnCreateAnimator(transit, enter, nextAnim);
          if (r == null) {
            if (!this.IsAdded) {
              this.OnContextHidden();
            }
          }
          return r;
      }
    }


    public Animator EnterFromLeftAnimator {
      get {
        float width = Screen.MainScreen.PixelSize.Width; // this is an object of mine; other code cached the width there long ago. It would actually be better to use the window width.
        ObjectAnimator animator = ObjectAnimator.OfFloat(this, "X", width, 0);
        animator.SetDuration(this.TransitDuration);
        Animator r = animator as Animator;
        return r;
      }
    }
    public Animator ExitToRightAnimator {
      get {
        float width = Screen.MainScreen.PixelSize.Width;
        ObjectAnimator animator = ObjectAnimator.OfFloat(this, "X", 0, -width);
        animator.SetDuration(this.TransitDuration);
        Animator r = animator as Animator;
        r.AddListener(new AnimatorEndListenerAdapter(() => this.OnContextHidden()));
        return r;
      }
    }
    public Animator EnterFromRightAnimator {
      get {
        float width = this.ScreenWidth;
        ObjectAnimator animator = ObjectAnimator.OfFloat(this, "X", -width, 0);
        animator.SetDuration(this.TransitDuration);
        Animator r = animator as Animator;
        return r;
      }
    }
    public Animator ExitToLeftAnimator {
      get {
        float width = this.ScreenWidth;
        ObjectAnimator animator = ObjectAnimator.OfFloat(this, "X", 0, width);
        animator.SetDuration(this.TransitDuration);
        Animator r = animator as Animator;
        r.AddListener(new AnimatorEndListenerAdapter(() => this.OnContextHidden()));

        return r;
      }
    }

    public override void OnActivityCreated(Bundle savedInstanceState) {
      base.OnActivityCreated(savedInstanceState);
      this.RetainInstance = true;
    }

}
于 2016-08-13T21:25:49.043 に答える