Gmail 3フラグメントアニメーションシナリオの完全な実用サンプル?


128

TL; DR:「Gmailの3フラグメントアニメーション」シナリオと呼んでいるものの完全な実用サンプルを探しています。具体的には、次のように2つのフラグメントから始めます。

2つの断片

いくつかのUIイベント(たとえば、フラグメントBの何かをタップする)では、次のことが必要です。

  • 画面から左にスライドするフラグメントA
  • フラグメントBは画面の左端までスライドし、縮小してフラグメントAが空いたスポットを取り込みます。
  • 画面の右側からフラグメントCをスライドさせて挿入し、フラグメントBによって空いた場所を取り上げます。

そして、戻るボタンを押すと、その一連の操作を元に戻します。

今、私はたくさんの部分的な実装を見てきました。以下でそのうちの4つを確認します。不完全であることに加えて、彼ら全員に問題があります。


@Reto Meier は、同じ基本的な質問に対するこの人気のある回答を提供してsetCustomAnimations()おり、で使用することを示していますFragmentTransaction。2つのフラグメントのシナリオ(たとえば、最初はフラグメントAしか表示されておらず、アニメーション効果を使用して新しいフラグメントBに置き換えたい)の場合、私は完全に同意しています。しかしながら:

  • 「イン」と「アウト」のアニメーションは1つしか指定できないため、3つのフラグメントのシナリオに必要なすべての異なるアニメーションをどのように処理するかわかりません。
  • <objectAnimator>彼のサンプルコードではピクセル単位でハードワイヤード位置を使用し、それは非現実的な与えられた様々な画面サイズであるように見えるだろう、まだsetCustomAnimations()Javaでこれらのものを定義する可能性を排除する、アニメーションのリソースを必要とします
  • 私はのようなものとでは規模のネクタイのための方法を対象アニメーターへと途方に暮れていますandroid:layout_weightAにおけるLinearLayout割合ごとにスペースを割り当てます
  • フラグメントCが最初にどのように処理されるかについて困惑しています(GONEandroid:layout_weightof 0?0のスケールで事前にアニメーション化されている?他の何か?)

@Roman Nurikは、自分で定義したものを含め、どのプロパティでもアニメーション化できることを指摘しています。これは、独自のカスタムレイアウトマネージャーサブクラスを発明する代わりに、ハードワイヤードポジションの問題を解決するのに役立ちます。これは一部には役立ちますが、Retoの残りのソリューションにはまだ困惑しています。


このpastebinエントリの作成者はいくつかの食欲をそそる疑似コードを示しています。基本的に、3つのフラグメントはすべて最初にコンテナに常駐し、フラグメントCはhide()トランザクション操作によって最初から非表示になっています。次に、UIイベントが発生したときにshow()Cとhide()Aを実行します。ただし、Bがサイズを変更するという事実をどのように処理するかはわかりません。また、同じコンテナに複数のフラグメントを追加できるように見えるという事実にも依存しています。それが長期にわたって信頼できる動作であるかどうかはわかりません(もちろん、問題なく動作するはずですがfindFragmentById()、中断する必要があります)。


このブログ投稿の著者は、Gmailがまったく使用setCustomAnimations()しておらず、代わりにオブジェクトアニメーターを直接使用していることを示しています(「ルートビューの左マージンを変更+右ビューの幅を変更するだけ」)。ただし、これは依然として2フラグメントソリューションAFAICTであり、ここで示した実装では、ピクセル単位で寸法を固定しています。


私はこれでプラグインを続けるつもりですので、いつか自分で答えてしまうかもしれませんが、誰かがこのアニメーションシナリオの3フラグメントソリューションを解決し、コード(またはそのリンク)を投稿できることを本当に期待しています。Androidのアニメーションでは、髪の毛を抜いたくなります。私を見た人は、これはほとんど実を結ばない試みであることを知っています。


2
Romain Guyが投稿したこのようなものではないですか?curious-creature.org/2011/02/22/...
フェルナンド・ガジェゴ

1
@ ferdy182:彼はフラグメントAFAICTを使用していません。視覚的には、これは正しい基本的な効果であり、フラグメントベースの回答を作成するための別のデータポイントとして使用できる場合があります。ありがとう!
CommonsWare 2012

