Fragmen bersarang menghilang selama animasi transisi

99

Berikut skenarionya: Aktivitas mengandung fragmen A, yang pada gilirannya menggunakan getChildFragmentManager()untuk menambahkan fragmen A1dan A2di nya onCreateseperti:

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

Sejauh ini bagus, semuanya berjalan seperti yang diharapkan.

Kami kemudian menjalankan transaksi berikut di Aktivitas:

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

Selama transisi, enteranimasi untuk fragmen Bberjalan dengan benar, tetapi fragmen A1 dan A2 hilang seluruhnya . Saat kami mengembalikan transaksi dengan tombol Kembali, mereka menginisialisasi dengan benar dan ditampilkan secara normal selama popEnteranimasi.

Dalam pengujian singkat saya, itu menjadi lebih aneh - jika saya mengatur animasi untuk fragmen anak (lihat di bawah), exitanimasi berjalan sesekali saat kita menambahkan fragmenB

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

Efek yang ingin saya capai sederhana - Saya ingin animasi exit(atau haruskah popExit?) Pada fragmen A(anim2) berjalan, menganimasikan seluruh container, termasuk turunannya yang bersarang.

Apakah ada cara untuk mencapai itu?

Edit : Silakan temukan kasus uji di sini

Sunting2 : Terima kasih kepada @StevenByle karena telah mendorong saya untuk terus mencoba dengan animasi statis. Rupanya Anda dapat menyetel animasi pada basis per-op (tidak global untuk seluruh transaksi), yang berarti anak-anak dapat memiliki set animasi statis tak terbatas, sementara induknya dapat memiliki animasi yang berbeda dan semuanya dapat dilakukan dalam satu transaksi . Lihat pembahasan di bawah ini dan proyek kasus uji yang diperbarui .

Delyan
sumber
Apa yang terkait R.id.fragmentHolderdengan A, A1, A2, dll?
CommonsWare
fragmentHolder adalah id dalam tata letak aktivitas, fragmen {One, Two} Holder berada dalam tata letak fragmen A. Ketiganya berbeda. Fragmen A awalnya ditambahkan di fragmentHolder (yaitu, fragmen B menggantikan fragmen A).
Delyan
Saya telah membuat proyek contoh di sini: github.com/BurntBrunch/NestedFragmentsAnimationsTest , ada juga apk yang disertakan dalam repositori. Ini adalah bug yang sangat mengganggu dan saya sedang mencari cara untuk mengatasinya (dengan asumsi bahwa itu tidak ada dalam kode saya).
Delyan
Saya tahu lebih banyak tentang masalah ini sekarang. Alasan hilangnya fragmen adalah karena anak-anak menangani peristiwa siklus proses sebelum induknya. Intinya, A1 dan A2 dihapus sebelum A dan karena animasi tidak disetel, keduanya menghilang secara tiba-tiba. Cara untuk mengurangi hal ini adalah dengan secara eksplisit menghapus A1 dan A2 dalam transaksi yang menggantikan A. Dengan begitu, mereka beranimasi saat keluar, namun kecepatan animasinya dikuadratkan, karena container induk juga dianimasikan. Solusi yang tidak menghasilkan artefak ini akan diapresiasi.
Delyan
Perubahan (mengganti fragmen starter) yang Anda sebutkan di pertanyaan adalah yang asli yang ingin Anda lakukan atau itu hanya contoh? Anda akan memanggil changeFragmentmetode ini hanya satu kali?
Luksprog

Jawaban:

37

Untuk menghindari pengguna melihat fragmen bertingkat menghilang saat fragmen induk dihapus / diganti dalam transaksi, Anda dapat "mensimulasikan" fragmen yang masih ada dengan memberikan gambarnya, seperti yang muncul di layar. Gambar ini akan digunakan sebagai latar belakang untuk wadah fragmen bersarang sehingga meskipun tampilan dari fragmen bersarang hilang, gambar akan mensimulasikan keberadaannya. Selain itu, saya tidak melihat kehilangan interaktivitas dengan tampilan fragmen bersarang sebagai masalah karena menurut saya Anda tidak ingin pengguna menindaklanjutinya saat mereka baru saja dalam proses penghapusan (mungkin sebagai tindakan pengguna sebagai baik).

