Вкладені фрагменти зникають під час анімації переходу


100

Ось сценарій: активність містить фрагмент A, який , в свою чергу , використовує getChildFragmentManager()для додавання фрагментів A1і A2в його onCreateнаступним чином:

getChildFragmentManager()
  .beginTransaction()
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

Поки так добре, все працює так, як очікувалося.

Потім ми виконуємо таку транзакцію в діяльності:

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .replace(R.id.fragmentHolder, new FragmentB())
  .addToBackStack(null)
  .commit()

Під час переходу enterанімації для фрагмента Bзапускаються правильно, але фрагменти A1 та A2 повністю зникають . Коли ми повертаємо транзакцію за допомогою кнопки Назад, вони ініціалізуються належним чином і нормально відображаються під час popEnterанімації.

У моєму короткому тестуванні воно стало дивнішим - якщо я встановив анімації для дочірніх фрагментів (див. Нижче), exitанімація запускається з перервами, коли ми додаємо фрагментB

getChildFragmentManager()
  .beginTransaction()
  .setCustomAnimations(enter, exit)
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

Ефект, який я хочу досягти, простий - я хочу, щоб exit(або повинна бути popExit?) Анімація на фрагменті A(anim2) запускалася, анімувавши весь контейнер, включаючи його вкладених дітей.

Чи є спосіб досягти цього?

Редагувати : Будь ласка, знайдіть тестовий випадок тут

Edit2 : Дякую @StevenByle за те, що він наполягав на тому, щоб я намагався стати зі статичними анімаціями. Мабуть, ви можете встановлювати анімацію на основі операції (не глобальну для всієї транзакції), а це означає, що діти можуть мати безстроковий набір статичної анімації, тоді як їхній батько може мати іншу анімацію, і вся справа може бути здійснена однією транзакцією . Дивіться дискусію нижче та оновлений проект тестового випадку .


Що R.id.fragmentHolderстосується A, A1, A2 тощо?
CommonsWare

fragmentHolder - ідентифікатор у макеті діяльності, фрагмент {Один, два} Власник - у макеті фрагмента A. Усі троє виразні. Фрагмент A спочатку додавали у фрагментHolder (тобто фрагмент B замінює фрагмент A).
Делян

Тут я створив зразок проекту: github.com/BurntBrunch/NestedFragmentsAnimationsTest , в сховищі також є apk. Це дійсно дратівлива помилка, і я шукаю спосіб подолати її (якщо припустити, що це не в моєму коді).
Делян

Зараз я знаю трохи більше про це питання. Причина того, що фрагменти зникають, полягає в тому, що діти обробляють події життєвого циклу перед батьком. По суті, A1 і A2 видаляються перед A, і оскільки у них немає анімації, вони різко зникають. Спосіб дещо пом'якшити це - явно видалити A1 та A2 в транзакції, яка замінює A. Таким чином, вони анімують, коли вони виходять, однак швидкість їх анімації в квадраті, оскільки батьківський контейнер також анімація. Рішення, яке не створює цей артефакт, буде вдячне.
Делян

Зміна (заміна фрагменту стартера), яку ви згадуєте у запитанні, є справжньою, яку ви хочете зробити, або це лише приклад? Ви назвете changeFragmentметод лише один раз?
Лукспрог

Відповіді:


36

Щоб уникнути того, як користувач бачить вкладені фрагменти, коли батьківський фрагмент видаляється / замінюється в транзакції, ви можете "імітувати" ті фрагменти, які все ще присутні, надаючи їх зображення, як вони з'являлися на екрані. Це зображення буде використовуватися як фон для контейнера вкладених фрагментів, тому навіть якщо погляди вкладеного фрагмента відійдуть, зображення буде імітувати їх присутність. Крім того, я не бачу втрати інтерактивності з поглядами на вкладений фрагмент як проблему, тому що я не думаю, що ви хотіли б, щоб користувач діяв на них, коли вони просто видаляються (можливо, як дії користувача як Ну).

Я зробив невеликий приклад із налаштуванням фонового зображення (щось базове).


1
Я вирішив нагородити вас винагородою, оскільки це було рішення, яке я в кінцевому підсумку використав. Дякую вам за ваш час!
Делян

16
Нічого собі, такий брудний. Довжина, яку ми розробники Android мусимо продемонструвати лише на деяку виразність
Дін Уайлд