1
考えてみてください。標準のメールアプリは、Nexus 7で同様の(完全に同一ではありませんが)動作を持っています。これはオープンソースである必要があります。
クリストファー・オー2012

4
メールアプリは、左側のビューの位置と他の2つのビューの幅/可視性をアニメーション化するカスタムの「ThreePaneLayout」を使用します。それぞれのビューには、関連するフラグメントが含まれています。
クリストファー・オー

2
こんにちは@CommonsWare私はここで私の全体の答えを気にしませんが、あなたに似た質問があり、私は非常に満足のいく答えを出しました。だから、あなたにそれをチェックアウトすることを提案するだけでなく(コメントもチェックしてください):stackoverflow.com/a/14155204/906362
Budius

回答:


67

提案をgithubにアップロードしました (この種類のアニメーションには、ハードウェアアクセラレーションの表示を強くお勧めしますが、すべてのAndroidバージョンで機能しています。ハードウェアアクセラレーション以外のデバイスの場合は、ビットマップキャッシュの実装が適しています)

アニメーション付きのデモビデオはこちら(スクリーンキャストのフレームレートが遅い原因。実際のパフォーマンスは非常に速い)


使用法:

layout = new ThreeLayout(this, 3);
layout.setAnimationDuration(1000);
setContentView(layout);
layout.getLeftView();   //<---inflate FragmentA here
layout.getMiddleView(); //<---inflate FragmentB here
layout.getRightView();  //<---inflate FragmentC here

//Left Animation set
layout.startLeftAnimation();

//Right Animation set
layout.startRightAnimation();

//You can even set interpolators

説明

新しいカスタムRelativeLayout(ThreeLayout)と2つのカスタムアニメーション(MyScalAnimation、MyTranslateAnimation)を作成しました

ThreeLayout左側のペインの重みをparamとして取得しますweight=1。他の表示ビューが持っていると仮定します

したがってnew ThreeLayout(context,3)、3 つの子を持つ新しいビューと、画面全体の1/3の左側のペインが作成されます。もう1つのビューは、使用可能なすべてのスペースを占めます。

実行時に幅を計算します。より安全な実装は、寸法がdraw()で最初に計算されることです。post()の代わりに

拡大縮小と移動のアニメーションは、実際にはビューのサイズを変更して移動し、疑似[scale、move]ではありません。fillAfter(true)どこでも使用されていないことに注意してください。

ビュー2はビュー1の右側です

そして

ビュー3はビュー2の右側です

これらのルールを設定すると、RelativeLayoutが他のすべてを処理します。アニメーションはmargins(移動中)および[width,height]スケールを変更します