Saya telah membuat sedikit contoh dengan menyiapkan gambar latar belakang (sesuatu yang mendasar).

Luksprog
sumber
1
Saya telah memutuskan untuk memberi Anda hadiah, karena itu adalah solusi yang akhirnya saya gunakan. Terima kasih banyak atas waktunya!
Delyan
17
Wow, sangat kotor. Jangka waktu yang harus dilakukan oleh kami para pengembang Android hanya untuk beberapa kecerdikan
Dean Wild
1
i Memiliki Viewpager Dari tab Kedua Saya mengganti fragmen lain dan ketika saya menekannya kembali saya perlu menampilkan tab kedua viewpager, itu terbuka tetapi menampilkan halaman kosong. Saya mencoba apa yang Anda sarankan di utas di atas tetapi tetap saja sama.
Harish
Masalah tetap ada ketika pengguna kembali sebagai @Harish menulis
Ewoks
1
Wah serius? ini 2018 dan ini masih ada? :(
Archie G. Quiñones
69

Jadi tampaknya ada banyak solusi berbeda untuk ini, tetapi berdasarkan jawaban @ Jayd16, saya rasa saya telah menemukan solusi penampung-semua yang cukup solid yang masih memungkinkan animasi transisi khusus pada fragmen anak, dan tidak perlu dilakukan cache bitmap tata letak.

Miliki BaseFragmentkelas yang diperluas Fragment, dan buat semua fragmen Anda memperluas kelas itu (bukan hanya fragmen anak).

Di BaseFragmentkelas itu, tambahkan yang berikut ini:

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

Sayangnya, hal itu membutuhkan refleksi; namun, karena solusi ini adalah untuk pustaka dukungan, Anda tidak akan mengambil risiko penerapan yang mendasari berubah kecuali Anda memperbarui pustaka dukungan. Jika Anda membuat pustaka dukungan dari sumber, Anda dapat menambahkan pengakses untuk ID sumber daya animasi berikutnya ke Fragment.javadan menghapus kebutuhan refleksi.

Solusi ini menghilangkan kebutuhan untuk "menebak" durasi animasi induk (sehingga animasi "tidak melakukan apa-apa" akan memiliki durasi yang sama dengan animasi keluar induk), dan memungkinkan Anda untuk tetap melakukan animasi kustom pada fragmen turunan (misalnya jika Anda ' kembali menukar fragmen anak dengan animasi yang berbeda).

Kevin Coppock
sumber
5
Ini adalah solusi favorit saya di utas. Tidak memerlukan bitmap, tidak memerlukan kode tambahan apa pun di fragmen turunan, dan tidak benar-benar membocorkan informasi dari induk ke fragmen turunan.
jacobhyphenated
1
@EugenPechanec Anda memerlukan nextAnim dari fragmen induk - bukan dari anak. Itulah intinya.
Kevin Coppock
1
Sayangnya, pendekatan ini menyebabkan kebocoran memori untuk fragmen turunan dan secara implisit untuk fragmen induk juga pada versi Android di bawah Lollipop :(
Cosmin
8
Terima kasih atas solusi yang sangat berguna ini, namun memerlukan sedikit pembaruan untuk bekerja dengan pustaka dukungan saat ini (27.0.2, tidak tahu versi mana yang memecahkan kode ini). mNextAnimsekarang berada di dalam suatu mAnimationInfoobjek. Anda dapat mengaksesnya seperti ini:Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo"); animInfoField.setAccessible(true); Object animationInfo = animInfoField.get(fragment); Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
David Lericolais
5
@DavidLericolais, ingin menambahkan baris kode lain setelah Anda. val nextAnimResource = nextAnimField.getInt(animationInfo);untuk mengganti barisint nextAnimResource = nextAnimField.getInt(fragment);
tingyik90
32

Saya dapat menemukan solusi yang cukup bersih. IMO adalah yang paling tidak hacky, dan sementara ini secara teknis solusi "menggambar bitmap" setidaknya diabstraksikan oleh fragmen lib.

Pastikan frags anak Anda mengganti kelas induk dengan ini:

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

Jika kita memiliki animasi keluar pada anak frags, mereka akan dianimasikan alih-alih berkedip. Kita bisa memanfaatkan ini dengan memiliki animasi yang hanya menggambar fragmen turunan pada alfa penuh selama suatu durasi. Dengan cara ini, mereka akan tetap terlihat di fragmen induk saat dianimasikan, memberikan perilaku yang diinginkan.

Satu-satunya masalah yang dapat saya pikirkan adalah melacak durasi itu. Saya mungkin bisa mengaturnya ke angka besar tapi saya khawatir itu mungkin memiliki masalah kinerja jika masih menggambar animasi itu di suatu tempat.

Jayd16
sumber
Ini membantu, terima kasih. Nilai durasi waktu tidak masalah
iskariot
solusi terbersih sejauh ini
Liran Cohen
16

Saya memposting solusi saya untuk kejelasan. Solusinya cukup sederhana. Jika Anda mencoba meniru animasi transaksi fragmen induk, cukup tambahkan animasi khusus ke transaksi fragmen turunan dengan durasi yang sama. Oh dan pastikan Anda mengatur animasi khusus sebelum add ().

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

Xml untuk R.anim.none (Orang tua saya masuk / keluar waktu animasi adalah 250ms)

<?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>
Maurycy
sumber
Saya telah melakukan sesuatu yang sangat mirip tetapi menggunakan "tampilkan" daripada "tambah" saat memperbarui anak. Saya juga menambahkan "getChildFragmentManager (). ExecutePendingTransactions ()", meskipun saya tidak yakin apakah itu benar-benar diperlukan. Solusi ini berfungsi dengan baik, dan tidak memerlukan "memberikan gambar" dari Fragmen seperti yang disarankan beberapa.
Brian Yencho
Ini bagus. Namun, saya mendapatkan penundaan saat mengganti fragmen anak saya. untuk menghindari hal ini, cukup setel parameter ke-2 tanpa anim:fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
ono
7

Saya mengerti bahwa ini mungkin tidak dapat menyelesaikan masalah Anda sepenuhnya, tetapi mungkin itu akan sesuai dengan kebutuhan orang lain, Anda dapat menambahkan enter/ exitdan popEnter/ popExitanimasi ke anak-anak Anda Fragmentyang tidak benar-benar memindahkan / menghidupkannya Fragment. Selama animasi memiliki durasi / offset yang sama dengan Fragmentanimasi induknya , animasi tersebut akan tampak bergerak / beranimasi dengan animasi induknya.

Steven Byle
sumber
1
Saya telah memberikan hadiah kepada Luksprog karena solusinya bekerja secara universal. Saya memang mencoba trik animasi statis (durasi sebenarnya tidak masalah - setelah orang tua hilang, tampilan jelas menghilang) tetapi mereka tidak berfungsi dalam semua kemungkinan kasus (lihat komentar saya di bawah pertanyaan). Selain itu, pendekatan ini membocorkan abstraksi, karena induk fragmen dengan turunannya perlu mengetahui fakta tersebut dan mengambil langkah ekstra untuk menyetel animasi turunannya. Bagaimanapun, terima kasih banyak atas waktu Anda!
Delyan
Setuju, ini lebih merupakan solusi daripada solusi kedap air, dan dapat dianggap sedikit rapuh. Tapi ini akan berhasil untuk kasus sederhana.
Steven Byle
4

Anda dapat melakukan ini di fragmen anak.

@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);
}
peng gao
sumber
Terima kasih! Mungkin bukan yang terbaik tapi mungkin solusi yang paling sederhana.
bug56
2

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

EDIT: Saya akhirnya tidak menerapkan solusi ini karena ada masalah lain yang dimilikinya. Square baru-baru ini keluar dengan 2 library yang menggantikan fragmen. Saya akan mengatakan ini sebenarnya mungkin alternatif yang lebih baik daripada mencoba meretas fragmen untuk melakukan sesuatu yang tidak diinginkan Google.

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

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

Saya pikir saya akan memberikan solusi ini untuk membantu orang-orang yang memiliki masalah ini di masa depan. Jika Anda menelusuri percakapan poster asli dengan orang lain, dan melihat kode yang dia posting, Anda akan melihat poster asli akhirnya sampai pada kesimpulan menggunakan animasi tanpa operasi pada fragmen turunan sambil menganimasikan fragmen induk. Solusi ini tidak ideal karena memaksa Anda untuk melacak semua fragmen turunan, yang dapat merepotkan saat menggunakan ViewPager dengan FragmentPagerAdapter.

Karena saya menggunakan Fragmen Anak di semua tempat, saya mendapatkan solusi ini yang efisien, dan modular (sehingga dapat dengan mudah dihapus) jika mereka memperbaikinya dan animasi tanpa operasi ini tidak lagi diperlukan.

Ada banyak cara untuk menerapkan ini. Saya memilih untuk menggunakan singleton, dan saya menyebutnya ChildFragmentAnimationManager. Ini pada dasarnya akan melacak fragmen anak untuk saya berdasarkan induknya dan akan menerapkan animasi tanpa operasi ke anak-anak ketika diminta.

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

}

