Cara mendapatkan fragmen yang ada saat menggunakan FragmentPagerAdapter

99

Saya memiliki masalah dalam membuat fragmen saya berkomunikasi satu sama lain melalui Activity, yang menggunakan FragmentPagerAdapter, sebagai kelas pembantu yang mengimplementasikan pengelolaan tab dan semua detail menghubungkan ViewPagerdengan yang terkait TabHost. Saya telah menerapkan FragmentPagerAdaptersama seperti yang disediakan oleh proyek sampel Android Support4Demos .

Pertanyaan utamanya adalah bagaimana saya bisa mendapatkan fragmen tertentu dari FragmentManagersaat saya tidak memiliki Id atau Tag? FragmentPagerAdaptermembuat fragmen dan otomatis menghasilkan Id dan Tag.

Ismar Slomic
sumber
@ jk2K bagaimana ini bisa menjadi duplikat dari pertanyaan yang telah ditanyakan 1 tahun setelahnya
Davi
@Dawit duplikat seperti label, tidak terkait dengan waktu, pertanyaan selanjutnya memiliki pandangan yang lebih tinggi
jk2K
Sebagian besar jawaban di sini tidak berfungsi dalam produksi, jadi periksa jawaban saya di stackoverflow.com/a/54280113/2413303
EpicPandaForce

Jawaban:

194

Ringkasan masalah

Catatan: Dalam jawaban ini saya akan merujuk FragmentPagerAdapterdan kode sumbernya. Tetapi solusi umum juga harus diterapkan FragmentStatePagerAdapter.

Jika Anda membaca ini, Anda mungkin sudah tahu bahwa FragmentPagerAdapter/ FragmentStatePagerAdapterdimaksudkan untuk dibuat Fragmentsuntuk Anda ViewPager, tetapi setelah rekreasi Aktivitas (baik dari rotasi perangkat atau sistem yang mematikan Aplikasi Anda untuk mendapatkan kembali memori), ini Fragmentstidak akan dibuat lagi, tetapi contoh diambil dariFragmentManager . Sekarang katakan Activitykebutuhan Anda untuk mendapatkan referensi tentang ini Fragmentsuntuk mengerjakannya. Anda tidak memiliki idatau taguntuk ini dibuat Fragmentskarena FragmentPagerAdapter menyetelnya secara internal . Jadi masalahnya adalah bagaimana mendapatkan referensi kepada mereka tanpa informasi itu ...

Masalah dengan solusi saat ini: mengandalkan kode internal

Banyak solusi yang saya telah melihat hal ini dan yang sejenis pertanyaan mengandalkan mendapatkan referensi ke yang ada Fragmentdengan memanggil FragmentManager.findFragmentByTag()dan meniru tag dibuat secara internal:"android:switcher:" + viewId + ":" + id . Masalahnya adalah Anda mengandalkan kode sumber internal, yang seperti yang kita semua tahu tidak dijamin akan tetap sama selamanya. Para insinyur Android di Google dapat dengan mudah memutuskan untuk mengubah tagstruktur yang akan merusak kode Anda sehingga Anda tidak dapat menemukan referensi ke yang sudah ada Fragments.

Solusi alternatif tanpa mengandalkan internal tag

Berikut adalah contoh sederhana tentang cara mendapatkan referensi ke yang Fragmentsdikembalikan oleh FragmentPagerAdapteryang tidak bergantung pada tagskumpulan internal di Fragments. Kuncinya adalah mengganti instantiateItem()dan menyimpan referensi di sana, bukan di getItem().

public class SomeActivity extends Activity {
    private FragmentA m1stFragment;
    private FragmentB m2ndFragment;

    // other code in your Activity...

    private class CustomPagerAdapter extends FragmentPagerAdapter {
        // other code in your custom FragmentPagerAdapter...