各子にアクセスするには(フラグメントでそれを膨らませるように、

public FrameLayout getLeftLayout() {}

public FrameLayout getMiddleLayout() {}

public FrameLayout getRightLayout() {}

以下は2つのアニメーションを示しています


ステージ1

--- IN画面----------!----- OUT ----

[ビュー1] [_____ビュー2 _____] [_____ビュー3_____]

ステージ2

--OUT-!-------- IN画面------

[ビュー1] [ビュー2] [_____ビュー3_____]


1
私はまだスケールアニメータを実装していませんが、色の固体フィールドではうまく機能し、「実際の」コンテンツではうまくいかないのではないかと心配しています。フラグメントAのスペースに合わせてフラグメントBのサイズが変更された結果は、フラグメントBが元々そこに配置されていた場合と同じです。たとえば、スケールアニメーターのAFAIKは、フォントサイズを(アニメーション化されたView小さいものですべて描画することによって)スケーリングしますが、Gmail /メールでは、小さいフォントを取得するのではなく、単語数を減らします。
CommonsWare 2012

1
@CommonsWare scaleAnimationは、単なる抽象的な名前です。実際、スケーリングは行われません。ビューの幅を実際に変更します。ソースを確認してください。LayoutResizerの方が適切な名前かもしれません。
weakwire

1
これは、のscaleX値に関するソースの私の解釈ではありませんが、View誤解している可能性があります。
CommonsWare 2012

1
このチェック@CommonsWare github.com/weakwire/3PaneLayout/blob/master/src/com/example/...を行います。Animationを拡張する唯一の理由は、interpolatedTimeへのアクセスを取得することです。p.width =(int)幅; すべての魔法を行い、それはscaleXではありません。これは、指定された時間に変化する実際のビューの寸法です。
weakwire

2
ビデオは適切なフレームレートを示していますが、この例で使用されているビューは非常に単純です。アニメーション中にrequestLayout()を実行すると、非常にコストがかかります。特に子が複雑な場合(たとえば、ListView)。これはおそらく途切れ途切れのアニメーションになります。GMailアプリはrequestLayoutを呼び出しません。代わりに、アニメーションが開始する直前に、別の小さなビューが中央パネルに配置されます。
ティエリーディミトリロイ

31

質問のコメントでの@Christopherの提案に従って、Email AOSPアプリから派生した私の独自のソリューションを次に示します。

https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane

@weakwireのソリューションは私のものを連想させますがAnimation、アニメーターではなくクラシックを使用し、RelativeLayoutルールを使用してポジショニングを強制します。バウンティの観点からは、より洗練されたソリューションを持つ他の誰かが回答を投稿しない限り、彼はおそらくバウンティを獲得するでしょう。


簡単に言えば、ThreePaneLayoutそのプロジェクトのはLinearLayoutサブクラスであり、3人の子供がいる風景で作業するように設計されています。これらの子の幅は、レイアウトXMLで任意の方法で設定できます-重みを使用して示していますが、ディメンションリソースなどで特定の幅を設定できます。3番目の子(問題のフラグメントC)の幅はゼロにする必要があります。

package com.commonsware.android.anim.threepane;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;

public class ThreePaneLayout extends LinearLayout {
  private static final int ANIM_DURATION=500;
  private View left=null;
  private View middle=null;
  private View right=null;
  private int leftWidth=-1;
  private int middleWidthNormal=-1;

  public ThreePaneLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    initSelf();
  }

  void initSelf() {
    setOrientation(HORIZONTAL);
  }

  @Override
  public void onFinishInflate() {
    super.onFinishInflate();

    left=getChildAt(0);
    middle=getChildAt(1);
    right=getChildAt(2);
  }

  public View getLeftView() {
    return(left);
  }

  public View getMiddleView() {
    return(middle);
  }

  public View getRightView() {
    return(right);
  }

  public void hideLeft() {
    if (leftWidth == -1) {
      leftWidth=left.getWidth();
      middleWidthNormal=middle.getWidth();
      resetWidget(left, leftWidth);
      resetWidget(middle, middleWidthNormal);
      resetWidget(right, middleWidthNormal);
      requestLayout();
    }

    translateWidgets(-1 * leftWidth, left, middle, right);

    ObjectAnimator.ofInt(this, "middleWidth", middleWidthNormal,
                         leftWidth).setDuration(ANIM_DURATION).start();
  }

  public void showLeft() {
    translateWidgets(leftWidth, left, middle, right);

    ObjectAnimator.ofInt(this, "middleWidth", leftWidth,
                         middleWidthNormal).setDuration(ANIM_DURATION)
                  .start();
  }

  public void setMiddleWidth(int value) {
    middle.getLayoutParams().width=value;
    requestLayout();
  }

  private void translateWidgets(int deltaX, View... views) {
    for (final View v : views) {
      v.setLayerType(View.LAYER_TYPE_HARDWARE, null);

      v.animate().translationXBy(deltaX).setDuration(ANIM_DURATION)
       .setListener(new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
           v.setLayerType(View.LAYER_TYPE_NONE, null);
         }
       });
    }
  }

  private void resetWidget(View v, int width) {
    LinearLayout.LayoutParams p=
        (LinearLayout.LayoutParams)v.getLayoutParams();

    p.width=width;
    p.weight=0;
  }
}

ただし、実行時には、幅を最初に設定した方法に関係なくThreePaneLayout、最初にを使用hideLeft()して幅の管理が引き継がれ、フラグメントAおよびBと呼ばれる質問の表示からフラグメントBおよびCへの切り替えが行われます。ThreePaneLayout-その断片には特定の関係を持っていない- 3枚がありleftmiddleright。あなたが呼ぶ時hideLeft()、私たちはの大きさを記録leftしてmiddle、我々は完全にサイズを制御することができますので、この3つの任意に使用された任意の重みをゼロ。の時点でhideLeft()、のサイズをrightの元のサイズに設定しましたmiddle

