[解決済み] Gmailの3分割アニメーションシナリオの完全動作サンプル?
質問
TL;DR:を探しています。 完全な動作サンプル Gmail の 3 つのフラグメントのアニメーションと呼ぶシナリオの完全なサンプルを探しています。具体的には、次のような 2 つのフラグメントから開始したいと思います。
何らかのUIイベント(例:フラグメントBの何かをタップした)が発生したときに
- フラグメント A を画面左側にスライドさせる
- フラグメント B が画面の左端に移動し、フラグメント A が移動した場所を占めるように縮小する。
- フラグメント C が画面右側からスライドして、フラグメント B の位置に移動する。
そして、BACKボタンが押されたら、その一連の動作を反転させるようにします。
さて、私は多くの部分的な実装を見てきました。以下にそのうちの 4 つをレビューします。不完全であることに加え、どれも問題があります。
レトマイヤーの投稿
この人気のある答え
を使い、同じ基本的な質問に対して
setCustomAnimations()
と
FragmentTransaction
. 2つのフラグメントのシナリオ(例えば、最初はフラグメントAしか見えず、アニメーション効果を使って新しいフラグメントBに置き換えたい)については、私は完全に同意します。しかし
- 1 つの "in"、および 1 つの "out"アニメーションしか指定できないので、3 つのフラグメント シナリオに必要なすべての異なるアニメーションをどのように処理するのかがわかりません。
-
その
<objectAnimator>
はピクセル単位で固定された位置を使用しており、さまざまな画面サイズを考慮すると実用的ではありません。setCustomAnimations()
はアニメーション リソースを必要とし、Java でこれらのことを定義する可能性を排除しています。 -
スケール用のオブジェクトのアニメーターが、以下のようなものとどのように結びつくのか、途方に暮れています。
android:layout_weight
の中にLinearLayout
パーセント単位でスペースを割り当てるための -
フラグメントCが最初にどのように処理されるのか、途方に暮れています(
GONE
?android:layout_weight
の0
?スケール0にあらかじめアニメーション化されている?)
@Roman Nurik さんの指摘によると を使用すると、任意のプロパティをアニメーション化できます。 をアニメーション化することができます。これは、独自のカスタムレイアウトマネージャーのサブクラスを作成する代償として、ハードワイヤードの位置の問題を解決するのに役立ちます。これは多少役立ちますが、Reto のソリューションの残りの部分については、まだ困惑しています。
の作者は
このpastebinのエントリ
を見ると、基本的に 3 つのフラグメントはすべてコンテナ内に存在し、フラグメント C は最初に
hide()
トランザクション操作によって隠されます。次に
show()
C と
hide()
AをUIイベントが発生したときに表示します。しかし、Bのサイズが変わることをどう処理するのかがわからない。また、同じコンテナに複数のフラグメントを追加できるという事実にも依存しており、それが長期的に信頼できる動作なのかどうかもわかりません(言うまでもなく、それは
findFragmentById()
を壊すことは言うまでもありません。)
の作者は
このブログの記事
は、Gmail が
setCustomAnimations()
をまったく使用せず、代わりにオブジェクト アニメーターを直接使用していることがわかります ("ルート ビューの左マージンを変更 + 右ビューの幅を変更するだけです")。しかし、これはまだ AFAICT の 2 分割のソリューションであり、示された実装は再び寸法をピクセルでハードワイヤーしています。
しかし、誰かがこのアニメーション シナリオのための 3 分割ソリューションを開発し、コード (またはそのリンク) を投稿できることを強く望んでいます。Android でのアニメーションは、私の髪を引っ張り出したくなりますし、私を見た人は、これがほとんど実りのない努力であることを知っています。
どのように解決するのですか?
OK、質問のコメントにある@Christopherの提案に従って、メールAOSPアプリから派生した私自身の解決策です。
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane
Weakwire のソリューションは、私のソリューションと似ていますが、彼は古典的な
Animation
を使い、アニメーターではなく
RelativeLayout
ルールを使って位置決めを強制しています。懸賞金の観点からは、より巧妙な解決策を持つ他の誰かがまだ答えを投稿しない限り、彼はおそらく懸賞金を得ることになるでしょう。
一言で言えば
ThreePaneLayout
をそのプロジェクトでは
LinearLayout
のサブクラスで、3 つの子要素を持つランドスケープで動作するように設計されています。これらの子の幅は、レイアウト XML で好きなように設定できます -- 私はウェイトを使っていますが、ディメンションリソースなどで特定の幅を設定することも可能です。3番目の子(質問ではフラグメントC)は幅が0であるべきです。
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つのピースは
left
,
middle
そして
right
. を呼び出した時点で
hideLeft()
のサイズを記録します。
left
と
middle
の3つに使われていた重みをゼロにすることで、サイズを完全に制御することができます。の時点では
hideLeft()
の時点で
right
の元のサイズになるように
middle
.
アニメーションは2つあります。
-
を使う。
ViewPropertyAnimator
の幅だけ左に移動させます。left
ハードウェア レイヤーを使用して -
を使用します。
ObjectAnimator
のカスタム擬似プロパティでmiddleWidth
を変更するためにmiddle
の幅を変更し、元の幅であるleft
(を使用するのが良いという可能性もあります)。
AnimatorSet
と
ObjectAnimators
を使うこともできますが、今のところこれで大丈夫です。)
(また
middleWidth
ObjectAnimator
がハードウェア層の値を否定することも可能ですが、その場合はかなり継続的な無効化が必要なので)
(それは 間違いなく 私はまだアニメーションの理解力に欠けている可能性があり、また、私は括弧つきの文章が好きなのです)
正味のところ
left
が画面の外にスライドすることです。
middle
の元の位置とサイズにスライドします。
left
であり
right
の右後ろに翻訳されます。
middle
.
showLeft()
は、同じアニメーターの組み合わせで、ただ方向を逆にしただけのものです。
このアクティビティでは
ThreePaneLayout
のペアを保持するために
ListFragment
ウィジェットと
Button
. 左のフラグメントで何かを選択すると、中央のフラグメントが追加 (または内容の更新) されます。中央のフラグメントで何かを選択すると、ウィジェットのキャプションが設定されます。
Button
のキャプションを設定し、さらに
hideLeft()
を実行します。
ThreePaneLayout
. BACK を押すと、左側を隠した場合、実行されるのは
showLeft()
を実行します。そうでなければ、BACKはアクティビティを終了します。これは
FragmentTransactions
を使用しないので、私たちは自分自身でそのバックスタックを管理することになります。
上記のリンク先のプロジェクトでは、ネイティブ フラグメントとネイティブ アニメーター フレームワークを使用しています。同じプロジェクトの別のバージョンでは、Android Support fragments バックポートおよび NineOldAndroids をアニメーションに使用しています。
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC
バックポートは第 1 世代 Kindle Fire で問題なく動作しますが、ハードウェア スペックが低く、ハードウェア アクセラレーション サポートがないため、アニメーションは少しぎこちなくなります。両方の実装は、Nexus 7 やその他の現世代のタブレットでスムーズに動作するようです。
このソリューションをどのように改善するか、または私がここで行ったこと (あるいは @weakwire が使用したこと) よりも明確な利点を提供する他のソリューションについてのアイデアをお待ちしています。
貢献してくれたみなさん、本当にありがとうございました!
関連
-
[解決済み] 「KotlinとAndroidで「パラメータTを推測するのに十分な情報がありません。
-
[解決済み] XMLで矩形を描画できますか?
-
[解決済み] TabLayoutに対応したandroidデザインでタブテキストのフォントを変更する
-
[解決済み] Studio 3.4 をアップデートしたら、引数の leftShift() メソッドが見つかりませんでした。
-
[解決済み] 通知をクリックした後にアプリケーションを開く
-
[解決済み] 非ActivityクラスでContextを取得する [重複].
-
[解決済み] PendingIntentの "requestCode "は何に使うのですか?
-
[解決済み] 開発者コンソールでのベータ版/アルファ版テストについての説明が必要です。
-
[解決済み] BackStackでFragment Animationを逆再生するには?
-
[解決済み] フラグメントからツールバーを取得する方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】フラグメント間の遷移をアニメーション化する
-
[解決済み】Android Fragmentとアニメーション
-
[解決済み] SDカードからファイルを削除する方法を教えてください。
-
[解決済み] RecyclerView.Stateを使って、RecyclerViewのスクロール位置を保存するには?
-
[解決済み] AndroidにおけるViewPager2の適切な実装
-
[解決済み] ArrayAdapter<myClass> の使用方法
-
[解決済み] Android ConstraintLayout - あるビューを別のビューの上に配置する
-
[解決済み] アプリ内課金テスト:android.test.purchased already owned
-
[解決済み] Android Studioの「未実装メソッドの追加」機能
-
[解決済み] 非推奨のandroid.support.v4.app.ActionBarDrawerToggleの置き換え方法