1
У мене є вкладка Viewpager From Second, я замінюю інший фрагмент, і коли я натискаю на нього, мені потрібно показати другу вкладку viewpager, вона відкривається, але вона показує порожню сторінку. Я спробував те, що ви запропонували у вищенаведеній темі, але все одно це те саме.
Harish

Проблема залишається, коли користувач повертається, як писав @Harish
Ewoks,

1
Нічого серйозно? його 2018 рік і це все-таки річ? :(
Archie G. Quiñones

69

Тому, мабуть, існує багато різних способів вирішення цього питання, але, виходячи з відповіді @ Jayd16, я думаю, що я знайшов досить міцне рішення, що все ще дозволяє виконувати власну анімацію переходу на дочірні фрагменти, і не вимагає цього робити растровий кеш макета.

Майте BaseFragmentклас, який розширює Fragment, і змушуйте усі ваші фрагменти розширити цей клас (не лише дочірні фрагменти).

У цьому BaseFragmentкласі додайте наступне:

// Arbitrary value; set it to some reasonable default
private static final int DEFAULT_CHILD_ANIMATION_DURATION = 250;

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    final Fragment parent = getParentFragment();

    // Apply the workaround only if this is a child fragment, and the parent
    // is being removed.
    if (!enter && parent != null && parent.isRemoving()) {
        // This is a workaround for the bug where child fragments disappear when
        // the parent is removed (as all children are first removed from the parent)
        // See https://code.google.com/p/android/issues/detail?id=55228
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else {
        return super.onCreateAnimation(transit, enter, nextAnim);
    }
}

private static long getNextAnimationDuration(Fragment fragment, long defValue) {
    try {
        // Attempt to get the resource ID of the next animation that
        // will be applied to the given fragment.
        Field nextAnimField = Fragment.class.getDeclaredField("mNextAnim");
        nextAnimField.setAccessible(true);
        int nextAnimResource = nextAnimField.getInt(fragment);
        Animation nextAnim = AnimationUtils.loadAnimation(fragment.getActivity(), nextAnimResource);

        // ...and if it can be loaded, return that animation's duration
        return (nextAnim == null) ? defValue : nextAnim.getDuration();
    } catch (NoSuchFieldException|IllegalAccessException|Resources.NotFoundException ex) {
        Log.w(TAG, "Unable to load next animation from parent.", ex);
        return defValue;
    }
}

Це, на жаль, вимагає роздумів; однак, оскільки це вирішення стосується бібліотеки підтримки, ви не ризикуєте змінити базову реалізацію, якщо не оновите свою бібліотеку підтримки. Якщо ви створюєте бібліотеку підтримки з джерела, ви можете додати до неї наступний ідентифікатор ресурсу анімації Fragment.javaта усунути потребу в роздумах.

Це рішення усуває необхідність "здогадуватися" про тривалість анімації батьків (щоб анімація "не роби нічого" матиме таку ж тривалість, як анімація виходу з батьків) та дозволяє все-таки робити власні анімації на дочірніх фрагментах (наприклад, якщо ви ' повторно поміняйте дочірні фрагменти різними анімаціями).


5
Це моє улюблене рішення в нитці. Не вимагає растрової карти, не вимагає додаткового коду в дочірньому фрагменті і не дуже просочує інформацію від батьків до дочірнього фрагмента.
Якобхіфен

1
@EugenPechanec Вам потрібен nextAnim з батьківського фрагмента - а не той, який знаходиться у дитини. У цьому вся суть.
Кевін Коппок

1
На жаль, цей підхід спричиняє витік пам’яті для дочірніх фрагментів і неявно для батьківського фрагмента, а також для версій Android під Lollipop :(
Cosmin

8
Дякую за це дуже корисне рішення, однак для роботи з поточною бібліотекою підтримки потрібне невелике оновлення (27.0.2, не знаю, яка версія порушила цей код). mNextAnimзараз знаходиться всередині mAnimationInfoоб'єкта. Ви можете отримати доступ до нього так:Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo"); animInfoField.setAccessible(true); Object animationInfo = animInfoField.get(fragment); Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
David Lericolais

5
@DavidLericolais, хотів би ще додати ще один рядок коду після вашого. val nextAnimResource = nextAnimField.getInt(animationInfo);замінити рядокint nextAnimResource = nextAnimField.getInt(fragment);
tingyik90

32

Мені вдалося придумати досить чисте рішення. IMO - це найменш хакі, і хоча це технічно рішення "намалювати растрову карту", принаймні його абстрагується фрагментом lib.

Переконайтесь, що вашій дитині фрагменти замінюють батьківський клас із цим:

private static final Animation dummyAnimation = new AlphaAnimation(1,1);
static{
    dummyAnimation.setDuration(500);
}

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if(!enter && getParentFragment() != null){
        return dummyAnimation;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}

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

Єдине питання, про яке я можу придумати, - це відстеження цієї тривалості. Я, можливо, міг би встановити його на велике число, але я боюся, що це може мати проблеми з продуктивністю, якщо його все-таки малюватиме таку анімацію.


Це допомагає, дякую. Значення тривалості часу не має значення
iscariot

Найчистіше рішення поки що
Ліран Коен

16

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

getChildFragmentManager().beginTransaction()
        .setCustomAnimations(R.anim.none, R.anim.none, R.anim.none, R.anim.none)
        .add(R.id.container, nestedFragment)
        .commit();

Xml для R.anim.none (Час анімації моїх батьків на вхід / вихід - 250 мс)

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0" android:toXDelta="0" android:duration="250" />
</set>

Я робив щось дуже схоже, але використовував "шоу", а не "додавати" при оновленні дитини. Я також додав "getChildFragmentManager (). ExecutePendingTransaction ()", хоча я не впевнений, що це вкрай необхідно. Це рішення працює чудово, проте не вимагає "надання зображення" Фрагменту, як деякі з них пропонують.
Брайан Єнчо

Це було чудово. Однак я отримував затримку при переключенні фрагментів своєї дитини. щоб уникнути цього, просто встановіть 2-й параметр без аніме:fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
ono

7

Наскільки я розумію, це може бути не в змозі повністю вирішити вашу проблему, але, можливо, це відповідатиме чужим потребам, ви можете додати enter/ exitта popEnter/ popExitанімацію для своїх дітей Fragment, які насправді не рухають / не оживляють Fragment. Поки анімації мають таку саму тривалість / зміщення, як і їхні батьківські Fragmentанімації, вони з'являться, щоб рухатися / анімувати разом з анімацією батьків.


1
Я нагородив суму винагороди Лукспрогу, оскільки його рішення працює універсально. Я спробував трюк із статичною анімацією (тривалість насправді не має значення - як тільки батько не зникне, погляди очевидно зникають), але вони не спрацьовували у всіх можливих випадках (див. Мої коментарі під запитанням). Крім того, такий підхід просочується абстракцією, оскільки батько фрагменту з дітьми повинен знати про цей факт і вживати додаткових кроків для налаштування анімації для дітей. У будь-якому випадку, дуже дякую за ваш час!
Делян

Погоджено, це швидше рішення, ніж водонепроникний розчин, і його можна вважати делікатним. Але це спрацює для простих випадків.
Стівен Біле

4

це можна зробити в дочірньому фрагменті.

@Override
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
    if (true) {//condition
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(getView(), "alpha", 1, 1);
        objectAnimator.setDuration(333);//time same with parent fragment's animation
        return objectAnimator;
    }
    return super.onCreateAnimator(transit, enter, nextAnim);
}

Дякую! Можливо, не найкраще, але можливо найпростіше рішення.
помилка56

2

@@@@@@@@@@@@@@@@@@@@@@@@@@@

EDIT: Я вирішив не виконати це рішення, оскільки виникли інші проблеми. Нещодавно на площі вийшли 2 бібліотеки, які замінюють фрагменти. Я б сказав, що це насправді може бути кращою альтернативою, ніж намагатися зламати фрагменти, робити щось google, не хоче, щоб вони це робили.

http://corner.squareup.com/2014/01/mortar-and-flow.html

@@@@@@@@@@@@@@@@@@@@@@@@@@@

Я подумав, що запропонував би це рішення, щоб допомогти людям, які мають цю проблему в майбутньому. Якщо ви простежите за початковою розмовою плакатів з іншими людьми і подивитесь на код, який він опублікував, ви побачите, що оригінальний плакат врешті-решт прийде до висновку про використання анімації без операції на дочірніх фрагментах під час анімації батьківського фрагмента. Це рішення не є ідеальним, оскільки воно змушує вас відслідковувати всі дочірні фрагменти, які можуть бути громіздкими при використанні ViewPager з FragmentPagerAdapter.

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

Існує маса способів цього здійснити. Я вирішив використовувати синглтон, і я називаю це ChildFragmentAnimationManager. В основному він буде відслідковувати дочірній фрагмент для мене на основі його батька і застосовуватиме неоперативну анімацію для дітей, коли її запитують.

public class ChildFragmentAnimationManager {

private static ChildFragmentAnimationManager instance = null;

private Map<Fragment, List<Fragment>> fragmentMap;

private ChildFragmentAnimationManager() {
    fragmentMap = new HashMap<Fragment, List<Fragment>>();
}

public static ChildFragmentAnimationManager instance() {
    if (instance == null) {
        instance = new ChildFragmentAnimationManager();
    }
    return instance;
}

public FragmentTransaction animate(FragmentTransaction ft, Fragment parent) {
    List<Fragment> children = getChildren(parent);

    ft.setCustomAnimations(R.anim.no_anim, R.anim.no_anim, R.anim.no_anim, R.anim.no_anim);
    for (Fragment child : children) {
        ft.remove(child);
    }

    return ft;
}

public void putChild(Fragment parent, Fragment child) {
    List<Fragment> children = getChildren(parent);
    children.add(child);
}

public void removeChild(Fragment parent, Fragment child) {
    List<Fragment> children = getChildren(parent);
    children.remove(child);
}

private List<Fragment> getChildren(Fragment parent) {
    List<Fragment> children;

    if ( fragmentMap.containsKey(parent) ) {
        children = fragmentMap.get(parent);
    } else {
        children = new ArrayList<Fragment>(3);
        fragmentMap.put(parent, children);
    }

    return children;
}

}

Далі вам потрібно мати клас, який розширює фрагмент, який розширюють усі ваші фрагменти (принаймні, ваші дочірні фрагменти). У мене вже був цей клас, і я називаю його BaseFragment. Коли створено подання фрагментів, ми додаємо його до ChildFragmentAnimationManager і видаляємо його, коли він знищується. Ви можете зробити це onAttach / Detach або інші методи узгодження у послідовності. Моя логіка вибору «Створити / знищити перегляд» полягала в тому, що якщо у фрагмента немає перегляду, я не переймаюся анімацією, щоб продовжувати його бачити. Цей підхід також повинен краще працювати з ViewPagers, який використовує фрагменти, оскільки ви не будете відслідковувати кожен окремий фрагмент, який утримує FragmentPagerAdapter, а лише 3.

public abstract class BaseFragment extends Fragment {

@Override
public  View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    Fragment parent = getParentFragment();
    if (parent != null) {
        ChildFragmentAnimationManager.instance().putChild(parent, this);
    }

    return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onDestroyView() {
    Fragment parent = getParentFragment();
    if (parent != null) {
        ChildFragmentAnimationManager.instance().removeChild(parent, this);
    }

    super.onDestroyView();
}

}

Тепер, коли всі ваші фрагменти зберігаються в пам’яті батьківським фрагментом, ви можете викликати анімацію на них так, і ваші дочірні фрагменти не зникнуть.

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ChildFragmentAnimationManager.instance().animate(ft, ReaderFragment.this)
                    .setCustomAnimations(R.anim.up_in, R.anim.up_out, R.anim.down_in, R.anim.down_out)
                    .replace(R.id.container, f)
                    .addToBackStack(null)
                    .commit();

Крім того, просто так у вас є ось файл no_anim.xml, який знаходиться у вашій папці res / anim:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/linear_interpolator">
    <translate android:fromXDelta="0" android:toXDelta="0"
        android:duration="1000" />
</set>

Знову ж таки, я не вважаю, що це рішення є ідеальним, але це набагато краще, ніж для кожного примірника у вас є дочірній фрагмент, реалізуючи спеціальний код у батьківському фрагменті для відстеження кожної дитини. Я там був, і це не весело.


1

Я думаю, що я знайшов краще рішення цієї проблеми, ніж знімок поточного фрагмента до растрової карти, як запропонував Luksprog.

Трюк полягає в тому, щоб приховати фрагмент, що видаляється або від'єднується, і лише після того, як анімація буде виконана, фрагмент видаляється або відривається в його власній транзакції з фрагментом.

Уявіть, що у нас є, FragmentAі FragmentBобидва з фрагментами. Тепер, коли ви зазвичай робите:

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .add(R.id.fragmentHolder, new FragmentB())
  .remove(fragmentA)    <-------------------------------------------
  .addToBackStack(null)
  .commit()

Натомість ви робите

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .add(R.id.fragmentHolder, new FragmentB())
  .hide(fragmentA)    <---------------------------------------------
  .addToBackStack(null)
  .commit()

fragmentA.removeMe = true;

Тепер для реалізації фрагменту:

public class BaseFragment extends Fragment {

    protected Boolean detachMe = false;
    protected Boolean removeMe = false;

    @Override
    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
        if (nextAnim == 0) {
            if (!enter) {
                onExit();
            }

            return null;
        }

        Animation animation = AnimationUtils.loadAnimation(getActivity(), nextAnim);
        assert animation != null;

        if (!enter) {
            animation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    onExit();
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                }
            });
        }

        return animation;
    }

    private void onExit() {
        if (!detachMe && !removeMe) {
            return;
        }

        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        if (detachMe) {
            fragmentTransaction.detach(this);
            detachMe = false;
        } else if (removeMe) {
            fragmentTransaction.remove(this);
            removeMe = false;
        }
        fragmentTransaction.commit();
    }
}

