Повний робочий зразок сценарію анімації з трьома фрагментами Gmail?


128

TL; DR: Я шукаю повний робочий зразок того, що я буду називати сценарієм "трифрагментної анімації Gmail". Зокрема, ми хочемо почати з двох фрагментів, таких як:

два фрагменти

Після деякої події інтерфейсу (наприклад, натискання на щось у фрагменті B), ми хочемо:

  • Фрагмент А, щоб зісковзнути з екрана вліво
  • Фрагмент B пересуне до лівого краю екрану і зменшиться, щоб зайняти місце, звільнене фрагментом A
  • Фрагмент C, щоб засунути з правого боку екрана і зайняти місце, звільнене фрагментом B

І, натиснувши кнопку НАЗАД, ми хочемо, щоб цей набір операцій було змінено.

Тепер я бачив безліч часткових реалізацій; Я розгляну чотири з них нижче. Окрім того, що вони є неповними, вони мають свої проблеми.


@Reto Meier зробив цю популярну відповідь на той самий основний запитання, вказавши, що ви б використовували setCustomAnimations()з a FragmentTransaction. У сценарії з двома фрагментами (наприклад, ви бачите лише фрагмент A спочатку і хочете замінити його на новий фрагмент B з використанням анімованих ефектів), я повністю згоден. Однак:

  • Оскільки ви можете вказати лише одну "в" та одну "вихідну" анімацію, я не можу побачити, як би ви обробляли всі різні анімації, необхідні для сценарію з трьома фрагментами
  • У <objectAnimator>своєму зразковому коді використовуються жорсткі провідні позиції у пікселях, і це може здатися недоцільним з огляду на різний розмір екрана, але setCustomAnimations()вимагає анімаційних ресурсів, що виключає можливість визначення цих речей на Java
  • Я в занепокоєнні, як аніматори об'єктів для масштабу пов'язуються з такими речами, як android:layout_weightу LinearLayoutдля розподілу місця у відсотках
  • Я в подиві щодо того , як фрагмент C обробляється в самому початку ( GONE? android:layout_weightПро 0? Заздалегідь анімований шкалою від 0? Що - то інше?)

@Roman Nurik вказує, що ви можете анімувати будь-яке майно , включаючи те, яке ви самі визначаєте. Це може допомогти вирішити проблему з провідними позиціями за рахунок створення власного підкласу менеджера макета. Це допомагає деяким, але я все ще здивований рештою рішення Рето.


Автор цього запису на пастбіні показує деякий плідокод, що вразливий, в основному каже, що всі три фрагменти спочатку перебуватимуть у контейнері, а фрагмент C прихований в самому початку hide()операції транзакції. Ми тоді show()C і hide()A, коли відбувається подія інтерфейсу користувача. Однак я не бачу, як це справляється з тим, що B змінює розмір. Він також покладається на те, що ви, мабуть, можете додати кілька фрагментів до одного контейнера, і я не впевнений, чи є це надійною поведінкою протягом тривалого періоду (не кажучи вже про те, що воно повинно ламатись findFragmentById(), хоча я можу з цим жити).


Автор цієї публікації в блозі вказує, що Gmail взагалі не використовує setCustomAnimations(), а натомість безпосередньо використовує аніматори об’єктів ("ви просто змінюєте лівий край кореневого виду + ​​змінюєте ширину правого виду"). Однак це все ще дво фрагментне рішення AFAICT, і реалізація ще раз показала розміри жорстких проводів у пікселях.


Я продовжую підключатися до цього, тому, можливо, коли-небудь відповім на це, але я дуже сподіваюся, що хтось розробив трифрагментне рішення для цього сценарію анімації і зможе опублікувати код (або посилання на нього). Анімації в Android змушують мене витягнути волосся, і ті з вас, хто бачив мене, знають, що це багато в чому безрезультатне починання.


2
Хіба це не щось на зразок опублікованого Романом Гаєм? curious-creature.org/2011/02/22/…
Fernando Gallego

1
@ ferdy182: Він не використовує фрагменти AFAICT. Візуально це правильний базовий ефект, і я можу використати це як іншу точку даних для спроби створити відповідь на основі фрагмента. Дякую!
CommonsWare

1
Лише думка: стандартний додаток електронної пошти має подібну (хоча і не зовсім ідентичну) поведінку на моєму Nexus 7 - який має бути відкритим кодом.
Крістофер Орр