        public CustomPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            // Do NOT try to save references to the Fragments in getItem(),
            // because getItem() is not always called. If the Fragment
            // was already created then it will be retrieved from the FragmentManger
            // and not here (i.e. getItem() won't be called again).
            switch (position) {
                case 0:
                    return new FragmentA();
                case 1:
                    return new FragmentB();
                default:
                    // This should never happen. Always account for each position above
                    return null;
            }
        }

        // Here we can finally safely save a reference to the created
        // Fragment, no matter where it came from (either getItem() or
        // FragmentManger). Simply save the returned Fragment from
        // super.instantiateItem() into an appropriate reference depending
        // on the ViewPager position.
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            // save the appropriate reference depending on position
            switch (position) {
                case 0:
                    m1stFragment = (FragmentA) createdFragment;
                    break;
                case 1:
                    m2ndFragment = (FragmentB) createdFragment;
                    break;
            }
            return createdFragment;
        }
    }

    public void someMethod() {
        // do work on the referenced Fragments, but first check if they
        // even exist yet, otherwise you'll get an NPE.

        if (m1stFragment != null) {
            // m1stFragment.doWork();
        }

        if (m2ndFragment != null) {
            // m2ndFragment.doSomeWorkToo();
        }
    }
}

atau jika Anda lebih memilih untuk bekerja dengan tagsdaripada variabel anggota kelas / referensi ke FragmentsAnda juga dapat mengambil tagsset dengan FragmentPagerAdapterdengan cara yang sama: CATATAN: ini tidak berlaku FragmentStatePagerAdapterkarena tidak diatur tagssaat membuat its Fragments.

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
    // get the tags set by FragmentPagerAdapter
    switch (position) {
        case 0:
            String firstTag = createdFragment.getTag();
            break;
        case 1:
            String secondTag = createdFragment.getTag();
            break;
    }
    // ... save the tags somewhere so you can reference them later
    return createdFragment;
}

Perhatikan bahwa metode ini TIDAK bergantung pada peniruan tagset internal oleh FragmentPagerAdapterdan sebagai gantinya menggunakan API yang tepat untuk mengambilnya. Dengan cara ini meskipun tagperubahan di versi mendatang SupportLibraryAnda akan tetap aman.


Jangan lupa bahwa tergantung desain Anda Activity, yang FragmentsAnda coba kerjakan mungkin atau mungkin belum ada, jadi Anda harus memperhitungkannya dengan melakukan nullpengecekan sebelum menggunakan referensi Anda.

Juga, jika sebaliknya Anda bekerja dengan FragmentStatePagerAdapter, maka Anda tidak ingin menyimpan referensi keras Fragmentskarena Anda mungkin memiliki banyak dari mereka dan referensi keras tidak perlu menyimpannya dalam memori. Sebagai gantinya simpan Fragmentreferensi dalam WeakReferencevariabel, bukan dalam variabel standar. Seperti ini:

WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
    // reference hasn't been cleared yet; do work...
}
Tony Chan
sumber
3
Ini adalah solusi yang sangat bagus, tetapi tampaknya akan kehilangan keefektifannya jika Anda tidak tahu berapa banyak fragmen yang akan diteruskan.
Riot Goes Woof
3
@Zorpix Anda dapat menyimpan fragmen yang dibuat dalam HashMap: map.put (position, createFragment);
Tom Bevelander
12
Ini layak mendapat tanda centang! Cara yang sangat cerdas dan komprehensif untuk mencapai ini. Anda telah banyak membantu saya terima kasih!
young_souvlaki
2
Pada awalnya solusi ini terlihat terlalu rumit, jadi saya melewatkannya. Saya akhirnya kembali lagi, karena jawaban yang lain tidak memuaskan. Dan itu tidak sesulit yang saya kira.
Suragch
1
tidak perlu menimpa apa pun dan Anda sebenarnya tidak boleh menimpa instantiateItem. cara yang tepat untuk melakukan ini adalah untuk memanggil instantiateItem di onCreatemetode aktivitas Anda dikelilingi oleh startUpdatedan finishUpdate. Lihat jawaban saya untuk detailnya
morgwai
82