Selanjutnya Anda perlu memiliki kelas yang memperluas Fragmen yang diperluas oleh semua Fragmen Anda (setidaknya Fragmen Anak Anda). Saya sudah memiliki kelas ini, dan saya menyebutnya BaseFragment. Saat tampilan fragmen dibuat, kami menambahkannya ke ChildFragmentAnimationManager, dan menghapusnya saat dihancurkan. Anda bisa melakukan ini onAttach / Detach, atau metode lain yang cocok secara berurutan. Logika saya untuk memilih Buat / Hancurkan Tampilan adalah karena jika Fragmen tidak memiliki Tampilan, saya tidak peduli untuk menganimasinya agar terus terlihat. Pendekatan ini juga akan bekerja lebih baik dengan ViewPager yang menggunakan Fragmen karena Anda tidak akan melacak setiap Fragment yang dipegang FragmentPagerAdapter, melainkan hanya 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();
}

}

Sekarang semua Fragmen Anda disimpan dalam memori oleh fragmen induk, Anda dapat memanggil animate-nya seperti ini, dan fragmen turunan Anda tidak akan hilang.

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();

Juga, asal Anda memilikinya, berikut adalah file no_anim.xml yang ada di folder res / anim Anda:

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

Sekali lagi, menurut saya solusi ini tidak sempurna, tetapi jauh lebih baik daripada untuk setiap contoh Anda memiliki Fragmen Anak, menerapkan kode khusus di fragmen induk untuk melacak setiap anak. Saya pernah ke sana, dan itu tidak menyenangkan.