Чи не popBackStack викликає помилку, тому що він намагається показати фрагмент, який був відокремлений?
Олександр

1

У мене виникла та сама проблема з фрагментом карти. Він продовжував зникати під час анімації виходу його містить фрагмент. Вирішення проблеми полягає в тому, щоб додати анімацію для фрагмента дочірньої карти, яка буде зберігати його видимим під час анімації виходу з батьківського фрагмента. Анімація дочірнього фрагмента утримує альфа на 100% протягом його тривалості.

Анімація: res / animator / Keep_child_fragment.xml

<?xml version="1.0" encoding="utf-8"?>    
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:propertyName="alpha"
        android:valueFrom="1.0"
        android:valueTo="1.0"
        android:duration="@integer/keep_child_fragment_animation_duration" />
</set>

Потім анімація застосовується, коли фрагмент карти додається до батьківського фрагмента.

Батьківський фрагмент

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.map_parent_fragment, container, false);

    MapFragment mapFragment =  MapFragment.newInstance();

    getChildFragmentManager().beginTransaction()
            .setCustomAnimations(R.animator.keep_child_fragment, 0, 0, 0)
            .add(R.id.map, mapFragment)
            .commit();

    return view;
}

Нарешті, тривалість анімації дочірнього фрагмента встановлюється у файлі ресурсів.