Saya telah menemukan jawaban atas pertanyaan saya berdasarkan posting berikut: menggunakan kembali fragmen dalam fragmentpageradapter

Beberapa hal yang telah saya pelajari:

  1. getItem(int position)di nama FragmentPagerAdapteryang agak menyesatkan tentang apa metode ini sebenarnya. Ini menciptakan fragmen baru, tidak mengembalikan yang sudah ada. Artinya, metode tersebut harus diganti namanya menjadi sesuatu seperti createItem(int position)di Android SDK. Jadi metode ini tidak membantu kami mendapatkan fragmen.
  2. Berdasarkan penjelasan dalam dukungan posting FragmentPagerAdapterholds referensi ke fragmen lama Anda harus membiarkan pembuatan fragmen ke FragmentPagerAdapterdan dengan demikian berarti Anda tidak memiliki referensi ke Fragmen atau tagnya. Jika Anda memiliki tag fragmen, Anda dapat dengan mudah mengambil referensi ke tag tersebut dari FragmentManagerdengan memanggil findFragmentByTag(). Kami membutuhkan cara untuk mengetahui tag dari sebuah fragmen pada posisi halaman tertentu.

Larutan

Tambahkan metode helper berikut di kelas Anda untuk mengambil tag fragmen dan mengirimkannya ke findFragmentByTag()metode.

private String getFragmentTag(int viewPagerId, int fragmentPosition)
{
     return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}

CATATAN! Ini adalah metode identik yang FragmentPagerAdapterdigunakan saat membuat fragmen baru. Lihat tautan ini http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104

Ismar Slomic
sumber
Ngomong-ngomong, ada lebih banyak tentang topik dalam T&J ini: stackoverflow.com/questions/6976027/…
Thomas
1
apa itu parameter masukan viewId? Pemandangan yang mana?
Nilzor
@Nilzor viewId adalah id dari ViewPager.
Dr.jacky
Mungkin alih-alih menebak tag, fragmen bisa memberi tahu tagnya ke aktivitasnya di onAttach()?
baskom
4
Ini bukanlah cara yang benar. Jawaban @Tony Chan adalah cara terbaik dan benar.
Morteza Rastgoo
17

Anda tidak perlu mengganti instantiateItematau mengandalkan kompatibilitas dengan makeFragmentNamemetode internal dengan membuat tag fragmen secara manual.
instantiateItemadalah metode publik sehingga Anda dapat dan sebenarnya harus memanggilnya dalam onCreatemetode aktivitas Anda yang dikelilingi oleh panggilan ke startUpdatedan finishUpdatemetode seperti yang dijelaskan di PagerAdapter javadoc :

Panggilan ke metode PagerAdapter startUpdate (ViewGroup) menunjukkan bahwa konten ViewPager akan segera berubah. Satu atau beberapa panggilan ke instantiateItem (ViewGroup, int) dan / atau destroyItem (ViewGroup, int, Object) akan mengikuti, dan akhir pembaruan akan ditandai dengan panggilan ke finishUpdate (ViewGroup).

Anda kemudian dapat dengan cara di atas, menyimpan referensi ke instance fragmen Anda di vars lokal jika perlu. Lihat contoh:

public class MyActivity extends AppCompatActivity {

    Fragment0 tab0; Fragment1 tab1;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myLayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        ((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);

        adapter.startUpdate(viewPager);
        tab0 = (Fragment0) adapter.instantiateItem(viewPager, 0);
        tab1 = (Fragment1) adapter.instantiateItem(viewPager, 1);
        adapter.finishUpdate(viewPager);
    }

    class MyPagerAdapter extends FragmentPagerAdapter {

        public MyPagerAdapter(FragmentManager manager) {super(manager);}

        @Override public int getCount() {return 2;}

        @Override public Fragment getItem(int position) {
            if (position == 0) return new Fragment0();
            if (position == 1) return new Fragment1();
            return null;  // or throw some exception
        }

