2013-04-08 14 views
9

私のアプリケーションでFragmentsAnimationを作成しました。 はタブの間を動くようにアニメーションします。そのためには、現在Fragmentをアニメーション化して画面から完全に移動する必要がありますが、反対側からはFragmentが必要です。種類はAnimationのようにViewPagerを使用しています。XMLで画面サイズのアニメーションを定義する方法

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

私の質問は次のとおりです。Fragmentをスライドさせて同時にすべてのデバイスにフィットさせるXMLアニメーションを定義するにはどうすればよいですか?

マイアニメーション:

<?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> 

答えて

17

1):

Existing answers on SOあなたはObjectAnimatorを使用したい場合は、あなたがアニメーション化するViewをサブクラス化するしかないでframeLayoutをサブクラス化...

を提案。基本的にgetterメソッドとsetterメソッドを呼び出してAnimationを実行するので、ObjectAnimatorに必要なゲッターメソッドとセッターメソッドを提供する必要があります。

あなたは(Animate the transition between fragments)にリンクされている質問はsetXFraction()getXFraction()メソッドを追加するFrameLayoutをサブクラス化されます。これらは、FrameLayoutの幅を基準にx値を設定する方法で実装されています。これを行うことによってのみ、ObjectAnimatorは絶対値の間でアニメートする以外に何かを行うことができます。

要約すると、ObjectAnimator自体は実際にはほとんどアニメーション化されていません。それは、リフレクションによってゲッターとセッターメソッドを呼び出すだけです。

xmlファイルに実際の画面のピクセルサイズ( ではなく)を取得する方法はありますか?

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

、コードのセットは、次に からそれが一定のアクセス、画面の幅に等しくする 定数を前記有し、幅は 微細あろう返すカスタム関数を呼び出す、またはコードを設定することができる定数を定義しますxmlも同様に便利です。

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


2)解決策:ビューアニメーションが

一つの可能​​な解決策は、ObjectAnimatorするのではなく、ビューのアニメーションを使用することです。ビューのアニメーションを使用すると、だけでなく、絶対値の端数を指定することができます。これは、画面の高さの0%-100%からViewをアニメーションう

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

。言い換えれば、完全に画面からViewの位置までです。

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

これはあなたにとって役に立たないかもしれないと私は気付いています。 ObjectAnimatorsを使用する必要があり、ビューアニメーションを使用できない場合があります。しかし、ビューアニメーションを使用できる限り、これはあなたの問題をかなり解決するはずです。


3)私たちは本当にここに取り組むべきであるどのようなベストプラクティス

は、私はあなたの部分の誤解と思われるものです。すでに気づいているように、xmlで定義したすべてのアニメーションには絶対的な開始値と終了値があります。これらの値は静的であり、アプリケーションのコンパイル後は変更できません。

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

リソースは実際には静的で不変のもののために設計されています。ディメンションや文字列の変換には効果的ですが、アニメーションでは時には痛みがあります。上で述べたように、ビューアニメーションは、絶対値ではなく端数を指定するオプションを提供することで、静的な性質を取り巻く方法を提供します。しかし、一般的にxmlでは動的なものを定義することはできません。アニメーションに関するGoogleの優れたDevBytesの動画で、この引数の外観を裏付けるfutherする

これらの例では、xmlで1つのアニメーションしか定義されていません。確かに、彼らはxmlでそれらを定義しないと主張することができます。なぜなら、コードを説明して表示するのが楽になるからですが、私の意見ではこれがもう一度一つの点を証明します:

静的純粋に動的な値に依存するリソース

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

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

これは、ピクセル単位での終了値に最初からViewのx位置をアニメーション化します。

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

これは左サイドからスライドさせるViewをアニメーション化します:あなたがしたいようにあなたはそれをアニメーション化するViewの幅に渡すことができます。それは画面から完全に始まり、最終位置で停止します。また、単にこれを行うことができます:

view.animate().x(targetValue); 

これは、ターゲットのx値に、現在のx位置からViewをアニメーション化するでしょう。

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


4)まとめ

だから、要約する:

  • をあなたは、あなたがアニメーション化するViewに必要なgetterメソッドとsetterメソッドを追加することなく、ObjectAnimatorsとやりたいことができません。
  • 可能であればview animationsを使用してください。それらを使用して、Viewの幅または高さの分数に基づいてアニメーションを定義できます。
  • 上記が機能しない、または状況に該当しない場合は、アニメーションprogrammaticallyを定義してください。これはどんな場合でも動作します。

お手数ですがお気軽にお問い合わせください。

+0

すばらしい答えをありがとう。大きな制限は、FragmentTransactionにアニメーションを追加する唯一の方法は、setCustomAnimations(int enter、int exit)であることです。そのAPIはAnimatorリソースIDではなく、AnimatorリソースIDを期待しているようです。アニメーターをコードで定義することは問題ありません。しかし、それにリソースIDを付けることはできますか? –

+0

問題は解決しました。フラグメントのonCreateAnimatorメソッドでコードアニメーターを作成します。 –

+0

私はちょうど 'onCreateAnimator()'についてあなたに伝えたいと思っています。あなた自身でそれを理解したと思います。私は助けることができてうれしい! –

3

だけで、画面密度に基づいて別のフォルダを作成します。

アニメーションのxmlがanim(res親フォルダ内)というフォルダにあるとします。

次に、resにもanim-ldpi、anim-mdpiなどを作成します。画面密度を表すこれらのフォルダのそれぞれにそれぞれのアニメーションを入れ、アンドロイドが適切なものを選択します。恵みについて

+0

はお答えしますか?コメントしてください。 – j2emanue

+0

'anim-ldpi'、' anim-mdpi'などは、画面のサイズではなく、画面の_density_のバリエーションです。 – matiash

+0

私はこの答えは質問のポイントを逃すと思います。 –

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; 
    } 

} 
関連する問題