アニメーションは2つに分かれています。

  • a ViewPropertyAnimatorを使用しleftて、ハードウェアレイヤーを使用して、3つのウィジェットをの幅で左に変換します。
  • ObjectAnimatorカスタム疑似プロパティでmiddleWidthを使用して、middle幅を最初の幅から元の幅に変更しますleft

(これは今のところ機能しますが、これらすべてにAnimatorSetand を使用する方が良い可能性がありますObjectAnimators

middleWidth ObjectAnimatorハードウェアレイヤーの値を無効にすることも可能です。これは、かなり継続的な無効化を必要とするためです)

(私がアニメーションの理解にまだギャップがあること、そして括弧付きのステートメントが好きなことは間違いなく可能です)

正味の効果はつまりleft、画面の外のスライドmiddleの元の位置と大きさにスライドleftし、right右背後に変換しますmiddle

showLeft() アニメーターの同じミックスで、方向を逆にして、単にプロセスを逆にします。

アクティビティは、ウィジェットのThreePaneLayoutペアとを保持するためにを使用しListFragmentますButton。左側のフラグメントで何かを選択すると、中央のフラグメントが追加(または内容が更新)されます。中央のフラグメントで何かを選択すると、のキャプションが設定され、でButton実行さhideLeft()れますThreePaneLayout。左側を非表示にした場合、[戻る]を押すと実行されshowLeft()ます。それ以外の場合、BACKはアクティビティを終了します。これはFragmentTransactionsアニメーションに影響を与えるために使用されないため、その「バックスタック」を自分で管理することはできません。

上記にリンクされているプロジェクトは、ネイティブフラグメントとネイティブアニメーターフレームワークを使用しています。アニメーションにAndroidサポートフラグメントバックポートとNineOldAndroidsを使用する同じプロジェクトの別のバージョンがあります。

https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC

バックポートは第1世代のKindle Fireでは問題なく動作しますが、ハードウェアのスペックが低く、ハードウェアアクセラレーションがサポートされていないため、アニメーションは少しぎこちなくなります。Nexus 7や他の最新世代のタブレットでは、どちらの実装もスムーズに見えます。

このソリューションを改善する方法や、ここでやったこと(または@weakwireを使用したこと)に対して明確な利点を提供する他のソリューションのアイデアには、私は確かにオープンです。

貢献してくれた皆さん、ありがとうございました!


1
こんにちは!解決策に感謝しますが、v.setLayerType()を追加すると、処理が少し遅くなることがあります。これらの行(およびリスナーも)を削除するだけです。これは、少なくともGalaxy Nexusで試したAndroid 3以上のデバイスに当てはまります。これらの行がないと、はるかにうまく機能します
Evgeny Nacu

1
「Nexus 7やその他の現世代のタブレットでは、どちらの実装もスムーズに見えます。」私はあなたを信じています; ただし、Nexus 7のネイティブObjectAnimatorを使用して実装をテストしているため、少し遅れます。最も可能性の高い原因は何ですか?AndroidMAnifestにはすでにhardwareAccelerated:trueがあります。何が問題なのか本当にわかりません。私はDanielGrechのバリアントも試しましたが、同じです。アニメーションの違いは確認できます(彼のアニメーションは重みに基づいています)が、どちらの場合も遅れる理由がわかりません。
ボグダンズラック2013

1
@Andrew:「少し遅れます」-ここで「遅れ」という動詞をどのように使用しているかはわかりません。私にとって、「遅れ」は比較のための具体的な目標を意味します。したがって、たとえば、スワイプモーションに関連付けられたアニメーションは、指の動きより遅れることがあります。この場合、すべてがタップによってトリガーされるため、アニメーションが遅れている可能性があるかどうかは不明です。おそらく「少し遅れる」とは単に「鈍感」を意味するだけだとは思いませんが、私はそれを見たことはありませんが、美的感覚が強いとは主張していません。
CommonsWare 2013