spierce7
sumber
1

Saya rasa saya menemukan solusi yang lebih baik untuk masalah ini daripada mengambil snapshot fragmen saat ini ke bitmap seperti yang disarankan Luksprog.

Triknya adalah bersembunyi fragmen yang sedang dihilangkan atau dilepas dan hanya setelah animasi selesai, fragmen tersebut akan dihapus atau dilepas dalam transaksi fragmennya sendiri.

Bayangkan kita memiliki FragmentAdan FragmentB, keduanya dengan sub fragmen. Sekarang saat Anda biasanya melakukan:

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

Sebaliknya Anda melakukannya

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

fragmentA.removeMe = true;

Sekarang untuk implementasi Fragmen:

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();
    }
}
Danilo
sumber
Bukankah popBackStack menyebabkan Kesalahan karena mencoba menampilkan Fragmen yang telah dilepaskan?
Alexandre
1

Saya mengalami masalah yang sama dengan fragmen peta. Itu terus menghilang selama animasi keluar dari fragmen yang mengandungnya. Solusinya adalah dengan menambahkan animasi untuk fragmen peta anak yang akan membuatnya tetap terlihat selama animasi keluar dari fragmen induk. Animasi fragmen turunan mempertahankan alfa 100% selama periode durasinya.

Animasi: 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>

Animasi tersebut kemudian diterapkan saat fragmen peta ditambahkan ke fragmen induk.

Fragmen induk

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

Terakhir, durasi animasi fragmen turunan disetel dalam file sumber daya.

nilai / integers.xml

<resources>
  <integer name="keep_child_fragment_animation_duration">500</integer>
</resources>
Evgenii
sumber
0