        @Override public CharSequence getPageTitle(int position) {
            if (position == 0) return getString(R.string.tab0);
            if (position == 1) return getString(R.string.tab1);
            return null;  // or throw some exception
        }
    }
}

instantiateItempertama-tama akan mencoba mendapatkan referensi ke instance fragmen yang ada dari FragmentManager. Hanya jika mereka belum ada, itu akan membuat yang baru menggunakangetItem metode dari adaptor Anda dan "menyimpannya" diFragmentManager untuk penggunaan di masa mendatang.

Penting untuk diperhatikan, bahwa meskipun Anda tidak perlu mendapatkan referensi ke fragmen Anda, Anda tetap harus memanggil instantiateItemsemua tab yang dikelilingi oleh startUpdate/ finishUpdatedalam onCreatemetode Anda seperti ini:

    adapter.startUpdate(viewPager);
    // ignoring return values of the below 2 calls, just side effects matter:
    adapter.instantiateItem(viewPager, 0);
    adapter.instantiateItem(viewPager, 1);
    adapter.finishUpdate(viewPager);

Jika Anda tidak melakukannya, Anda berisiko bahwa instance fragmen Anda tidak akan pernah dikomit ke FragmentManager: saat aktivitas Anda menjadi latar depan instantiateItemakan dipanggil secara otomatis untuk mendapatkan fragmen Anda, tetapi startUpdate/ finishUpdate mungkin tidak (bergantung pada detail implementasi) dan apa dasarnya lakukan adalah memulai / melakukan a FragmentTransaction.
Hal ini dapat mengakibatkan referensi ke instance fragmen yang dibuat hilang dengan sangat cepat (misalnya saat Anda memutar layar) dan dibuat ulang lebih sering dari yang diperlukan. Bergantung pada seberapa "berat" fragmen Anda, ini mungkin memiliki konsekuensi performa yang tidak dapat diabaikan.
Selain itu, dalam kasus seperti fragmen disimpan di vars lokal mungkinmenjadi basi: jika platform android mencoba mendapatkannya dari FragmentManageruntuk alasan apa pun, itu akan gagal dan dengan demikian akan membuat dan menggunakan yang baru, sementara vars Anda masih akan mereferensikan yang lama.

morgwai
sumber
1
Ini mungkin solusi terbaik dalam beberapa kasus. Tetapi apa yang akan terjadi jika FragmentManger akan membunuh fragmen tersebut dan menyusun ulangnya?
woltran
1
@woltran FragmentManagertidak bisa begitu saja membunuh ( menghancurkan adalah kata yang tepat di sini) milik Anda Fragment(pikirkan apa yang akan terjadi jika memutuskan untuk membunuh Fragmentyang sedang ditampilkan;)). Umumnya siklus hidup a Fragmentterikat padanyaActivity (lihat github.com/xxv/android-lifecycle untuk detailnya) -> a Fragmenthanya dapat dimusnahkan jika Activitysudah dimusnahkan. Dalam kasus seperti itu ketika pengguna menavigasi kembali ke yang diberikan Activityitu onCreateakan dipanggil lagi dan contoh baru Fragmentakan dibuat.
morgwai
Ini adalah Jawaban NYATA
MJ Studio
Haruskah Anda benar-benar membuat fragmen daripada mengandalkan pembuatannya saat pengguna menggulir ViewPager misalnya?
Yar
@Yar ya, kamu benar-benar harus. Kutipan dokumen yang saya berikan dengan jelas menyatakannya dan bagian "Beberapa info tambahan" menjelaskan alasannya.
morgwai
11

Cara saya melakukannya adalah mendefinisikan Hashtable of WeakReferences sebagai berikut:

protected Hashtable<Integer, WeakReference<Fragment>> fragmentReferences;

Kemudian saya menulis metode getItem () seperti ini:

@Override
public Fragment getItem(int position) {

    Fragment fragment;
    switch(position) {
    case 0:
        fragment = new MyFirstFragmentClass();
        break;

    default:
        fragment = new MyOtherFragmentClass();
        break;
    }

    fragmentReferences.put(position, new WeakReference<Fragment>(fragment));

    return fragment;
}