2
@CommonsWare:私は4.2でコードをコンパイルしました。中央をタップしてListView戻るボタンをすばやく押して繰り返したところ、レイアウト全体が右にずれました。左側に余分なスペースの列が表示され始めました。この動作が導入されたのは、最初のアニメーション(中央のタップで開始ListView)が完了せず、戻るボタンが押されなかったためです。私は交換することにより、それを固定translationXBytranslationXしてtranslateWidgetsの方法とtranslateWidgets(leftWidth, left, middle, right);してtranslateWidgets(0, left, middle, right);showLeft()する方法。
M-WaJeEh 2013

2
方法も入れ替えrequestLayout();ましmiddle.requestLayout();setMiddleWidth(int value);。GPUは完全なレイアウトパスを実行するので、パフォーマンスに影響しない可能性があります。コメントはありますか?
M-WaJeEh 2013

13

この問題を解決するPanesLibraryというライブラリを作成しました。以前提供されていたものよりもさらに柔軟性があります。

  • 各ペインは動的にサイズ変更できます
  • 2つまたは3つだけでなく、任意の数のペインを使用できます。
  • ペイン内のフラグメントは、向きを変更しても正しく保持されます。

あなたはそれをここでチェックすることができます:https//github.com/Mapsaurus/Android-PanesLibrary

こちらがデモです:http : //www.youtube.com/watch?v= UA-lAGVXoLU&feature =youtu.be

基本的には、動的にサイズ設定されたペインをいくつでも簡単に追加し、それらのペインにフラグメントを添付することができます。お役に立てば幸いです。:)


6

リンクした例の1つ(http://android.amberfog.com/?p=758)を基にして、layout_weightプロパティをアニメーション化してみませんか?このようにして、3つのフラグメントの重量の変化を一緒にアニメーション化して、すべてがうまくスライドするというボーナスを得ることができます。

シンプルなレイアウトから始めます。アニメーションlayout_weightを作成するのでLinearLayout、3つのパネルのルートビューとしてが必要です。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
    android:id="@+id/panel1"
    android:layout_width="0dip"
    android:layout_weight="1"
    android:layout_height="match_parent"/>

<LinearLayout
    android:id="@+id/panel2"
    android:layout_width="0dip"
    android:layout_weight="2"
    android:layout_height="match_parent"/>

<LinearLayout
    android:id="@+id/panel3"
    android:layout_width="0dip"
    android:layout_weight="0"
    android:layout_height="match_parent"/>
</LinearLayout>

次に、デモクラス:

public class DemoActivity extends Activity implements View.OnClickListener {
    public static final int ANIM_DURATION = 500;
    private static final Interpolator interpolator = new DecelerateInterpolator();

    boolean isCollapsed = false;

    private Fragment frag1, frag2, frag3;
    private ViewGroup panel1, panel2, panel3;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        panel1 = (ViewGroup) findViewById(R.id.panel1);
        panel2 = (ViewGroup) findViewById(R.id.panel2);
        panel3 = (ViewGroup) findViewById(R.id.panel3);

        frag1 = new ColorFrag(Color.BLUE);
        frag2 = new InfoFrag();
        frag3 = new ColorFrag(Color.RED);

        final FragmentManager fm = getFragmentManager();
        final FragmentTransaction trans = fm.beginTransaction();

        trans.replace(R.id.panel1, frag1);
        trans.replace(R.id.panel2, frag2);
        trans.replace(R.id.panel3, frag3);

        trans.commit();
    }

    @Override
    public void onClick(View view) {
        toggleCollapseState();
    }

    private void toggleCollapseState() {
        //Most of the magic here can be attributed to: http://android.amberfog.com/?p=758

        if (isCollapsed) {
            PropertyValuesHolder[] arrayOfPropertyValuesHolder = new PropertyValuesHolder[3];
            arrayOfPropertyValuesHolder[0] = PropertyValuesHolder.ofFloat("Panel1Weight", 0.0f, 1.0f);
            arrayOfPropertyValuesHolder[1] = PropertyValuesHolder.ofFloat("Panel2Weight", 1.0f, 2.0f);
            arrayOfPropertyValuesHolder[2] = PropertyValuesHolder.ofFloat("Panel3Weight", 2.0f, 0.0f);
            ObjectAnimator localObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(this, arrayOfPropertyValuesHolder).setDuration(ANIM_DURATION);
            localObjectAnimator.setInterpolator(interpolator);
            localObjectAnimator.start();
        } else {
            PropertyValuesHolder[] arrayOfPropertyValuesHolder = new PropertyValuesHolder[3];
            arrayOfPropertyValuesHolder[0] = PropertyValuesHolder.ofFloat("Panel1Weight", 1.0f, 0.0f);
            arrayOfPropertyValuesHolder[1] = PropertyValuesHolder.ofFloat("Panel2Weight", 2.0f, 1.0f);
            arrayOfPropertyValuesHolder[2] = PropertyValuesHolder.ofFloat("Panel3Weight", 0.0f, 2.0f);
            ObjectAnimator localObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(this, arrayOfPropertyValuesHolder).setDuration(ANIM_DURATION);
            localObjectAnimator.setInterpolator(interpolator);
            localObjectAnimator.start();
        }
        isCollapsed = !isCollapsed;
    }

    @Override
    public void onBackPressed() {
        //TODO: Very basic stack handling. Would probably want to do something relating to fragments here..
        if(isCollapsed) {
            toggleCollapseState();
        } else {
            super.onBackPressed();
        }
    }

    /*
     * Our magic getters/setters below!
     */

    public float getPanel1Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)     panel1.getLayoutParams();
        return params.weight;
    }

    public void setPanel1Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel1.getLayoutParams();
        params.weight = newWeight;
        panel1.setLayoutParams(params);
    }

    public float getPanel2Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel2.getLayoutParams();
        return params.weight;
    }

    public void setPanel2Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel2.getLayoutParams();
        params.weight = newWeight;
        panel2.setLayoutParams(params);
    }

    public float getPanel3Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel3.getLayoutParams();
        return params.weight;
    }

    public void setPanel3Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel3.getLayoutParams();
        params.weight = newWeight;
        panel3.setLayoutParams(params);
    }


    /**
     * Crappy fragment which displays a toggle button
     */
    public static class InfoFrag extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle     savedInstanceState) {
            LinearLayout layout = new LinearLayout(getActivity());
            layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            layout.setBackgroundColor(Color.DKGRAY);

            Button b = new Button(getActivity());
            b.setOnClickListener((DemoActivity) getActivity());
            b.setText("Toggle Me!");

            layout.addView(b);

            return layout;
        }
    }

    /**
     * Crappy fragment which just fills the screen with a color
     */
    public static class ColorFrag extends Fragment {
        private int mColor;

        public ColorFrag() {
            mColor = Color.BLUE; //Default
        }

        public ColorFrag(int color) {
            mColor = color;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            FrameLayout layout = new FrameLayout(getActivity());
            layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

            layout.setBackgroundColor(mColor);
            return layout;
        }
    }
}