значень / integers.xml

<resources>
  <integer name="keep_child_fragment_animation_duration">500</integer>
</resources>

0

Щоб оживити зникнення в'язаних фрагментів, ми можемо змусити з'явитися зворотній стек на ChildFragmentManager. Це призведе до анімації переходу. Для цього нам потрібно наздогнати подію OnBackButtonPress або послухати зміни за заднім числом.

Ось приклад з кодом.

View.OnClickListener() {//this is from custom button but you can listen for back button pressed
            @Override
            public void onClick(View v) {
                getChildFragmentManager().popBackStack();
                //and here we can manage other fragment operations 
            }
        });

  Fragment fr = MyNeastedFragment.newInstance(product);

  getChildFragmentManager()
          .beginTransaction()
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
                .replace(R.neasted_fragment_container, fr)
                .addToBackStack("Neasted Fragment")
                .commit();

0

Я нещодавно зіткнувся з цією проблемою у своєму питанні: Вкладені фрагменти неправильно переходять

У мене є рішення, яке вирішує це, не зберігаючи растрові карти, не використовуючи рефлексію чи будь-які інші незадовільні методи.

Приклад проекту можна переглянути тут: https://github.com/zafrani/NestedFragmentTransitions

GIF ефекту можна переглянути тут: https://imgur.com/94AvrW4