4
У додатку "Електронна пошта" використовується спеціальний "ThreePaneLayout", який анімує положення лівого перегляду та ширину / видимість двох інших представлень, кожен з яких містить відповідний фрагмент.
Крістофер Орр

2
Ей @CommonsWare я не буду турбувати ppl з усією своєю відповіддю тут, але було питання, подібне до вашого, в якому я дав дуже задовільну відповідь. Так, тільки пропозиція перевірити його (також перевірити коментарі): 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отримує вагу лівої панелі як парам, припускаючи, що інший видимий вид має weight=1.

Так new ThreeLayout(context,3) створюється новий перегляд з 3-ма дітьми, а ліва панель має 1/3 всього екрану. Інший вид займає весь наявний простір.

Він обчислює ширину під час виконання. Більш безпечною реалізацією є те, що розміри обчислюються вперше в малюнку (). замість допису ()

Анімації масштабу та перекладу фактично змінюють розмір і переміщують погляд, а не псевдо [масштаб, переміщення]. Зауважте цеfillAfter(true) ніде не використовується.

View2 є правою частиною View1

і

View3 є правою частиною View2

Встановивши ці правила, RelativeLayout піклується про все інше. Анімації змінюють margins(у русі) та[width,height] масштаб

Щоб отримати доступ до кожної дитини (щоб ви могли надути її своїм фрагментом, ви можете зателефонувати

public FrameLayout getLeftLayout() {}

public FrameLayout getMiddleLayout() {}

public FrameLayout getRightLayout() {}

Нижче продемонстровані 2 анімації


Етап1

--- В Екран ----------! ----- ВІН ----

[Вид1] [_____ Перегляд2 _____] [_____ Вид3_____]

Етап2

- ВІН -! -------- IN Екран ------

[View1] [View2] [_____ Перегляд3_____]


1
Поки я ще не застосував аніматора масштабу, я переживаю, що він буде добре працювати для суцільних кольорових полів і менш добре, скажемо так, "реального" вмісту. Результати зміни розміру фрагмента B, щоб відповідати простору фрагмента А, не повинні відрізнятися, ніж якби фрагмент B був розміщений там первісно. Наприклад, AFAIK, аніматор масштабу, буде масштабувати розмір шрифту (намалювавши все в анімації Viewменше), але в Gmail / Email ми не отримуємо менших шрифтів, лише менше слів.
CommonsWare

1
@CommonsWare scaleAnimation - це лише абстрактна назва. Насправді масштабування не відбувається. Він фактично змінює ширину подання. Перевірте джерело. LayoutResizer може бути кращою назвою для цього.
слабкий провід

1
Це не моя інтерпретація джерела навколо scaleXзначення View, хоча я можу неправильно трактувати речі.
CommonsWare

1
@CommonsWare перевірте цей github.com/weakwire/3PaneLayout/blob/master/src/com/example/… . Єдина причина, по якій я продовжую анімацію, - це отримати доступ до інтерпольованого часу. p.width = (int) ширина; робить усі чари, і це не масштабX. Це фактичні розміри подання, які змінюються протягом зазначеного часу.
слабкий провід

2
Ви на відео показуєте хороший кадр, але перегляди, використані у вашому прикладі, дуже прості. Зробити запитLayout () під час анімації дуже дорого. Особливо, якщо діти складні (наприклад, ListView). Це, ймовірно, призведе до похмурої анімації. Додаток GMail не викликає requestLayout. Натомість інший менший вигляд розміщується на середній панелі безпосередньо перед запуском анімації.
Тьєррі-Димитрі Рой

31

Гаразд, ось моє власне рішення, отримане з програми AOSP по електронній пошті, за пропозицією @ Крістофера в коментарях до питання.

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

@ рішення slawire нагадує моє, хоча він використовує класичні, Animationа не аніматори, і він використовує RelativeLayoutправила для забезпечення позиціонування. З точки зору щедрості він, ймовірно, отримає щедрість, якщо хтось ще не може відповісти на це.


Якщо коротко, то ThreePaneLayoutв цьому проекті є LinearLayoutпідклас, призначений для роботи в ландшафті з трьома дітьми. Ці дитячі ширини можна встановити в XML-макеті будь-якими бажаними способами - я показую, використовуючи ваги, але ви можете мати конкретну ширину, встановлену ресурсами розміру чи будь-яким іншим. Третя дитина - фрагмент 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- який не має будь - яких конкретних зв'язків з фрагментами - три частини є left, middleі right. У той час ви телефонуєте hideLeft(), ми записуємо розміри leftі middleі обнуляти будь вагові коефіцієнти , які були використані на будь-якому з трьох, так що ми можемо повністю контролювати розміри. У момент часу hideLeft(), ми встановлюємо розмір, rightякий буде початковим розміром middle.

Анімації двоякі:

  • Використовуйте a, ViewPropertyAnimatorщоб здійснити переклад трьох віджетів зліва на ширину left, використовуючи апаратний рівень
  • Використовуйте ObjectAnimatorна користувальницькій псевдо властивості, middleWidthщоб змінити middleширину від того, з чого він починався, на початкову ширинуleft

(можливо, краще використовувати AnimatorSetі ObjectAnimatorsдля всіх цих, хоча це працює на даний момент)

(можливо також, що middleWidth ObjectAnimatorзаперечує значення апаратного шару, оскільки це вимагає досить постійної інвалідизації)

( Однозначно можливо, у мене все ще є прогалини в моєму розумінні анімації, і що мені подобаються дужки в дужках)

Чистий ефект полягає в тому, що він leftковзає з екрана, middleковзає у вихідне положення та розмір leftта rightперекладається прямо ззаду middle.

showLeft() просто обертає процес, з тим самим поєднанням аніматорів, просто з оберненими напрямками.

У діяльності використовується параметр a, ThreePaneLayoutщоб вмістити пару ListFragmentвіджетів та a Button. Вибір чогось у лівому фрагменті додає (або оновлює вміст) середнього фрагмента. Вибір чогось у середньому фрагменті встановлює заголовки Button, а також виконується hideLeft()на ThreePaneLayout. Натискання НАЗАД, якщо ми заховали ліву сторону, виконається showLeft(); в іншому випадку BACK завершує роботу. Оскільки це не використовує FragmentTransactionsдля впливу на анімацію, ми затримуємося керуючи цим "зворотним стеком".

Проект, зв'язаний вище, використовує натурні фрагменти та нативну рамку аніматора. У мене є ще одна версія того ж проекту, яка використовує резервний портфель фрагментів Android Support та NineOldAndroids :

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

Зворотний порт працює чудово на Kindle Fire 1-го покоління, хоча анімація трохи хитра, враховуючи нижчі технічні характеристики та відсутність підтримки апаратного прискорення. Обидві реалізації виглядають плавно на Nexus 7 та інших планшетах поточного покоління.

Я, безумовно, відкритий для ідей, як вдосконалити це рішення чи інші рішення, які пропонують явні переваги перед тим, що я тут зробив (або те, що використовував @weakwire).

Ще раз дякую всім, хто зробив свій внесок!


1
Привіт! Дякую за рішення, але я думаю, додавання v.setLayerType () іноді робить щось трохи повільніше. Просто видаліть ці рядки (і слухача теж) Це, принаймні, стосується пристроїв Android 3+, які я лише спробував на Galaxy Nexus, і без цих ліній це працює набагато краще
Євгеній Наку

1
"Обидві реалізації виглядають плавними на Nexus 7 та інших планшетах поточного покоління." Я вірю тобі; але я тестую вашу реалізацію з нативної ObjectAnimator на Nexus 7, і вона трохи відстає. Які були найімовірніші причини? У мене вже є hardwareAccelerated: вірно в AndroidMAnifest. Я справді не знаю, у чому може бути проблема. Я також спробував варіант DanielGrech, і він відстає від того ж. Я бачу відмінності в анімації (його анімація заснована на вазі), але я не бачу, чому вона буде відставати в обох випадках.
Богдан Зурак

1
@Andrew: "це трохи відстає" - я не впевнений, як ви тут використовуєте дієслово "відставати". Для мене "відставання" передбачає конкретну ціль для порівняння. Так, наприклад, анімація, прив'язана до руху, що рухається, може відставати від руху пальця. У цьому випадку все спрацьовує дотиками, і тому мені незрозуміло, що анімація може відставати. Гадати, що, можливо, «трохи відстає», це означає «відчуваєш себе млявим», я цього не бачив, але не стверджую, що має сильне естетичне почуття, і тому те, що може бути для мене прийнятною анімацією, може бути для вас неприйнятним.
CommonsWare

2
@CommonsWare: Я склав ваш код з 4.2, чудова робота. Коли я ListViewнатиснув на середину і швидко натиснув кнопку назад і повторив, тоді весь макет перемістився вправо. На ньому почали відображатися стовпці додаткового простору зліва. Така поведінка запроваджена тому, що я не дозволив ListViewзавершити першу анімацію (розпочату натисканням на середину ) і натиснув кнопку назад. Я зафіксував його шляхом заміни translationXByз translationXв translateWidgetsспособі і translateWidgets(leftWidth, left, middle, right);з translateWidgets(0, left, middle, right);в showLeft()методі.
M-WaJeEh

2
Я також замінив requestLayout();з middle.requestLayout();в setMiddleWidth(int value);методі. Це може не вплинути на продуктивність, оскільки я думаю, що GPU все-таки зробить повний пропуск макета, будь-які коментарі?
M-WaJeEh

13

Ми створили бібліотеку під назвою PanesLibrary, яка вирішує цю проблему. Це навіть більш гнучко, ніж те, що пропонувалося раніше, тому що

  • Кожна панель може мати динамічний розмір
  • Він дозволяє використовувати будь-яку кількість панелей (не лише 2 або 3)
  • Фрагменти всередині панелей правильно зберігаються при зміні орієнтації.

Перевірити це можна тут: https://github.com/Mapsaurus/Android-PanesLibrary

Ось демонстрація: http://www.youtube.com/watch?v=UA-lAGVXoLU&feature=youtu.be

Це в основному дозволяє легко додавати будь-яку кількість панелей динамічного розміру та додавати фрагменти до цих панелей. Сподіваюся, вам це стане в нагоді! :)