また、この例では、FragmentTransactionsを使用してアニメーションを実現していないため(フラグメントがアタッチされているビューをアニメーション化しているため)、すべてのバックスタック/フラグメントトランザクションを自分で行う必要がありますが、アニメーションを機能させるのに比べてうまく、これは悪いトレードオフのようには見えません:)

動作中の恐ろしい低解像度ビデオ: http //youtu.be/Zm517j3bFCo


1
まあ、Gmailのは間違いなく0にその重量を減らすこと(例えば、視覚的なアーティファクトを導入かもしれないと心配です私は断片A.私として記述するものの翻訳を行うTextViewwrap_content、アニメーションの進行に伴って縦方向に自身を伸ばし)。ただし、ウェイトをアニメートすると、フラグメントBと呼ばれるもののサイズが変更される場合があります。ありがとうございます。
CommonsWare 2012

1
心配ない!それは良い点ですが、これは「フラグメントA」にある子ビューによっては視覚的なアーティファクトを引き起こす可能性があります。それを行うためのより堅実な方法を誰かが見つけられることを願いましょう
DanielGrech

5

これはフラグメントを使用していません。...3つの子を持つカスタムレイアウトです。メッセージをクリックすると、3人の子供を使用してオフセットしますoffsetLeftAndRight()と、アニメーター。

JellyBeanあなた「開発者に対しオプション」の設定で「表示が範囲をレイアウト」を有効にすることができます。スライドアニメーションが完了すると、左側のメニューはまだ表示されていますが、中央のパネルの下に表示されています。

Cyril MottierのFly-inアプリメニューに似ていますに、2つの要素ではなく3つの要素があり。