У моєму прикладі є 6 дитячих фрагментів, розділених між двома батьківськими фрагментами. Я можу без проблем досягти переходів для входу, виходу, поп-та та push. Зміни конфігурації та натискання назад також успішно обробляються.

Основна частина рішення знаходиться у моєму BaseFragment (фрагменті, розширеному моїми дітьми та батьківськими фрагментами) у функції onCreateAnimator, яка виглядає приблизно так:

   override fun onCreateAnimator(transit: Int, enter: Boolean, nextAnim: Int): Animator {
    if (isConfigChange) {
        resetStates()
        return nothingAnim()
    }

    if (parentFragment is ParentFragment) {
        if ((parentFragment as BaseFragment).isPopping) {
            return nothingAnim()
        }
    }

    if (parentFragment != null && parentFragment.isRemoving) {
        return nothingAnim()
    }

    if (enter) {
        if (isPopping) {
            resetStates()
            return pushAnim()
        }
        if (isSuppressing) {
            resetStates()
            return nothingAnim()
        }
        return enterAnim()
    }

    if (isPopping) {
        resetStates()
        return popAnim()
    }

    if (isSuppressing) {
        resetStates()
        return nothingAnim()
    }

    return exitAnim()
}

Активність та батьківський фрагмент відповідають за встановлення станів цих булей. Простіше переглядати, як і де з мого прикладу проекту.