Kemudian Anda dapat menulis metode:

public Fragment getFragment(int fragmentId) {
    WeakReference<Fragment> ref = fragmentReferences.get(fragmentId);
    return ref == null ? null : ref.get();
}

Sepertinya ini bekerja dengan baik dan saya merasa sedikit kurang hack daripada

"android:switcher:" + viewId + ":" + position

trik, karena tidak bergantung pada bagaimana FragmentPagerAdapter diimplementasikan. Tentu saja jika fragmen telah dirilis oleh FragmentPagerAdapter atau jika belum dibuat, getFragment akan mengembalikan null.

Jika ada yang menemukan sesuatu yang salah dengan pendekatan ini, komentar lebih dari diterima.

personne3000
sumber
int fragmentIdharus diganti namanya menjadiint position
lmaooooo
7
Saya menggunakan pendekatan yang sangat mirip. Tapi ini gagal saat pager dibuat dari bundel storedState. misalnya: Aktivitas masuk ke latar belakang dan kembali ke latar depan setelah onSavedStateInstance () dipanggil. Dalam hal ini, metode getItem () tidak akan dipanggil.
Anoop
apa alasan untuk membuat peta Anda sendiri karena sudah ada di FragmentManager yang selalu up2date? Lihat jawaban saya untuk detailnya.
morgwai
Selain itu, fakta bahwa suatu fragmen telah dihancurkan, tidak menjamin bahwa tidak ada referensi yang kuat ke sana (meskipun kemungkinan besar, tetapi TIDAK dijamin) dalam hal ini peta Anda masih akan berisi fragmen usang.
morgwai
1
Ini tidak akan berfungsi dengan benar setelah sistem membuat ulang Fragmen.
EpicPandaForce
10

Saya membuat metode ini yang berfungsi untuk mendapatkan referensi ke fragmen saat ini.

public static Fragment getCurrentFragment(ViewPager pager, FragmentPagerAdapter adapter) {
    try {
        Method m = adapter.getClass().getSuperclass().getDeclaredMethod("makeFragmentName", int.class, long.class);
        Field f = adapter.getClass().getSuperclass().getDeclaredField("mFragmentManager");
        f.setAccessible(true);
        FragmentManager fm = (FragmentManager) f.get(adapter);
        m.setAccessible(true);
        String tag = null;
        tag = (String) m.invoke(null, pager.getId(), (long) pager.getCurrentItem());
        return fm.findFragmentByTag(tag);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } 
    return null;
}
Pepijn
sumber
Senang mengingat membuat Metode dan Bidang di luar metode untuk kinerja yang lebih baik
Marcos Vasconcelos
2

solusi yang disarankan oleh @ personne3000 bagus, tetapi memiliki satu masalah: ketika aktivitas beralih ke latar belakang dan dimatikan oleh sistem (untuk mendapatkan beberapa memori fragmentReferenceskosong ) dan kemudian dipulihkan, maka akan kosong, karenagetItem tidak akan dipanggil.

Kelas di bawah menangani situasi seperti itu:

public abstract class AbstractHolderFragmentPagerAdapter<F extends Fragment> extends FragmentPagerAdapter {

    public static final String FRAGMENT_SAVE_PREFIX = "holder";
    private final FragmentManager fragmentManager; // we need to store fragment manager ourselves, because parent's field is private and has no getters.