Additionnally、 ViewPager:三人の子供のは、この動作の別の指標であるViewPager通常のフラグメントを使用しています(私は、彼らが持っていないけど、私は、他の実装を見たことがないFragment)、そしてあなたが使用することができないので、Fragments別の内Fragment3人の子供をおそらく断片ではない...


これまで「レイアウト境界の表示」を使用したことがありませんでした。これは、このようなクローズドソースのアプリに非常に役立ちます(ありがとうございます)。フラグメントAのような感じている私は、フラグメントBとして説明するものの下で背中をポップする前に、オフスクリーン(あなたは右から左への右端のスライドを見ることができます)翻訳されたとして、私が説明するものと思われるTranslateAnimation私はかかわらず、同じ目的を達成するためにアニメーターを使用する方法があることを確認してください。これについていくつかの実験を実行する必要があります。ありがとう!
CommonsWare 2012

2

現在、私はそのようなことを試みていますが、フラグメントBのスケールが使用可能なスペースを確保し、十分なスペースがある場合は3つのペインを同時に開くことができます。これまでのところ私の解決策ですが、それを続けるつもりかどうかはわかりません。誰かが正しい方法を示す答えを提供してくれることを願っています。

LinearLayoutを使用して重みをアニメーション化する代わりに、RelativeLayoutを使用してマージンをアニメーション化します。更新のたびにrequestLayout()を呼び出す必要があるため、これが最善の方法かどうかはわかりません。それは私のすべてのデバイスでもスムーズです。

だから、私はレイアウトをアニメーション化し、フラグメントトランザクションを使用していません。フラグメントCが開いている場合は、戻るボタンを手動で処理して閉じます。

FragmentBは、layout_toLeftOf / ToRightOfを使用して、フラグメントAおよびCに位置合わせします。

アプリがフラグメントCを表示するイベントをトリガーすると、フラグメントCをスライドインし、同時にフラグメントAをスライドアウトします。(2つの別のアニメーション)。逆に、フラグメントAが開くと、同時にCも閉じます。

ポートレートモードまたは小さい画面で、少し異なるレイアウトを使用して、画面上でフラグメントCをスライドさせます。

フラグメントAとCの幅にパーセンテージを使用するには、実行時に計算する必要があると思います...(?)

アクティビティのレイアウトは次のとおりです。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rootpane"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <!-- FRAGMENT A -->
    <fragment
        android:id="@+id/fragment_A"
        android:layout_width="300dp"
        android:layout_height="fill_parent"
        class="com.xyz.fragA" />

    <!-- FRAGMENT C -->
    <fragment
        android:id="@+id/fragment_C"
        android:layout_width="600dp"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        class="com.xyz.fragC"/>

    <!-- FRAGMENT B -->
    <fragment
        android:id="@+id/fragment_B"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="0dip"
        android:layout_marginRight="0dip"
        android:layout_toLeftOf="@id/fragment_C"
        android:layout_toRightOf="@id/fragment_A"
        class="com.xyz.fragB" />

</RelativeLayout>

FragmentCをスライドインまたはスライドアウトするアニメーション:

private ValueAnimator createFragmentCAnimation(final View fragmentCRootView, boolean slideIn) {

    ValueAnimator anim = null;

    final RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) fragmentCRootView.getLayoutParams();
    if (slideIn) {
        // set the rightMargin so the view is just outside the right edge of the screen.
        lp.rightMargin = -(lp.width);
        // the view's visibility was GONE, make it VISIBLE
        fragmentCRootView.setVisibility(View.VISIBLE);
        anim = ValueAnimator.ofInt(lp.rightMargin, 0);
    } else
        // slide out: animate rightMargin until the view is outside the screen
        anim = ValueAnimator.ofInt(0, -(lp.width));

    anim.setInterpolator(new DecelerateInterpolator(5));
    anim.setDuration(300);
    anim.addUpdateListener(new AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer rightMargin = (Integer) animation.getAnimatedValue();
            lp.rightMargin = rightMargin;
            fragmentCRootView.requestLayout();
        }
    });

    if (!slideIn) {
        // if the view was sliding out, set visibility to GONE once the animation is done
        anim.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                fragmentCRootView.setVisibility(View.GONE);
            }
        });
    }
    return anim;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.