6

Створюючи один із прикладів, з якими ви пов’язані ( 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;
        }
    }
}

Також цей приклад не використовує FragmentTransaction для досягнення анімації (скоріше, він оживляє перегляди, до яких додаються фрагменти), тому вам потрібно буде зробити всі транзакції з backstack / fragment самостійно, але порівняно із зусиллями, щоб анімація працювала добре, це не здається поганим компромісом :)

Жахливе відео з низькою роздільною здатністю в дії: http://youtu.be/Zm517j3bFCo


1
Ну, Gmail безперечно робить переклад того , що я описую як фрагмент A. Я б турбуватися , що зниження його ваги до 0 може ввести візуальні артефакти (наприклад, TextViewз wrap_contentрозтягуванням себе вертикально , як анімація триває). Однак, можливо, анімація ваги полягає в тому, як вони змінюють розмір того, що я назвав Фрагмент В. Дякую!
CommonsWare

1
Не хвилюйтесь! Це добре, але це, ймовірно, спричинить візуальні артефакти залежно від того, які дитячі погляди є у "Фрагменті А". Нехай сподівається, що хтось знайде більш солідний спосіб зробити це
DanielGrech

5

Тут не використовуються фрагменти .... Це власна розкладка з 3 дітьми. Коли ви натискаєте на повідомлення, ви компенсуєте 3 дитячі програми offsetLeftAndRight()та аніматора.

У JellyBeanналаштуваннях "Параметри розробника" можна включити "Показати межі макета". Коли анімація слайдів завершена, ви все ще можете побачити, що ліве меню все ще є, але під середньою панеллю.

Це схоже на меню додатка Cyry Mottier Fly-in , але з 3 елементами замість 2.

Крім того, ViewPagerтретій дитина - це ще одна ознака такої поведінки: ViewPagerзазвичай використовуються фрагменти (я знаю, що їм не доведеться, але я ніколи не бачив іншої реалізації цього Fragment), а оскільки ви не можете використовувати Fragmentsвсередині Fragmentтрьох дітей ймовірно, не фрагменти….


Я ніколи раніше не використовував "Показати межі макета" - це дуже корисно для таких програм із закритим кодом (спасибі!). Здається, що те, що я описав як Фрагмент А, є перекладом поза екраном (ви можете бачити його ковзання правого краю праворуч ліворуч), перш ніж вискочити назад під те, що я описав як Фрагмент B. Це відчувається як TranslateAnimation, хоча я впевнені, що існують способи використання аніматорів для досягнення тих же цілей. Мені доведеться провести кілька експериментів з цього приводу. Дякую!
CommonsWare

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.