Я не використовую у своєму прикладі фрагменти підтримки, але з ними та їх функцією onCreateAnimation можна використовувати ту саму логіку.


0

Простий спосіб вирішити цю проблему - використовувати Fragmentклас з цієї бібліотеки замість стандартного класу фрагментів бібліотеки:

https://github.com/marksalpeter/contract-fragment

Як бічна примітка, пакет також містить корисну схему делегата, ContractFragmentяка може бути корисною для створення ваших додатків, використовуючи фрагменти відносин батько-дитина.


0

З наведеної вище відповіді @kcoppock,

якщо у вас активність-> фрагмент-> фрагменти (декілька укладання, наступна допомога), незначне редагування на найкращу відповідь IMHO.

public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {

    final Fragment parent = getParentFragment();

    Fragment parentOfParent = null;

    if( parent!=null ) {
        parentOfParent = parent.getParentFragment();
    }

    if( !enter && parent != null && parentOfParent!=null && parentOfParent.isRemoving()){
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else
    if (!enter && parent != null && parent.isRemoving()) {
        // This is a workaround for the bug where child fragments disappear when
        // the parent is removed (as all children are first removed from the parent)
        // See https://code.google.com/p/android/issues/detail?id=55228
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else {
        return super.onCreateAnimation(transit, enter, nextAnim);
    }
}

0

Моя проблема полягала у видаленні батьківського фрагмента (ft.remove (фрагмент)), дочірні анімації не відбувалися.

Основна проблема полягає в тому, що дочірні фрагменти негайно ВИКОРИСТОВУЮТЬСЯ ПЕРЕМОГИ до батьківського фрагменту, який виходить з анімації.

Дочірні фрагменти спеціальної анімації не виконуються при видаленні батьківського фрагмента

Як і інші ухилялися, ховати БРИЦЯ (а не дитину) перед видаленням РОДИТЕЛЯ - це шлях.

            val ft = fragmentManager?.beginTransaction()
            ft?.setCustomAnimations(R.anim.enter_from_right,
                    R.anim.exit_to_right)
            if (parentFragment.isHidden()) {
                ft?.show(vehicleModule)
            } else {
                ft?.hide(vehicleModule)
            }
            ft?.commit()

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

До речі, вам не потрібні спеціальні анімації на дочірньому фрагменті, оскільки вони успадкують батьківські анімації.



0

Стара нитка, але якщо хтось натикається сюди:

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

val currentFragment = supportFragmentManager.findFragmentByTag(TAG)
val transaction = supportFragmentManager
    .beginTransaction()
    .setCustomAnimations(anim1, anim2, anim1, anim2)
    .add(R.id.fragmentHolder, FragmentB(), TAG)
if (currentFragment != null) {
    transaction.hide(currentFragment).commit()
    Handler().postDelayed({
        supportFragmentManager.beginTransaction().remove(currentFragment).commit()
    }, DURATION_OF_ANIM)
} else {
    transaction.commit()
}

Ми просто приховуємо поточний фрагмент і додаємо новий фрагмент, коли анімація закінчена, ми видаляємо старий фрагмент. Таким чином він обробляється в одному місці і не створюється растрова карта.

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