    public AbstractHolderFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
        fragmentManager = fm;
    }

    private SparseArray<WeakReference<F>> holder = new SparseArray<WeakReference<F>>();

    protected void holdFragment(F fragment) {
        holdFragment(holder.size(), fragment);
    }

    protected void holdFragment(int position, F fragment) {
        if (fragment != null)
            holder.put(position, new WeakReference<F>(fragment));
    }

    public F getHoldedItem(int position) {
        WeakReference<F> ref = holder.get(position);
        return ref == null ? null : ref.get();
    }

    public int getHolderCount() {
        return holder.size();
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) { // code inspired by Google's FragmentStatePagerAdapter implementation
        super.restoreState(state, loader);
        Bundle bundle = (Bundle) state;
        for (String key : bundle.keySet()) {
            if (key.startsWith(FRAGMENT_SAVE_PREFIX)) {
                int index = Integer.parseInt(key.substring(FRAGMENT_SAVE_PREFIX.length()));
                Fragment f = fragmentManager.getFragment(bundle, key);
                holdFragment(index, (F) f);
            }
        }
    }

    @Override
    public Parcelable saveState() {
        Bundle state = (Bundle) super.saveState();
        if (state == null)
            state = new Bundle();

        for (int i = 0; i < holder.size(); i++) {
            int id = holder.keyAt(i);
            final F f = getHoldedItem(i);
            String key = FRAGMENT_SAVE_PREFIX + id;
            fragmentManager.putFragment(state, key, f);
        }
        return state;
    }
}
netimen
sumber
1

Hambatan utama dengan mendapatkan pegangan ke fragmen adalah Anda tidak bisa mengandalkan getItem (). Setelah orientasi berubah, referensi ke fragmen akan menjadi null dan getItem () tidak dipanggil lagi.

Berikut adalah pendekatan yang tidak bergantung pada penerapan FragmentPagerAdapter untuk mendapatkan tag. Ganti instantiateItem () yang akan mengembalikan fragmen yang dibuat dari getItem () atau ditemukan dari pengelola fragmen.

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object value =  super.instantiateItem(container, position);

    if (position == 0) {
        someFragment = (SomeFragment) value;
    } else if (position == 1) {
        anotherFragment = (AnotherFragment) value;
    }

    return value;
}
Adam
sumber
0

Lihat postingan ini tentang mengembalikan fragmen dari FragmentPagerAdapter. Apakah mengandalkan Anda mengetahui indeks fragmen Anda - tetapi ini akan disetel di getItem () (hanya pada instantiation)

Komunitas
sumber
0

Saya berhasil menyelesaikan masalah ini dengan menggunakan id, bukan tag. (Saya menggunakan FragmentStatePagerAdapter yang saya definisikan yang menggunakan Fragmen khusus saya tempat saya mengganti metode onAttach, tempat Anda menyimpan id di suatu tempat:

@Override
public void onAttach(Context context){
    super.onAttach(context);
    MainActivity.fragId = getId();
}

Lalu Anda cukup mengakses fragmen dengan mudah di dalam aktivitas:

Fragment f = getSupportFragmentManager.findFragmentById(fragId);
pengguna2740905
sumber
0

Saya tidak tahu apakah ini pendekatan terbaik tetapi tidak ada yang berhasil untuk saya. Semua opsi lain termasuk getActiveFragment mengembalikan null atau menyebabkan aplikasi mogok.

Saya perhatikan bahwa pada rotasi layar, fragmen sedang dilampirkan, jadi saya menggunakannya untuk mengirim fragmen kembali ke aktivitas.

Dalam fragmen:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnListInteractionListener) activity;
        mListener.setListFrag(this);
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
    }
}

Kemudian dalam aktivitas:

@Override
public void setListFrag(MyListFragment lf) {
    if (mListFragment == null) {
        mListFragment = lf;
    }
}

Dan terakhir di aktivitas onCreate ():

if (savedInstanceState != null) {
    if (mListFragment != null)
        mListFragment.setListItems(items);
}

Pendekatan ini melampirkan fragmen yang terlihat ke aktivitas tanpa membuat yang baru.

TacoEater
sumber
0

Tidak yakin apakah metode saya adalah cara yang benar atau terbaik untuk melakukan ini karena saya relatif pemula dengan Java / Android, tetapi itu berhasil (saya yakin itu melanggar prinsip berorientasi objek tetapi tidak ada solusi lain yang berfungsi untuk kasus penggunaan saya).