Untuk menganimasikan hilangnya fragmen yang tidak diinginkan, kita dapat memaksa pop back stack di ChildFragmentManager. Ini akan mengaktifkan animasi transisi. Untuk melakukan ini, kita perlu mengikuti acara OnBackButtonPressed atau mendengarkan perubahan backstack.

Berikut adalah contoh dengan kode.

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();
Mateusz Biedron
sumber
0

Saya baru-baru ini mengalami masalah ini dalam pertanyaan saya: Transisi fragmen bersarang yang salah

Saya memiliki solusi yang menyelesaikan ini tanpa menyimpan bitmap, atau menggunakan refleksi atau metode lain yang tidak memuaskan.

Contoh proyek dapat dilihat di sini: https://github.com/zafrani/NestedFragmentTransitions

GIF dari efeknya dapat dilihat di sini: https://imgur.com/94AvrW4

Dalam contoh saya, ada 6 fragmen anak, dipisahkan antara dua fragmen induk. Saya dapat mencapai transisi untuk masuk, keluar, pop, dan dorong tanpa masalah. Perubahan konfigurasi dan penekanan kembali juga berhasil ditangani.

Sebagian besar solusi ada di fungsi onCreateAnimator BaseFragment saya (fragmen diperpanjang oleh anak-anak dan orang tua saya) yang terlihat seperti ini:

   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()
}

Aktivitas dan fragmen induk bertanggung jawab untuk menyetel status boolean ini. Lebih mudah untuk melihat bagaimana dan di mana dari proyek contoh saya.

Saya tidak menggunakan fragmen dukungan dalam contoh saya, tetapi logika yang sama dapat digunakan dengan mereka dan fungsi onCreateAnimation mereka

zafrani
sumber
0

Cara sederhana untuk memperbaiki masalah ini adalah menggunakan Fragmentkelas dari pustaka ini, bukan dari kelas fragmen pustaka standar:

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

Sebagai catatan tambahan, paket ini juga berisi pola delegasi berguna yang disebut ContractFragmentyang mungkin berguna bagi Anda untuk membangun aplikasi dengan memanfaatkan hubungan fragmen induk-anak.

Tandai Salpeter
sumber
0

Dari jawaban @kcoppock di atas,

jika Anda memiliki Activity-> Fragment-> Fragments (multiple stacking, berikut membantu), sedikit edit untuk jawaban terbaik 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);
    }
}
alekshandru.dll
sumber
0

Masalah saya adalah pada penghapusan fragmen induk (ft.remove (fragment)), animasi anak tidak terjadi.

Masalah dasarnya adalah bahwa fragmen turunan segera DIHANCURKAN SEBELUM animasi keluar fragmen induk.

Animasi kustom fragmen turunan tidak dijalankan saat penghapusan Fragmen Induk

Seperti yang telah dihindari orang lain, menyembunyikan ORANGTUA (dan bukan anak) sebelum penghapusan PARENT adalah cara yang harus dilakukan.

            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()

Jika Anda benar-benar ingin menghapus induk, Anda mungkin harus menyiapkan pendengar pada animasi khusus Anda untuk mengetahui kapan animasi tersebut berakhir, sehingga Anda dapat dengan aman melakukan beberapa penyelesaian pada Fragmen Induk (hapus). Jika Anda tidak melakukan ini, pada waktunya, Anda bisa membunuh animasinya. Animasi NB dilakukan pada antrian asynchronous-nya sendiri.

BTW Anda tidak memerlukan animasi khusus pada fragmen turunan, karena mereka akan mewarisi animasi induk.

Ed Manners
sumber
0

Utas lama, tetapi jika seseorang tersandung di sini:

Semua pendekatan di atas terasa sangat tidak menarik bagi saya, solusi bitmap sangat kotor dan tidak berkinerja baik; yang lain mengharuskan fragmen turunan mengetahui tentang durasi transisi yang digunakan dalam transaksi yang digunakan untuk membuat fragmen turunan yang dimaksud. Solusi yang lebih baik di mata saya seperti berikut ini:

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()
}

Kami hanya menyembunyikan fragmen saat ini dan menambahkan fragmen baru, ketika animasi telah selesai, kami menghapus fragmen lama. Dengan cara ini ditangani di satu tempat dan tidak ada bitmap yang dibuat.

jeppeman
sumber