Saya memiliki Aktivitas hosting yang menggunakan ViewPager dengan FragmentStatePagerAdapter. Untuk mendapatkan referensi ke Fragmen yang dibuat oleh FragmentStatePagerAdapter, saya membuat antarmuka callback dalam kelas fragmen:

public interface Callbacks {
    public void addFragment (Fragment fragment);
    public void removeFragment (Fragment fragment);
}

Dalam aktivitas hosting, saya mengimplementasikan antarmuka dan membuat LinkedHasSet untuk melacak fragmen:

public class HostingActivity extends AppCompatActivity implements ViewPagerFragment.Callbacks {

    private LinkedHashSet<Fragment> mFragments = new LinkedHashSet<>();

    @Override
    public void addFragment (Fragment fragment) {
        mFragments.add(fragment);
    }

    @Override
    public void removeFragment (Fragment fragment) {
        mFragments.remove(fragment);
    }
}

Dalam kelas ViewPagerFragment saya menambahkan fragmen ke daftar di dalam onAttach dan menghapusnya di dalam onDetach:

public class ViewPagerFragment extends Fragment {

    private Callbacks mCallbacks;

    public interface Callbacks {
        public void addFragment (Fragment fragment);
        public void removeFragment (Fragment fragment);
    } 

    @Override
    public void onAttach (Context context) {
        super.onAttach(context);
        mCallbacks = (Callbacks) context;
        // Add this fragment to the HashSet in the hosting activity
        mCallbacks.addFragment(this);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        // Remove this fragment from the HashSet in the hosting activity
        mCallbacks.removeFragment(this);
        mCallbacks = null;
    }
}

Dalam aktivitas hosting, Anda sekarang dapat menggunakan mFragments untuk melakukan iterasi melalui fragmen yang saat ini ada di FragmentStatePagerAdapter.

sbearben
sumber
0

Kelas ini melakukan triknya tanpa bergantung pada tag internal. Peringatan: Fragmen harus diakses menggunakan metode getFragment dan bukan metode getItem.

public class ViewPagerAdapter extends FragmentPagerAdapter {

    private final Map<Integer, Reference<Fragment>> fragments = new HashMap<>();
    private final List<Callable0<Fragment>> initializers = new ArrayList<>();
    private final List<String> titles = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    void addFragment(Callable0<Fragment> initializer, String title) {
        initializers.add(initializer);
        titles.add(title);
    }

    public Optional<Fragment> getFragment(int position) {
        return Optional.ofNullable(fragments.get(position).get());
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment =  initializers.get(position).execute();
        return fragment;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        fragments.put(position, new WeakReference<>(fragment));
        return fragment;
    }

    @Override
    public int getCount() {
        return initializers.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }
}
Alex Miragall
sumber
-5

Coba saja kode ini,

public class MYFragmentPAdp extends FragmentPagerAdapter {

    public MYFragmentPAdp(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return 2;
    }

     @Override
     public Fragment getItem(int position) {
         if (position == 0)
             Fragment fragment = new Fragment1();
         else (position == 1)
             Fragment fragment = new Fragment2();
         return fragment;
     }
}
Najib Ahmed Puthawala
sumber
Najib, seperti yang saya jelaskan dalam jawaban saya di bawah getItem () membuat fragmen baru sebagai ganti mengembalikan yang sudah ada seperti yang diharapkan oleh nama yang diberikan get dan bukan buat . Lihat solusi saya di posting yang sama.
Ismar Slomic
Fragment fragment = new YourCustomFragmentClass (); tulis di sini periksa ini.
Najib Ahmed Puthawala
Saya masih tidak mengerti bagaimana ini mengubah fakta bahwa Anda membuat fragmen baru alih-alih mendapatkan yang sudah ada ..
Ismar Slomic
Anda masih menginisialisasi dan mengembalikan hanya untuk fragmen khusus Anda, seperti Fragment fragment = new YourFragment (); fragmen pengembalian;
Najib Ahmed Puthawala