Ganti Fragmen di dalam ViewPager

268

Saya mencoba menggunakan Fragmen dengan ViewPagermenggunakan FragmentPagerAdapter. Yang ingin saya capai adalah mengganti sebuah fragmen, yang diposisikan di halaman pertama ViewPager, dengan yang lain.

Pager terdiri dari dua halaman. Yang pertama adalah FirstPagerFragment, yang kedua adalah SecondPagerFragment. Mengklik tombol halaman pertama. Saya ingin mengganti FirstPagerFragmentdengan NextFragment.

Ada kode saya di bawah ini.

public class FragmentPagerActivity extends FragmentActivity {

    static final int NUM_ITEMS = 2;

    MyAdapter mAdapter;
    ViewPager mPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_pager);

        mAdapter = new MyAdapter(getSupportFragmentManager());

        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);

    }


    /**
     * Pager Adapter
     */
    public static class MyAdapter extends FragmentPagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

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

        @Override
        public Fragment getItem(int position) {

            if(position == 0) {
                return FirstPageFragment.newInstance();
            } else {
                return SecondPageFragment.newInstance();
            }

        }
    }


    /**
     * Second Page FRAGMENT
     */
    public static class SecondPageFragment extends Fragment {

        public static SecondPageFragment newInstance() {
            SecondPageFragment f = new SecondPageFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            return inflater.inflate(R.layout.second, container, false);

        }
    }

    /**
     * FIRST PAGE FRAGMENT
     */
    public static class FirstPageFragment extends Fragment {

        Button button;

        public static FirstPageFragment newInstance() {
            FirstPageFragment f = new FirstPageFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            View root = inflater.inflate(R.layout.first, container, false);
            button = (Button) root.findViewById(R.id.button);
            button.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    FragmentTransaction trans = getFragmentManager().beginTransaction();
                                    trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
                    trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                    trans.addToBackStack(null);
                    trans.commit();

                }

            });

            return root;
        }

        /**
     * Next Page FRAGMENT in the First Page
     */
    public static class NextFragment extends Fragment {

        public static NextFragment newInstance() {
            NextFragment f = new NextFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            return inflater.inflate(R.layout.next, container, false);

        }
    }
}

... dan di sini file xml

fragment_pager.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:padding="4dip"
        android:gravity="center_horizontal"
        android:layout_width="match_parent" android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1">
    </android.support.v4.view.ViewPager>

</LinearLayout>

first.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/first_fragment_root_id"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <Button android:id="@+id/button"
     android:layout_width="wrap_content" android:layout_height="wrap_content"
     android:text="to next"/>

</LinearLayout>

Sekarang masalahnya ... ID mana yang harus saya gunakan

trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());

?

Jika saya gunakan R.id.first_fragment_root_id, penggantian berfungsi, tetapi Hierarchy Viewer menunjukkan perilaku aneh, seperti di bawah ini.

Pada awalnya situasinya

setelah penggantian situasinya

Seperti yang Anda lihat ada sesuatu yang salah, saya berharap menemukan kondisi yang sama seperti pada gambar pertama setelah saya mengganti fragmen.

Mie
sumber
Apakah Anda membuat diagram tata letak itu entah bagaimana atau apakah Anda melakukannya secara manual?
Jeroen Vannevel
@ Mie: Saya perlu melakukan hal yang sama. Pertanyaan Anda telah menghasilkan utas panjang dengan banyak jawaban berbeda. Apa yang paling berhasil pada akhirnya? Terimakasih banyak!
narb
1
@ JoeroenVannevel Anda mungkin sudah menemukannya, tetapi diagram tata letak itu adalah developer.android.com/tools/performance/hierarchy-viewer/…
Ankit Popli
mendapat beberapa masalah butuh bantuan Anda stackoverflow.com/questions/40149039/...
albert

Jawaban:

162

Ada solusi lain yang tidak perlu memodifikasi kode sumber ViewPagerdan FragmentStatePagerAdapter, dan bekerja dengan FragmentPagerAdapterkelas dasar yang digunakan oleh penulis.

Saya ingin memulai dengan menjawab pertanyaan penulis tentang ID mana yang harus dia gunakan; itu adalah ID dari wadah, yaitu ID dari pager tampilan itu sendiri. Namun, seperti yang mungkin Anda perhatikan sendiri, menggunakan ID itu dalam kode Anda tidak menyebabkan apa-apa terjadi. Saya akan menjelaskan alasannya:

Pertama-tama, untuk membuat ViewPagerpenghuni kembali halaman, Anda perlu memanggil notifyDataSetChanged()yang berada di kelas dasar adaptor Anda.

Kedua, ViewPagergunakan getItemPosition()metode abstrak untuk memeriksa halaman mana yang harus dihancurkan dan mana yang harus disimpan. Implementasi default dari fungsi ini selalu kembali POSITION_UNCHANGED, yang menyebabkan ViewPageruntuk menjaga semua halaman saat ini, dan akibatnya tidak melampirkan halaman baru Anda. Jadi, untuk membuat penggantian fragmen berfungsi, getItemPosition()perlu diganti dalam adaptor Anda dan harus kembali POSITION_NONEketika dipanggil dengan fragmen lama, untuk disembunyikan, sebagai argumen.

Ini juga berarti bahwa adaptor Anda selalu perlu mengetahui bagian mana yang harus ditampilkan pada posisi 0, FirstPageFragmentatau NextFragment. Salah satu cara melakukan ini adalah memasok pendengar saat membuat FirstPageFragment, yang akan dipanggil ketika saatnya untuk beralih fragmen. Saya pikir ini adalah hal yang baik, untuk membiarkan adaptor fragmen Anda menangani semua switch fragmen dan panggilan ke ViewPagerdan FragmentManager.

Ketiga, FragmentPagerAdaptercache fragmen yang digunakan dengan nama yang berasal dari posisi, jadi jika ada fragmen di posisi 0, itu tidak akan diganti meskipun kelasnya baru. Ada dua solusi, tetapi yang paling sederhana adalah dengan menggunakan remove()fungsi FragmentTransaction, yang akan menghapus tag-nya juga.

Itu banyak teks, di sini adalah kode yang harus bekerja dalam kasus Anda:

public class MyAdapter extends FragmentPagerAdapter
{
    static final int NUM_ITEMS = 2;
    private final FragmentManager mFragmentManager;
    private Fragment mFragmentAtPos0;

    public MyAdapter(FragmentManager fm)
    {
        super(fm);
        mFragmentManager = fm;
    }

    @Override
    public Fragment getItem(int position)
    {
        if (position == 0)
        {
            if (mFragmentAtPos0 == null)
            {
                mFragmentAtPos0 = FirstPageFragment.newInstance(new FirstPageFragmentListener()
                {
                    public void onSwitchToNextFragment()
                    {
                        mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
                        mFragmentAtPos0 = NextFragment.newInstance();
                        notifyDataSetChanged();
                    }
                });
            }
            return mFragmentAtPos0;
        }
        else
            return SecondPageFragment.newInstance();
    }

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

    @Override
    public int getItemPosition(Object object)
    {
        if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }
}

public interface FirstPageFragmentListener
{
    void onSwitchToNextFragment();
}

Semoga ini bisa membantu siapa saja!

Wize
sumber
24
bekerja untuk saya juga, tetapi bagaimana menerapkan tombol kembali untuk menampilkan fragmen pertama?
user1159819
6
Penjelasan hebat tetapi Anda dapat memposting kode sumber lengkap ini, khususnya kelas FirstPageFragment dan SecondPageFragment.
georgiecasey
6
Bagaimana Anda menambahkan FirstPageFragmentListener ke Bundle di newInstance ()?
miguel
4
Bisakah Anda menambahkan kode di mana Anda menangani FirstPageFragment.newInstance()param pendengar?
MiguelHincapieC
6
Ini hanya berfungsi untuk saya jika saya memanggil commitNow () alih-alih komit () saat menghapus fragmen. Tidak yakin mengapa use case saya berbeda, tapi mudah-mudahan ini akan menghemat waktu seseorang.
Ian Lovejoy
37

Pada 13 November 2012, mengganti fragmen dalam ViewPager tampaknya menjadi jauh lebih mudah. Google merilis Android 4.2 dengan dukungan untuk fragmen bersarang, dan itu juga didukung di Perpustakaan Dukungan Android v11 baru sehingga ini akan bekerja sepanjang jalan kembali ke 1,6

Ini sangat mirip dengan cara biasa mengganti sebuah fragmen kecuali Anda menggunakan getChildFragmentManager. Tampaknya berfungsi kecuali backstack fragmen bersarang tidak muncul ketika pengguna mengklik tombol kembali. Sesuai solusi dalam pertanyaan terkait itu, Anda perlu memanggil popBackStackImmediate () secara manual pada child manager dari fragmen. Jadi, Anda perlu mengganti onBackPressed () dari aktivitas ViewPager di mana Anda akan mendapatkan fragmen ViewPager saat ini dan memanggil getChildFragmentManager (). PopBackStackImmediate () di atasnya.

Mendapatkan fragmen yang saat ini sedang ditampilkan juga sedikit macet, saya menggunakan solusi "android: switcher yang kotor ini : VIEWPAGER_ID: INDEX" tetapi Anda juga dapat melacak sendiri semua fragmen ViewPager seperti yang dijelaskan dalam solusi kedua di halaman ini .

Jadi, inilah kode saya untuk ViewPager dengan 4 ListViews dengan tampilan detail yang ditampilkan di ViewPager ketika pengguna mengklik satu baris, dan dengan tombol kembali berfungsi. Saya mencoba memasukkan hanya kode yang relevan demi singkatnya, jadi tinggalkan komentar jika Anda ingin aplikasi lengkapnya diunggah ke GitHub.

HomeActivity.java

 public class HomeActivity extends SherlockFragmentActivity {
FragmentAdapter mAdapter;
ViewPager mPager;
TabPageIndicator mIndicator;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    mAdapter = new FragmentAdapter(getSupportFragmentManager());
    mPager = (ViewPager)findViewById(R.id.pager);
    mPager.setAdapter(mAdapter);
    mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
    mIndicator.setViewPager(mPager);
}

// This the important bit to make sure the back button works when you're nesting fragments. Very hacky, all it takes is some Google engineer to change that ViewPager view tag to break this in a future Android update.
@Override
public void onBackPressed() {
    Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":"+mPager.getCurrentItem());
    if (fragment != null) // could be null if not instantiated yet
    {
        if (fragment.getView() != null) {
            // Pop the backstack on the ChildManager if there is any. If not, close this activity as normal.
            if (!fragment.getChildFragmentManager().popBackStackImmediate()) {
                finish();
            }
        }
    }
}

class FragmentAdapter extends FragmentPagerAdapter {        
    public FragmentAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
        case 0:
            return ListProductsFragment.newInstance();
        case 1:
            return ListActiveSubstancesFragment.newInstance();
        case 2:
            return ListProductFunctionsFragment.newInstance();
        case 3:
            return ListCropsFragment.newInstance();
        default:
            return null;
        }
    }

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

 }
}

ListProductsFragment.java

public class ListProductsFragment extends SherlockFragment {
private ListView list;

public static ListProductsFragment newInstance() {
    ListProductsFragment f = new ListProductsFragment();
    return f;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View V = inflater.inflate(R.layout.list, container, false);
    list = (ListView)V.findViewById(android.R.id.list);
    list.setOnItemClickListener(new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
          // This is important bit
          Fragment productDetailFragment = FragmentProductDetail.newInstance();
          FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
          transaction.addToBackStack(null);
          transaction.replace(R.id.products_list_linear, productDetailFragment).commit();
        }
      });       
    return V;
}
}
georgiecasey
sumber
7
Apakah mungkin bagi Anda untuk mengunggah kode Anda ke github? Terima kasih.
Gautham
4
@georgiecasey: Saya mengunduh API 17, Android 4.2 yang memungkinkan saya untuk mulai menggunakan getChildFragmentManager (). Tapi saya harus kehilangan sesuatu dari solusi Anda, karena saya sekarang mendapatkan tumpang tindih + fragmen baru di layar. Catatan: Saya mencoba menggunakan solusi Anda bersamaan dengan kode contoh TabsAdapter Google dari developer.android.com/reference/android/support/v4/view/… . Terima kasih atas saran dan / atau kode referensi.
gcl1
27
Apa yang dirujuk oleh R.id.products_list_linear? Harap tautkan ke kode Anda seperti yang ditawarkan.
Howettl
1
@howettl referensi ID ke anggota fragmen luar. Kunci untuk mengatasi masalah ini adalah dengan menggunakan fragmen bersarang (dengan begitu Anda tidak perlu memanipulasi adaptor viewpager).
Indrek Kõue
2
Apakah ini pernah berhasil sampai ke github?
Barukh
32

Berdasarkan jawaban @wize, yang saya temukan bermanfaat dan elegan, saya dapat mencapai apa yang saya inginkan sebagian, karena saya ingin kemampuan untuk kembali ke Fragmen pertama setelah diganti. Saya mencapainya sedikit memodifikasi sedikit kodenya.

Ini akan menjadi FragmentPagerAdapter:

public static class MyAdapter extends FragmentPagerAdapter {
    private final class CalendarPageListener implements
            CalendarPageFragmentListener {
        public void onSwitchToNextFragment() {
            mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
                    .commit();
            if (mFragmentAtPos0 instanceof FirstFragment){
                mFragmentAtPos0 = NextFragment.newInstance(listener);
            }else{ // Instance of NextFragment
                mFragmentAtPos0 = FirstFragment.newInstance(listener);
            }
            notifyDataSetChanged();
        }
    }

    CalendarPageListener listener = new CalendarPageListener();;
    private Fragment mFragmentAtPos0;
    private FragmentManager mFragmentManager;

    public MyAdapter(FragmentManager fm) {
        super(fm);
        mFragmentManager = fm;
    }

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

    @Override
    public int getItemPosition(Object object) {
        if (object instanceof FirstFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
        if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }

    @Override
    public Fragment getItem(int position) {
        if (position == 0)
            return Portada.newInstance();
        if (position == 1) { // Position where you want to replace fragments
            if (mFragmentAtPos0 == null) {
                mFragmentAtPos0 = FirstFragment.newInstance(listener);
            }
            return mFragmentAtPos0;
        }
        if (position == 2)
            return Clasificacion.newInstance();
        if (position == 3)
            return Informacion.newInstance();

        return null;
    }
}

public interface CalendarPageFragmentListener {
    void onSwitchToNextFragment();
}

Untuk melakukan penggantian, cukup tentukan bidang statis, dari jenis CalendarPageFragmentListenerdan diinisialisasi melalui newInstancemetode fragmen yang sesuai dan panggilan FirstFragment.pageListener.onSwitchToNextFragment()atau penuh NextFragment.pageListener.onSwitchToNextFragment()respek.

mdelolmo
sumber
Bagaimana Anda melakukan penggantian? tidak mungkin menginisialisasi Pendengar tanpa mengabaikan metode onSwitchToNext.
Leandros
1
jangan sampai Anda kehilangan status yang disimpan di mFragmentAtPos0 jika Anda memutar perangkat?
seb
@seb, saya sebenarnya memperbaiki solusi ini untuk menghemat mFragmentAtPos0referensi saat menyimpan status aktivitas. Ini bukan solusi yang paling elegan, tetapi berhasil.
mdelolmo
Anda dapat melihat jawaban saya . Ini menggantikan fragmen dan kembali ke yang pertama.
Lyd
@mdelolmo Bisakah Anda melihat pertanyaan tentang ViewPager ini? Terima kasih stackoverflow.com/questions/27937250/...
Hoa Vu
21

Saya telah menerapkan solusi untuk:

  • Penggantian fragmen dinamis di dalam tab
  • Pemeliharaan histori per tab
  • Bekerja dengan perubahan orientasi

Trik untuk mencapai ini adalah sebagai berikut:

  • Gunakan metode notifyDataSetChanged () untuk menerapkan penggantian fragmen
  • Gunakan manajer fragmen hanya untuk back stage dan tidak untuk penggantian fragament
  • Pertahankan riwayat menggunakan pola kenang-kenangan (tumpukan)

Kode adaptor adalah sebagai berikut:

public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener {

/** The sherlock fragment activity. */
private final SherlockFragmentActivity mActivity;

/** The action bar. */
private final ActionBar mActionBar;

/** The pager. */
private final ViewPager mPager;

/** The tabs. */
private List<TabInfo> mTabs = new LinkedList<TabInfo>();

/** The total number of tabs. */
private int TOTAL_TABS;

private Map<Integer, Stack<TabInfo>> history = new HashMap<Integer, Stack<TabInfo>>();

/**
 * Creates a new instance.
 *
 * @param activity the activity
 * @param pager    the pager
 */
public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
    super(activity.getSupportFragmentManager());
    activity.getSupportFragmentManager();
    this.mActivity = activity;
    this.mActionBar = activity.getSupportActionBar();
    this.mPager = pager;
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
}

/**
 * Adds the tab.
 *
 * @param image         the image
 * @param fragmentClass the class
 * @param args          the arguments
 */
public void addTab(final Drawable image, final Class fragmentClass, final Bundle args) {
    final TabInfo tabInfo = new TabInfo(fragmentClass, args);
    final ActionBar.Tab tab = mActionBar.newTab();
    tab.setTabListener(this);
    tab.setTag(tabInfo);
    tab.setIcon(image);

    mTabs.add(tabInfo);
    mActionBar.addTab(tab);

    notifyDataSetChanged();
}

@Override
public Fragment getItem(final int position) {
    final TabInfo tabInfo = mTabs.get(position);
    return Fragment.instantiate(mActivity, tabInfo.fragmentClass.getName(), tabInfo.args);
}

@Override
public int getItemPosition(final Object object) {
    /* Get the current position. */
    int position = mActionBar.getSelectedTab().getPosition();

    /* The default value. */
    int pos = POSITION_NONE;
    if (history.get(position).isEmpty()) {
        return POSITION_NONE;
    }

    /* Checks if the object exists in current history. */
    for (Stack<TabInfo> stack : history.values()) {
        TabInfo c = stack.peek();
        if (c.fragmentClass.getName().equals(object.getClass().getName())) {
            pos = POSITION_UNCHANGED;
            break;
        }
    }
    return pos;
}

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

@Override
public void onPageScrollStateChanged(int arg0) {
}

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}

@Override
public void onPageSelected(int position) {
    mActionBar.setSelectedNavigationItem(position);
}

@Override
public void onTabSelected(final ActionBar.Tab tab, final FragmentTransaction ft) {
    TabInfo tabInfo = (TabInfo) tab.getTag();
    for (int i = 0; i < mTabs.size(); i++) {
        if (mTabs.get(i).equals(tabInfo)) {
            mPager.setCurrentItem(i);
        }
    }
}

@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}

@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}

public void replace(final int position, final Class fragmentClass, final Bundle args) {
    /* Save the fragment to the history. */
    mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();

    /* Update the tabs. */
    updateTabs(new TabInfo(fragmentClass, args), position);

    /* Updates the history. */
    history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));

    notifyDataSetChanged();
}

/**
 * Updates the tabs.
 *
 * @param tabInfo
 *          the new tab info
 * @param position
 *          the position
 */
private void updateTabs(final TabInfo tabInfo, final int position) {
    mTabs.remove(position);
    mTabs.add(position, tabInfo);
    mActionBar.getTabAt(position).setTag(tabInfo);
}

/**
 * Creates the history using the current state.
 */
public void createHistory() {
    int position = 0;
    TOTAL_TABS = mTabs.size();
    for (TabInfo mTab : mTabs) {
        if (history.get(position) == null) {
            history.put(position, new Stack<TabInfo>());
        }
        history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
        position++;
    }
}

/**
 * Called on back
 */
public void back() {
    int position = mActionBar.getSelectedTab().getPosition();
    if (!historyIsEmpty(position)) {
        /* In case there is not any other item in the history, then finalize the activity. */
        if (isLastItemInHistory(position)) {
            mActivity.finish();
        }
        final TabInfo currentTabInfo = getPrevious(position);
        mTabs.clear();
        for (int i = 0; i < TOTAL_TABS; i++) {
            if (i == position) {
                mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
            } else {
                TabInfo otherTabInfo = history.get(i).peek();
                mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
            }
        }
    }
    mActionBar.selectTab(mActionBar.getTabAt(position));
    notifyDataSetChanged();
}

/**
 * Returns if the history is empty.
 *
 * @param position
 *          the position
 * @return  the flag if empty
 */
private boolean historyIsEmpty(final int position) {
    return history == null || history.isEmpty() || history.get(position).isEmpty();
}

private boolean isLastItemInHistory(final int position) {
    return history.get(position).size() == 1;
}

/**
 * Returns the previous state by the position provided.
 *
 * @param position
 *          the position
 * @return  the tab info
 */
private TabInfo getPrevious(final int position) {
    TabInfo currentTabInfo = history.get(position).pop();
    if (!history.get(position).isEmpty()) {
        currentTabInfo = history.get(position).peek();
    }
    return currentTabInfo;
}

/** The tab info class */
private static class TabInfo {

    /** The fragment class. */
    public Class fragmentClass;

    /** The args.*/
    public Bundle args;

    /**
     * Creates a new instance.
     *
     * @param fragmentClass
     *          the fragment class
     * @param args
     *          the args
     */
    public TabInfo(Class fragmentClass, Bundle args) {
        this.fragmentClass = fragmentClass;
        this.args = args;
    }

    @Override
    public boolean equals(final Object o) {
        return this.fragmentClass.getName().equals(o.getClass().getName());
    }

    @Override
    public int hashCode() {
        return fragmentClass.getName() != null ? fragmentClass.getName().hashCode() : 0;
    }

    @Override
    public String toString() {
        return "TabInfo{" +
                "fragmentClass=" + fragmentClass +
                '}';
    }
}

Pertama kali Anda menambahkan semua tab, kita perlu memanggil metode createHistory (), untuk membuat sejarah awal

public void createHistory() {
    int position = 0;
    TOTAL_TABS = mTabs.size();
    for (TabInfo mTab : mTabs) {
        if (history.get(position) == null) {
            history.put(position, new Stack<TabInfo>());
        }
        history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
        position++;
    }
}

Setiap kali Anda ingin mengganti fragmen ke tab tertentu yang Anda panggil: replace (posisi int terakhir, Kelas fragmentClass akhir, args Bundel akhir)

/* Save the fragment to the history. */
    mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();

    /* Update the tabs. */
    updateTabs(new TabInfo(fragmentClass, args), position);

    /* Updates the history. */
    history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));

    notifyDataSetChanged();

Saat ditekan kembali, Anda perlu memanggil metode back ():

public void back() {
    int position = mActionBar.getSelectedTab().getPosition();
    if (!historyIsEmpty(position)) {
        /* In case there is not any other item in the history, then finalize the activity. */
        if (isLastItemInHistory(position)) {
            mActivity.finish();
        }
        final TabInfo currentTabInfo = getPrevious(position);
        mTabs.clear();
        for (int i = 0; i < TOTAL_TABS; i++) {
            if (i == position) {
                mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
            } else {
                TabInfo otherTabInfo = history.get(i).peek();
                mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
            }
        }
    }
    mActionBar.selectTab(mActionBar.getTabAt(position));
    notifyDataSetChanged();
}

Solusinya bekerja dengan panel aksi sherlock dan dengan gerakan gesek.

mspapant
sumber
itu bagus! bisakah kamu meletakkannya di github?
Nicramus
Implementasi ini adalah yang paling bersih dari semua yang disediakan di sini, dan membahas setiap masalah. Itu harus menjadi jawaban yang diterima.
dazedviper
Adakah yang memiliki masalah dengan implementasi ini? Saya memiliki 2 dari mereka. 1. punggung pertama tidak mendaftar - saya harus mengklik kembali dua kali untuk mendaftar. 2. Setelah saya mengklik kembali (dua kali) pada tab a dan saya pergi ke tab b, ketika saya kembali ke tab a itu hanya menunjukkan isi tab b
yaronbic
7

tl; dr: Gunakan fragmen host yang bertanggung jawab untuk mengganti konten yang dihosting dan melacak riwayat navigasi belakang (seperti di browser).

Karena case use Anda terdiri dari jumlah tab yang tetap, solusi saya berfungsi dengan baik: Idenya adalah untuk mengisi ViewPager dengan instance dari kelas kustom HostFragment, yang dapat mengganti konten yang dihosting dan menyimpan sendiri riwayat navigasi. Untuk mengganti fragmen yang dihosting Anda membuat panggilan ke metode hostfragment.replaceFragment():

public void replaceFragment(Fragment fragment, boolean addToBackstack) {
    if (addToBackstack) {
        getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
    } else {
        getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
    }
}

Semua metode yang dilakukan adalah mengganti tata letak frame dengan id R.id.hosted_fragmentdengan fragmen yang disediakan untuk metode.

Lihat tutorial saya tentang topik ini untuk detail lebih lanjut dan contoh kerja lengkap tentang GitHub!

marktani
sumber
Meskipun ini kelihatan clumsier, saya pikir sebenarnya yang terbaik (dalam hal struktur dan kemudahan pemeliharaan) cara untuk melakukan ini.
Sira Lam
Saya akhirnya headbutting NPE. : / Tidak dapat menemukan Tampilan yang akan diganti
Fariz Fakkel
6

Beberapa solusi yang disajikan banyak membantu saya untuk menyelesaikan sebagian masalah tetapi masih ada satu hal penting yang hilang dalam solusi yang telah menghasilkan pengecualian yang tidak terduga dan konten halaman hitam daripada konten fragmen dalam beberapa kasus.

Masalahnya adalah bahwa kelas FragmentPagerAdapter menggunakan ID item untuk menyimpan fragmen yang di-cache ke FragmentManager . Untuk alasan ini, Anda perlu mengganti juga metode getItemId (posisi int) sehingga mengembalikan misalnya posisi untuk halaman tingkat atas dan posisi 100 + untuk halaman detail. Kalau tidak, fragmen tingkat atas yang sebelumnya dibuat akan dikembalikan dari cache alih-alih fragmen tingkat detail.

Selain itu, saya membagikan di sini contoh lengkap bagaimana menerapkan aktivitas seperti tab dengan halaman Fragmen menggunakan ViewPager dan tombol tab menggunakan RadioGroup yang memungkinkan penggantian halaman tingkat atas dengan halaman terperinci dan juga mendukung tombol kembali. Implementasi ini hanya mendukung satu tingkat susun belakang (daftar item - detail item) tetapi implementasi susun balik multi-level sangat mudah. Contoh ini bekerja cukup baik dalam kasus normal kecuali itu melemparkan NullPointerException jika Anda beralih ke misalnya halaman kedua, mengubah fragmen halaman pertama (sementara tidak terlihat) dan kembali ke halaman pertama. Saya akan mengirim solusi untuk masalah ini setelah saya mengetahuinya:

public class TabsActivity extends FragmentActivity {

  public static final int PAGE_COUNT = 3;
  public static final int FIRST_PAGE = 0;
  public static final int SECOND_PAGE = 1;
  public static final int THIRD_PAGE = 2;

  /**
   * Opens a new inferior page at specified tab position and adds the current page into back
   * stack.
   */
  public void startPage(int position, Fragment content) {
    // Replace page adapter fragment at position.
    mPagerAdapter.start(position, content);
  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Initialize basic layout.
    this.setContentView(R.layout.tabs_activity);

    // Add tab fragments to view pager.
    {
      // Create fragments adapter.
      mPagerAdapter = new PagerAdapter(pager);
      ViewPager pager = (ViewPager) super.findViewById(R.id.tabs_view_pager);
      pager.setAdapter(mPagerAdapter);

      // Update active tab in tab bar when page changes.
      pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int index, float value, int nextIndex) {
          // Not used.
        }

        @Override
        public void onPageSelected(int index) {
          RadioGroup tabs_radio_group = (RadioGroup) TabsActivity.this.findViewById(
            R.id.tabs_radio_group);
          switch (index) {
            case 0: {
              tabs_radio_group.check(R.id.first_radio_button);
            }
            break;
            case 1: {
              tabs_radio_group.check(R.id.second_radio_button);
            }
            break;
            case 2: {
              tabs_radio_group.check(R.id.third_radio_button);
            }
            break;
          }
        }

        @Override
        public void onPageScrollStateChanged(int index) {
          // Not used.
        }
      });
    }

    // Set "tabs" radio group on checked change listener that changes the displayed page.
    RadioGroup radio_group = (RadioGroup) this.findViewById(R.id.tabs_radio_group);
    radio_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
      @Override
      public void onCheckedChanged(RadioGroup radioGroup, int id) {
        // Get view pager representing tabs.
        ViewPager view_pager = (ViewPager) TabsActivity.this.findViewById(R.id.tabs_view_pager);
        if (view_pager == null) {
          return;
        }

        // Change the active page.
        switch (id) {
          case R.id.first_radio_button: {
            view_pager.setCurrentItem(FIRST_PAGE);
          }
          break;
          case R.id.second_radio_button: {
            view_pager.setCurrentItem(SECOND_PAGE);
          }
          break;
          case R.id.third_radio_button: {
            view_pager.setCurrentItem(THIRD_PAGE);
          }
          break;
        }
      });
    }
  }

  @Override
  public void onBackPressed() {
    if (!mPagerAdapter.back()) {
      super.onBackPressed();
    }
  }

  /**
   * Serves the fragments when paging.
   */
  private class PagerAdapter extends FragmentPagerAdapter {

    public PagerAdapter(ViewPager container) {
      super(TabsActivity.this.getSupportFragmentManager());

      mContainer = container;
      mFragmentManager = TabsActivity.this.getSupportFragmentManager();

      // Prepare "empty" list of fragments.
      mFragments = new ArrayList<Fragment>(){};
      mBackFragments = new ArrayList<Fragment>(){};
      for (int i = 0; i < PAGE_COUNT; i++) {
        mFragments.add(null);
        mBackFragments.add(null);
      }
    }

    /**
     * Replaces the view pager fragment at specified position.
     */
    public void replace(int position, Fragment fragment) {
      // Get currently active fragment.
      Fragment old_fragment = mFragments.get(position);
      if (old_fragment == null) {
        return;
      }

      // Replace the fragment using transaction and in underlaying array list.
      // NOTE .addToBackStack(null) doesn't work
      this.startUpdate(mContainer);
      mFragmentManager.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
        .remove(old_fragment).add(mContainer.getId(), fragment)
        .commit();
      mFragments.set(position, fragment);
      this.notifyDataSetChanged();
      this.finishUpdate(mContainer);
    }

    /**
     * Replaces the fragment at specified position and stores the current fragment to back stack
     * so it can be restored by #back().
     */
    public void start(int position, Fragment fragment) {
      // Remember current fragment.
      mBackFragments.set(position, mFragments.get(position));

      // Replace the displayed fragment.
      this.replace(position, fragment);
    }

    /**
     * Replaces the current fragment by fragment stored in back stack. Does nothing and returns
     * false if no fragment is back-stacked.
     */
    public boolean back() {
      int position = mContainer.getCurrentItem();
      Fragment fragment = mBackFragments.get(position);
      if (fragment == null) {
        // Nothing to go back.
        return false;
      }

      // Restore the remembered fragment and remove it from back fragments.
      this.replace(position, fragment);
      mBackFragments.set(position, null);
      return true;
    }

    /**
     * Returns fragment of a page at specified position.
     */
    @Override
    public Fragment getItem(int position) {
      // If fragment not yet initialized, create its instance.
      if (mFragments.get(position) == null) {
        switch (position) {
          case FIRST_PAGE: {
            mFragments.set(FIRST_PAGE, new DefaultFirstFragment());
          }
          break;
          case SECOND_PAGE: {
            mFragments.set(SECOND_PAGE, new DefaultSecondFragment());
          }
          break;
          case THIRD_PAGE: {
            mFragments.set(THIRD_PAGE, new DefaultThirdFragment());
          }
          break;
        }
      }

      // Return fragment instance at requested position.
      return mFragments.get(position);
    }

    /**
     * Custom item ID resolution. Needed for proper page fragment caching.
     * @see FragmentPagerAdapter#getItemId(int).
     */
    @Override
    public long getItemId(int position) {
      // Fragments from second level page hierarchy have their ID raised above 100. This is
      // important to FragmentPagerAdapter because it is caching fragments to FragmentManager with
      // this item ID key.
      Fragment item = mFragments.get(position);
      if (item != null) {
        if ((item instanceof NewFirstFragment) || (item instanceof NewSecondFragment) ||
          (item instanceof NewThirdFragment)) {
          return 100 + position;
        }
      }

      return position;
    }

    /**
     * Returns number of pages.
     */
    @Override
    public int getCount() {
      return mFragments.size();
    }

    @Override
    public int getItemPosition(Object object)
    {
      int position = POSITION_UNCHANGED;
      if ((object instanceof DefaultFirstFragment) || (object instanceof NewFirstFragment)) {
        if (object.getClass() != mFragments.get(FIRST_PAGE).getClass()) {
          position = POSITION_NONE;
        }
      }
      if ((object instanceof DefaultSecondragment) || (object instanceof NewSecondFragment)) {
        if (object.getClass() != mFragments.get(SECOND_PAGE).getClass()) {
          position = POSITION_NONE;
        }
      }
      if ((object instanceof DefaultThirdFragment) || (object instanceof NewThirdFragment)) {
        if (object.getClass() != mFragments.get(THIRD_PAGE).getClass()) {
          position = POSITION_NONE;
        }
      }
      return position;
    }

    private ViewPager mContainer;
    private FragmentManager mFragmentManager;

    /**
     * List of page fragments.
     */
    private List<Fragment> mFragments;

    /**
     * List of page fragments to return to in onBack();
     */
    private List<Fragment> mBackFragments;
  }

  /**
   * Tab fragments adapter.
   */
  private PagerAdapter mPagerAdapter;
}
Blackhex
sumber
Hmm, ada masalah lain dengan kode ini. Ketika saya ingin memulai suatu aktivitas dari fragmen yang dihapus dan ditambahkan again, saya mendapatkan kesalahan: "Keadaan gagal penyimpanan: aktif NewFirstFragment {405478a0} telah menghapus indeks: -1" (jejak tumpukan penuh: nopaste.info/547a23dfea.html ). Melihat ke sumber android, saya mengetahui bahwa itu ada hubungannya dengan backstack tapi saya tidak tahu bagaimana cara memperbaikinya sejauh ini. Adakah yang punya petunjuk?
Blackhex
2
Cool, menggunakan <code> mFragmentManager.beginTransaction (). Detach (old_fragment) .attach (fragment) .commit (); </code> alih-alih <pre> mFragmentManager.beginTransaction (). SetTransition (FragmentTransaction.TRANSIT_FRAGMENT_OPAG_KEMBANGKAN). old_fragment) .add (mContainer.getId (), fragment) .commit (); </pre> pada contoh di atas tampaknya memperbaiki kedua masalah :-).
Blackhex
1
Jika Anda menggunakan metode ini, pastikan Anda tidak membiarkan Android membuat ulang Aktivitas Anda saat memutar perangkat. Jika tidak, PagerAdapter akan dibuat ulang dan Anda akan kehilangan mBackFragments Anda. Ini juga akan secara serius membingungkan Manajer Fragment. Menggunakan BackStack menghindari masalah ini, dengan mengorbankan implementasi PagerAdapter yang lebih rumit (yaitu Anda tidak dapat mewarisi dari FragmentPagerAdapter.)
Norman
6

Saya telah membuat ViewPager dengan 3 elemen dan 2 sub elemen untuk indeks 2 dan 3 dan di sini apa yang ingin saya lakukan ..

masukkan deskripsi gambar di sini

Saya telah menerapkan ini dengan bantuan dari pertanyaan dan jawaban sebelumnya dari StackOverFlow dan di sini adalah tautannya.

LihatPagerChildFragments

Rukmal Dias
sumber
Proyek Anda gagal, jika Anda memutar perangkat.
Dory
6

Untuk mengganti fragmen di dalam ViewPagerAnda dapat memindahkan kode sumber ViewPager, PagerAdapterdanFragmentStatePagerAdapter kelas ke proyek Anda dan menambahkan kode berikut.

ke dalam ViewPager:

public void notifyItemChanged(Object oldItem, Object newItem) {
    if (mItems != null) {
            for (ItemInfo itemInfo : mItems) {
                        if (itemInfo.object.equals(oldItem)) {
                                itemInfo.object = newItem;
                        }
                    }
       }
       invalidate();
    }

ke dalam FragmentStatePagerAdapter:

public void replaceFragmetns(ViewPager container, Fragment oldFragment, Fragment newFragment) {
       startUpdate(container);

       // remove old fragment

       if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
       int position = getFragmentPosition(oldFragment);
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, null);
        mFragments.set(position, null);

        mCurTransaction.remove(oldFragment);

        // add new fragment

        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        mFragments.set(position, newFragment);
        mCurTransaction.add(container.getId(), newFragment);

       finishUpdate(container);

       // ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
       handleGetItemInbalidated(container, oldFragment, newFragment);

       container.notifyItemChanged(oldFragment, newFragment);
    }

protected abstract void handleGetItemInbalidated(View container, Fragment oldFragment, Fragment newFragment);
protected abstract int  getFragmentPosition(Fragment fragment);

handleGetItemInvalidated()memastikan bahwa setelah panggilan berikutnya getItem()mengembalikan newFragment getFragmentPosition()mengembalikan posisi fragmen di adaptor Anda.

Sekarang, untuk mengganti panggilan fragmen

mAdapter.replaceFragmetns(mViewPager, oldFragment, newFragment);

Jika Anda tertarik pada contoh proyek, tanyakan sumbernya kepada saya.

AndroidTeam Di Mail.Ru
sumber
Bagaimana seharusnya metode getFragmentPosition () dan handleGetItemInbalidated ()? Saya tidak dapat memperbarui getItem () untuk mengembalikan NewFragment .. Tolong bantu ..
Hai! Silakan temukan proyek uji pada tautan ini files.mail.ru/eng?back=%2FEZU6G4
AndroidTeam At Mail.Ru
1
Proyek pengujian Anda gagal, jika Anda memutar perangkat.
seb
1
@ AndroidTeamAtMail.Ru tautan tampaknya kedaluwarsa. Maukah Anda berbagi kode
Sunny
Anda dapat melihat jawaban saya . Ini menggantikan fragmen dan kembali ke yang pertama.
Lyd
4

Bekerja sangat baik dengan solusi AndroidTeam, namun saya menemukan bahwa saya membutuhkan kemampuan untuk kembali seperti halnya FrgmentTransaction.addToBackStack(null) Tetapi menambahkan ini hanya akan menyebabkan Fragmen diganti tanpa memberi tahu ViewPager. Menggabungkan solusi yang disediakan dengan peningkatan kecil ini akan memungkinkan Anda untuk kembali ke keadaan sebelumnya hanya dengan mengesampingkan metode aktivitas onBackPressed(). Kerugian terbesar adalah bahwa ia hanya akan kembali satu per satu yang dapat menghasilkan banyak klik kembali

private ArrayList<Fragment> bFragments = new ArrayList<Fragment>();
private ArrayList<Integer> bPosition = new ArrayList<Integer>();

public void replaceFragmentsWithBackOut(ViewPager container, Fragment oldFragment, Fragment newFragment) {
    startUpdate(container);

    // remove old fragment

    if (mCurTransaction == null) {
         mCurTransaction = mFragmentManager.beginTransaction();
     }
    int position = getFragmentPosition(oldFragment);
     while (mSavedState.size() <= position) {
         mSavedState.add(null);
     }

     //Add Fragment to Back List
     bFragments.add(oldFragment);

     //Add Pager Position to Back List
     bPosition.add(position);

     mSavedState.set(position, null);
     mFragments.set(position, null);

     mCurTransaction.remove(oldFragment);

     // add new fragment

     while (mFragments.size() <= position) {
         mFragments.add(null);
     }
     mFragments.set(position, newFragment);
     mCurTransaction.add(container.getId(), newFragment);

    finishUpdate(container);

    // ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
    handleGetItemInvalidated(container, oldFragment, newFragment);

    container.notifyItemChanged(oldFragment, newFragment);
 }


public boolean popBackImmediate(ViewPager container){
    int bFragSize = bFragments.size();
    int bPosSize = bPosition.size();

    if(bFragSize>0 && bPosSize>0){
        if(bFragSize==bPosSize){
            int last = bFragSize-1;
            int position = bPosition.get(last);

            //Returns Fragment Currently at this position
            Fragment replacedFragment = mFragments.get(position);               
            Fragment originalFragment = bFragments.get(last);

            this.replaceFragments(container, replacedFragment, originalFragment);

            bPosition.remove(last);
            bFragments.remove(last);

            return true;
        }
    }

    return false;       
}

Semoga ini bisa membantu seseorang.

Juga sejauh getFragmentPosition() ini cukup banyak getItem()terbalik. Anda tahu fragmen mana yang digunakan, pastikan Anda mengembalikan posisi yang benar. Berikut ini contohnya:

    @Override
    protected int getFragmentPosition(Fragment fragment) {
            if(fragment.equals(originalFragment1)){
                return 0;
            }
            if(fragment.equals(replacementFragment1)){
                return 0;
            }
            if(fragment.equals(Fragment2)){
                return 1;
            }
        return -1;
    }
penikmat
sumber
3

Dalam onCreateViewmetode Anda , containersebenarnya aViewPager contoh.

Jadi, hanya menelepon

ViewPager vpViewPager = (ViewPager) container;
vpViewPager.setCurrentItem(1);

akan mengubah fragmen saat ini di ViewPager.

Ivan Bajalovic
sumber
1
Dari jam pencarian, baris kode inilah yang saya perlukan untuk membuat semua tab saya berfungsi dengan benar. vpViewPager.setCurrentItem(1);. Saya membaca contoh setiap orang, dengan tidak ada yang terjadi setiap kali sampai saya akhirnya mendapatkan milik Anda. Terima kasih.
Brandon
1
pikiran hancur ... ini luar biasa !! Saya tidak menyadari bahwa variabel kontainer adalah viewpager itu sendiri. Jawaban ini harus dieksplorasi terlebih dahulu sebelum semua yang panjang lainnya. Plus, tidak ada variabel pendengar yang lewat ke konstruktor fragmen yang menyederhanakan semua implementasi ketika Android otomatis membuat ulang fragmen menggunakan konstruktor no-arg default saat konfigurasi berubah.
Kevin Lee
3
bukankah ini hanya berubah menjadi tab yang ada? bagaimana ini terkait dengan mengganti fragmen?
behelit
2
Setuju dengan @behelit, ini tidak menjawab pertanyaan, itu hanya akan memindahkan ViewPagerke halaman lain, itu tidak akan menggantikan bagian apa pun.
Yoann Hercouet
2

Inilah solusi saya yang relatif sederhana untuk masalah ini. Kunci dari solusi ini adalah untuk digunakan FragmentStatePagerAdapteralih-alih FragmentPagerAdapterkarena yang pertama akan menghapus fragmen yang tidak digunakan untuk Anda sementara yang kemudian masih mempertahankan instans mereka. Yang kedua adalah penggunaan POSITION_NONEin getItem (). Saya telah menggunakan Daftar sederhana untuk melacak fragmen saya. Persyaratan saya adalah mengganti seluruh daftar fragmen sekaligus dengan daftar baru, tetapi di bawah ini dapat dengan mudah dimodifikasi untuk mengganti masing-masing fragmen:

public class MyFragmentAdapter extends FragmentStatePagerAdapter {
    private List<Fragment> fragmentList = new ArrayList<Fragment>();
    private List<String> tabTitleList = new ArrayList<String>();

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

    public void addFragments(List<Fragment> fragments, List<String> titles) {
        fragmentList.clear();
        tabTitleList.clear();
        fragmentList.addAll(fragments);
        tabTitleList.addAll(titles);
        notifyDataSetChanged();
    }

    @Override
    public int getItemPosition(Object object) {
        if (fragmentList.contains(object)) {
            return POSITION_UNCHANGED;
        }
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int item) {
        if (item >= fragmentList.size()) {
            return null;
        }
        return fragmentList.get(item);
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        return tabTitleList.get(position);
    }
}
Chris Knight
sumber
2

Saya juga membuat solusi, yang bekerja dengan Stacks . Ini adalah pendekatan yang lebih modular sehingga Anda tidak perlu menentukan masing-masing Fragmen dan Fragmen Detail di Anda FragmentPagerAdapter. Itu dibangun di atas Contoh dari ActionbarSherlock yang berasal jika saya langsung dari Aplikasi Demo Google.

/**
 * This is a helper class that implements the management of tabs and all
 * details of connecting a ViewPager with associated TabHost.  It relies on a
 * trick.  Normally a tab host has a simple API for supplying a View or
 * Intent that each tab will show.  This is not sufficient for switching
 * between pages.  So instead we make the content part of the tab host
 * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
 * view to show as the tab content.  It listens to changes in tabs, and takes
 * care of switch to the correct paged in the ViewPager whenever the selected
 * tab changes.
 * 
 * Changed to support more Layers of fragments on each Tab.
 * by sebnapi (2012)
 * 
 */
public class TabsAdapter extends FragmentPagerAdapter
        implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
    private final Context mContext;
    private final TabHost mTabHost;
    private final ViewPager mViewPager;

    private ArrayList<String> mTabTags = new ArrayList<String>();
    private HashMap<String, Stack<TabInfo>> mTabStackMap = new HashMap<String, Stack<TabInfo>>();

    static final class TabInfo {
        public final String tag;
        public final Class<?> clss;
        public Bundle args;

        TabInfo(String _tag, Class<?> _class, Bundle _args) {
            tag = _tag;
            clss = _class;
            args = _args;
        }
    }

    static class DummyTabFactory implements TabHost.TabContentFactory {
        private final Context mContext;

        public DummyTabFactory(Context context) {
            mContext = context;
        }

        @Override
        public View createTabContent(String tag) {
            View v = new View(mContext);
            v.setMinimumWidth(0);
            v.setMinimumHeight(0);
            return v;
        }
    }

    public interface SaveStateBundle{
        public Bundle onRemoveFragment(Bundle outState);
    }

    public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        mContext = activity;
        mTabHost = tabHost;
        mViewPager = pager;
        mTabHost.setOnTabChangedListener(this);
        mViewPager.setAdapter(this);
        mViewPager.setOnPageChangeListener(this);
    }

    /**
     * Add a Tab which will have Fragment Stack. Add Fragments on this Stack by using
     * addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args)
     * The Stack will hold always the default Fragment u add here.
     * 
     * DON'T ADD Tabs with same tag, it's not beeing checked and results in unexpected
     * beahvior.
     * 
     * @param tabSpec
     * @param clss
     * @param args
     */
    public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args){
        Stack<TabInfo> tabStack = new Stack<TabInfo>();

        tabSpec.setContent(new DummyTabFactory(mContext));
        mTabHost.addTab(tabSpec);
        String tag = tabSpec.getTag();
        TabInfo info = new TabInfo(tag, clss, args);

        mTabTags.add(tag);                  // to know the position of the tab tag 
        tabStack.add(info);
        mTabStackMap.put(tag, tabStack);
        notifyDataSetChanged();
    }

    /**
     * Will add the Fragment to Tab with the Tag _tag. Provide the Class of the Fragment
     * it will be instantiated by this object. Proivde _args for your Fragment.
     * 
     * @param fm
     * @param _tag
     * @param _class
     * @param _args
     */
    public void addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args){
        TabInfo info = new TabInfo(_tag, _class, _args);
        Stack<TabInfo> tabStack = mTabStackMap.get(_tag);   
        Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
        if(frag instanceof SaveStateBundle){
            Bundle b = new Bundle();
            ((SaveStateBundle) frag).onRemoveFragment(b);
            tabStack.peek().args = b;
        }
        tabStack.add(info);
        FragmentTransaction ft = fm.beginTransaction();
        ft.remove(frag).commit();
        notifyDataSetChanged();
    }

    /**
     * Will pop the Fragment added to the Tab with the Tag _tag
     * 
     * @param fm
     * @param _tag
     * @return
     */
    public boolean popFragment(FragmentManager fm, String _tag){
        Stack<TabInfo> tabStack = mTabStackMap.get(_tag);   
        if(tabStack.size()>1){
            tabStack.pop();
            Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
            FragmentTransaction ft = fm.beginTransaction();
            ft.remove(frag).commit();
            notifyDataSetChanged();
            return true;
        }
        return false;
    }

    public boolean back(FragmentManager fm) {
        int position = mViewPager.getCurrentItem();
        return popFragment(fm, mTabTags.get(position));
    }

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

    @Override
    public int getItemPosition(Object object) {
        ArrayList<Class<?>> positionNoneHack = new ArrayList<Class<?>>();
        for(Stack<TabInfo> tabStack: mTabStackMap.values()){
            positionNoneHack.add(tabStack.peek().clss);
        }   // if the object class lies on top of our stacks, we return default
        if(positionNoneHack.contains(object.getClass())){
            return POSITION_UNCHANGED;
        }
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int position) {
        Stack<TabInfo> tabStack = mTabStackMap.get(mTabTags.get(position));
        TabInfo info = tabStack.peek();
        return Fragment.instantiate(mContext, info.clss.getName(), info.args);
    }

    @Override
    public void onTabChanged(String tabId) {
        int position = mTabHost.getCurrentTab();
        mViewPager.setCurrentItem(position);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
        // Unfortunately when TabHost changes the current tab, it kindly
        // also takes care of putting focus on it when not in touch mode.
        // The jerk.
        // This hack tries to prevent this from pulling focus out of our
        // ViewPager.
        TabWidget widget = mTabHost.getTabWidget();
        int oldFocusability = widget.getDescendantFocusability();
        widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        mTabHost.setCurrentTab(position);
        widget.setDescendantFocusability(oldFocusability);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

}

Tambahkan ini untuk fungsi tombol kembali di MainActivity Anda:

@Override
public void onBackPressed() {
  if (!mTabsAdapter.back(getSupportFragmentManager())) {
    super.onBackPressed();
  }
}

Jika Anda ingin menyimpan Status Fragmen ketika dihapus. Biarkan Fragmen Anda menerapkan antarmuka SaveStateBundlekembali dalam fungsi bundel dengan status penyimpanan Anda. Dapatkan bundel setelah instantiasi oleh this.getArguments().

Anda dapat instantiate tab seperti ini:

mTabsAdapter.addTab(mTabHost.newTabSpec("firstTabTag").setIndicator("First Tab Title"),
                FirstFragmentActivity.FirstFragmentFragment.class, null);

berfungsi serupa jika Anda ingin menambahkan Fragmen di atas Tab Stack. Penting : Saya pikir, itu tidak akan berfungsi jika Anda ingin memiliki 2 instance dari kelas yang sama di atas dua Tab. Saya melakukan solusi ini bersama-sama dengan cepat, jadi saya hanya bisa membagikannya tanpa memberikan pengalaman apa pun dengannya.

seb
sumber
Saya mendapatkan pengecualian nullpointer ketika menekan tombol kembali dan fragmen saya yang diganti juga tidak terlihat. Jadi mungkin saya melakukan sesuatu yang salah: apa yang seharusnya menjadi panggilan yang tepat untuk mengubah fragmen dengan benar?
Jeroen
Pembaruan: Saya memperbaiki sebagian besar hal, jika saya menekan tombol kembali setelah menambahkan fragmen baru dan kemudian pergi ke tab lain dan kembali, saya mendapatkan pengecualian nullpointer. Jadi saya punya 2 pertanyaan: 1: bagaimana saya bisa menghapus riwayat tab dan memastikan hanya fragmen terbaru (atas tumpukan te) masih ada? 2: bagaimana saya bisa menyingkirkan pengecualian nullpointer?
Jeroen
@Ero 1: u bisa mendapatkan tumpukan yang tepat dari mTabStackMap, dan kemudian menghapus setiap objek TabInfo yang terletak di bawah bagian atas tumpukan, 2: sangat sulit untuk mengatakan, ajukan pertanyaan dengan stacktrace maka saya bisa mencoba mencari tahu, apakah Anda yakin itu bukan kegagalan dalam kode Anda? Apakah ini terjadi juga jika Anda beralih ke tab (dan kemudian kembali) yang ada di lingkungan langsung (kanan atau kiri)?
seb
Ok, saya mengalami masalah baru: jika saya memutar layar saya, maka tumpukan tampaknya kosong lagi? Tampaknya kehilangan jejak item sebelumnya ...
Jeroen
1
@seb, terima kasih. Saya menggunakan kode ini tetapi fragmen bersarang sekarang mungkin jadi tidak ada masalah lagi. :)
Garcon
2

Mengganti fragmen dalam viewpager cukup terlibat tetapi sangat mungkin dan bisa terlihat sangat apik. Pertama, Anda harus membiarkan viewpager itu sendiri menangani penghapusan dan penambahan fragmen. Apa yang terjadi adalah ketika Anda mengganti fragmen di dalam SearchFragment, viewpager Anda mempertahankan tampilan fragmennya. Jadi Anda berakhir dengan halaman kosong karena SearchFragment dihapus ketika Anda mencoba untuk menggantinya.

Solusinya adalah membuat pendengar di dalam viewpager Anda yang akan menangani perubahan yang dibuat di luarnya jadi pertama-tama tambahkan kode ini ke bagian bawah adaptor Anda.

public interface nextFragmentListener {
    public void fragment0Changed(String newFragmentIdentification);
}

Maka Anda perlu membuat kelas pribadi di viewpager Anda yang menjadi pendengar ketika Anda ingin mengubah fragmen Anda. Misalnya Anda bisa menambahkan sesuatu seperti ini. Perhatikan bahwa itu mengimplementasikan antarmuka yang baru saja dibuat. Jadi, setiap kali Anda memanggil metode ini, itu akan menjalankan kode di dalam kelas di bawah ini.

private final class fragmentChangeListener implements nextFragmentListener {


    @Override
    public void fragment0Changed(String fragment) {
        //I will explain the purpose of fragment0 in a moment
        fragment0 = fragment;
        manager.beginTransaction().remove(fragAt0).commit();

        switch (fragment){
            case "searchFragment":
                fragAt0 = SearchFragment.newInstance(listener);
                break;
            case "searchResultFragment":
                fragAt0 = Fragment_Table.newInstance(listener);
                break;
        }

        notifyDataSetChanged();
    }

Ada dua hal utama untuk ditunjukkan di sini:

  1. fragAt0 adalah fragmen "fleksibel". Itu dapat mengambil jenis fragmen apa pun yang Anda berikan. Ini memungkinkannya untuk menjadi teman terbaik Anda dalam mengubah fragmen di posisi 0 menjadi fragmen yang Anda inginkan.
  2. Perhatikan pendengar yang ditempatkan di konstruktor 'newInstance (listener). Ini adalah bagaimana Anda akan memanggilfragment0Changed (String newFragmentIdentification) `. Kode berikut menunjukkan bagaimana Anda membuat pendengar di dalam fragmen Anda.

    static nextFragmentListener listenerSearch;

        public static Fragment_Journals newInstance(nextFragmentListener listener){
                listenerSearch = listener;
                return new Fragment_Journals();
            }

Anda dapat memanggil perubahan di bagian dalam onPostExecute

private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{

    protected Void doInBackground(Void... params){
        .
        .//some more operation
        .
    }
    protected void onPostExecute(Void param){

        listenerSearch.fragment0Changed("searchResultFragment");
    }

}

Ini akan memicu kode di dalam viewpager Anda untuk mengganti fragmen Anda di posisi nol fragAt0 menjadi searchResultFragment baru. Ada dua potong kecil yang perlu Anda tambahkan ke viewpager sebelum menjadi fungsional.

Satu akan berada di metode getItem menimpa viewpager.

@Override
public Fragment getItem(int index) {

    switch (index) {
    case 0:
        //this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.  

        if(fragAt0 == null){

            switch(fragment0){
            case "searchFragment":
                fragAt0 = FragmentSearch.newInstance(listener);
                break;
            case "searchResultsFragment":
                fragAt0 = FragmentSearchResults.newInstance(listener);
                break;
            }
        }
        return fragAt0;
    case 1:
        // Games fragment activity
        return new CreateFragment();

    }

Sekarang tanpa bagian terakhir ini Anda masih akan mendapatkan halaman kosong. Agak timpang, tetapi ini adalah bagian penting dari viewPager. Anda harus mengganti metode getItemPosition pada viewpager. Biasanya metode ini akan mengembalikan POSITION_UNCHANGED yang memberi tahu viewpager agar semuanya tetap sama sehingga getItem tidak akan pernah dipanggil untuk menempatkan fragmen baru di halaman. Inilah contoh sesuatu yang bisa Anda lakukan

public int getItemPosition(Object object)
{
    //object is the current fragment displayed at position 0.  
    if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
        return POSITION_NONE;
    //this condition is for when you press back
    }else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
        return POSITION_NONE;
    }
        return POSITION_UNCHANGED
}

Seperti yang saya katakan, kode menjadi sangat terlibat, tetapi pada dasarnya Anda harus membuat adaptor khusus untuk situasi Anda. Hal-hal yang saya sebutkan akan memungkinkan untuk mengubah fragmen. Mungkin akan membutuhkan waktu lama untuk merendam semuanya sehingga saya akan bersabar, tetapi semuanya akan masuk akal. Ini benar-benar layak diluangkan waktu karena dapat membuat aplikasi yang tampak sangat apik.

Inilah nugget untuk menangani tombol kembali. Anda memasukkan ini ke dalam MainActivity Anda

 public void onBackPressed() {
    if(mViewPager.getCurrentItem() == 0) {
        if(pagerAdapter.getItem(0) instanceof FragmentSearchResults){
            ((Fragment_Table) pagerAdapter.getItem(0)).backPressed();
        }else if (pagerAdapter.getItem(0) instanceof FragmentSearch) {
            finish();
        }
    }

Anda perlu membuat metode yang disebut backPressed () di dalam FragmentSearchResult yang memanggil fragment0 berubah. Ini bersamaan dengan kode yang saya tunjukkan sebelumnya akan menangani menekan tombol kembali. Semoga berhasil dengan kode Anda untuk mengubah viewpager. Butuh banyak pekerjaan, dan sejauh yang saya temukan, tidak ada adaptasi cepat. Seperti yang saya katakan, pada dasarnya Anda membuat adaptor viewpager khusus, dan membiarkannya menangani semua perubahan yang diperlukan menggunakan pendengar

pengguna3331142
sumber
Terima kasih atas solusi ini. Tetapi apakah Anda memiliki masalah setelah memutar layar. Aplikasi saya mogok ketika saya memutar layar dan mencoba memuat ulang fragmen lain di tab yang sama.
AnteGemini
Sudah lama sejak saya melakukan Android dev, tetapi dugaan saya adalah ada sepotong data yang tidak lagi ada di mana aplikasi Anda mengandalkan. Ketika Anda memutar, seluruh aktivitas dimulai ulang dan dapat menyebabkan crash jika Anda mencoba memuat ulang keadaan Anda sebelumnya tanpa semua data yang Anda butuhkan (katakanlah dari bundel di onPause, onResume)
user3331142
2

Ini cara saya untuk mencapai itu.

Pertama-tama tambahkan Root_fragmentviewPager tab di dalam di mana Anda ingin menerapkan fragmentacara klik tombol . Contoh;

@Override
public Fragment getItem(int position) {
  if(position==0)
      return RootTabFragment.newInstance();
  else
      return SecondPagerFragment.newInstance();
}

Pertama-tama, RootTabFragment harus dimasukkan FragmentLayoutuntuk perubahan fragmen.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/root_frame"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
</FrameLayout>

Lalu, di dalam RootTabFragment onCreateView , terapkan fragmentChangeuntuk AndaFirstPagerFragment

getChildFragmentManager().beginTransaction().replace(R.id.root_frame, FirstPagerFragment.newInstance()).commit();

Setelah itu, implementasikan onClick acara untuk tombol Anda di dalam FirstPagerFragmentdan buat perubahan fragmen seperti itu lagi.

getChildFragmentManager().beginTransaction().replace(R.id.root_frame, NextFragment.newInstance()).commit();

Semoga ini bisa membantu kalian.

Min Khant Lu
sumber
1

Saya menemukan solusi sederhana, yang berfungsi dengan baik bahkan jika Anda ingin menambahkan fragmen baru di tengah atau mengganti fragmen saat ini. Dalam solusi saya, Anda harus menimpagetItemId() yang harus mengembalikan id unik untuk setiap fragmen. Bukan posisi sebagai default.

Itu dia:

public class DynamicPagerAdapter extends FragmentPagerAdapter {

private ArrayList<Page> mPages = new ArrayList<Page>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

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

public void replacePage(int position, Page page) {
    mPages.set(position, page);
    notifyDataSetChanged();
}

public void setPages(ArrayList<Page> pages) {
    mPages = pages;
    notifyDataSetChanged();
}

@Override
public Fragment getItem(int position) {
    if (mPages.get(position).mPageType == PageType.FIRST) {
        return FirstFragment.newInstance(mPages.get(position));
    } else {
        return SecondFragment.newInstance(mPages.get(position));
    }
}

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

@Override
public long getItemId(int position) {
    // return unique id
    return mPages.get(position).getId();
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment fragment = (Fragment) super.instantiateItem(container, position);
    while (mFragments.size() <= position) {
        mFragments.add(null);
    }
    mFragments.set(position, fragment);
    return fragment;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    mFragments.set(position, null);
}

@Override
public int getItemPosition(Object object) {
    PagerFragment pagerFragment = (PagerFragment) object;
    Page page = pagerFragment.getPage();
    int position = mFragments.indexOf(pagerFragment);
    if (page.equals(mPages.get(position))) {
        return POSITION_UNCHANGED;
    } else {
        return POSITION_NONE;
    }
}
}

Perhatikan: Dalam contoh ini FirstFragmentdan SecondFragmentperpanjang kelas PageFragment abstrak, yang memiliki metode getPage().

Sergei Vasilenko
sumber
1

Saya melakukan sesuatu yang mirip dengan wize tetapi dalam jawaban saya Anda dapat mengubah antara dua fragmen kapan pun Anda inginkan. Dan dengan jawaban bijak saya punya beberapa masalah ketika mengubah orientasi layar dan hal-hal seperti itu. Ini adalah PagerAdapter seperti:

    public class MyAdapter extends FragmentPagerAdapter
{
    static final int NUM_ITEMS = 2;
    private final FragmentManager mFragmentManager;
    private Fragment mFragmentAtPos0;
     private Map<Integer, String> mFragmentTags;
     private boolean isNextFragment=false;

    public MyAdapter(FragmentManager fm)
    {
        super(fm);
        mFragmentManager = fm;
         mFragmentTags = new HashMap<Integer, String>();
    }

    @Override
    public Fragment getItem(int position)
    {
        if (position == 0)
        {


            if (isPager) {
                mFragmentAtPos0 = new FirstPageFragment();
            } else {
                mFragmentAtPos0 = new NextFragment();
            }
            return mFragmentAtPos0;
        }
        else
            return SecondPageFragment.newInstance();
    }

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


 @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object obj = super.instantiateItem(container, position);
        if (obj instanceof Fragment) {
            // record the fragment tag here.
            Fragment f = (Fragment) obj;
            String tag = f.getTag();
            mFragmentTags.put(position, tag);
        }
        return obj;
    }


    public void onChange(boolean isNextFragment) {

        if (mFragmentAtPos0 == null)
            mFragmentAtPos0 = getFragment(0);
        if (mFragmentAtPos0 != null)
            mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();


        if (!isNextFragment) {
            mFragmentAtFlashcards = new FirstPageFragment();
        } else {
            mFragmentAtFlashcards = new NextFragment();
        }

        notifyDataSetChanged();


    }


    @Override
    public int getItemPosition(Object object)
    {
        if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
         if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstPageFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }


    public Fragment getFragment(int position) {
        String tag = mFragmentTags.get(position);
        if (tag == null)
            return null;
        return mFragmentManager.findFragmentByTag(tag);
    }
}

Pendengar yang saya terapkan dalam aktivitas wadah adaptor untuk memasukkannya ke fragmen ketika melampirkannya, ini adalah aktivitasnya:

    public class PagerContainerActivity extends AppCompatActivity implements ChangeFragmentListener {

//...

  @Override
    public void onChange(boolean isNextFragment) {
        if (pagerAdapter != null)
            pagerAdapter.onChange(isNextFragment);


    }

//...
}

Kemudian dalam fragmen menempatkan pendengar ketika melampirkan memanggilnya:

public class FirstPageFragment extends Fragment{


private ChangeFragmentListener changeFragmentListener;


//...
 @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        changeFragmentListener = ((PagerContainerActivity) activity);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        changeFragmentListener = null;
    }
//...
//in the on click to change the fragment
changeFragmentListener.onChange(true);
//...
}

Dan akhirnya pendengar:

public interface changeFragmentListener {

    void onChange(boolean isNextFragment);

}
Jachumbelechao Kepada Mantekilla
sumber
1

Saya mengikuti jawaban oleh @wize dan @mdelolmo dan saya mendapatkan solusinya. Terima kasih banyak. Tapi, saya menyetel solusi ini sedikit untuk meningkatkan konsumsi memori.

Masalah yang saya amati:

Mereka menyimpan contoh Fragmentyang diganti. Dalam kasus saya, itu adalah Fragmen yang memegang MapViewdan saya pikir itu mahal. Jadi, saya menjaga FragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED)bukannya Fragmentitu sendiri.

Ini implementasi saya.

  public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {

    private SwitchFragListener mSwitchFragListener;
    private Switch mToggle;
    private int pagerAdapterPosChanged = POSITION_UNCHANGED;
    private static final int TOGGLE_ENABLE_POS = 2;


    public DemoCollectionPagerAdapter(FragmentManager fm, Switch toggle) {
        super(fm);
        mToggle = toggle;

        mSwitchFragListener = new SwitchFragListener();
        mToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                mSwitchFragListener.onSwitchToNextFragment();
            }
        });
    }

    @Override
    public Fragment getItem(int i) {
        switch (i)
        {
            case TOGGLE_ENABLE_POS:
                if(mToggle.isChecked())
                {
                    return TabReplaceFragment.getInstance();
                }else
                {
                    return DemoTab2Fragment.getInstance(i);
                }

            default:
                return DemoTabFragment.getInstance(i);
        }
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        return "Tab " + (position + 1);
    }

    @Override
    public int getItemPosition(Object object) {

        //  This check make sures getItem() is called only for the required Fragment
        if (object instanceof TabReplaceFragment
                ||  object instanceof DemoTab2Fragment)
            return pagerAdapterPosChanged;

        return POSITION_UNCHANGED;
    }

    /**
     * Switch fragments Interface implementation
     */
    private final class SwitchFragListener implements
            SwitchFragInterface {

        SwitchFragListener() {}

        public void onSwitchToNextFragment() {

            pagerAdapterPosChanged = POSITION_NONE;
            notifyDataSetChanged();
        }
    }

    /**
     * Interface to switch frags
     */
    private interface SwitchFragInterface{
        void onSwitchToNextFragment();
    }
}

Tautan demo di sini .. https://youtu.be/l_62uhKkLyM

Untuk keperluan demo, gunakan 2 fragmen TabReplaceFragmentdan DemoTab2Fragmentdi posisi dua. Dalam semua kasus lain saya menggunakan DemoTabFragmentcontoh.

Penjelasan:

Saya beralih Switchdari Aktivitas ke DemoCollectionPagerAdapter. Berdasarkan keadaan sakelar ini, kami akan menampilkan fragmen yang benar. Ketika cek saklar berubah, saya akan menelepon SwitchFragListener's onSwitchToNextFragmentmetode, di mana saya mengubah nilai pagerAdapterPosChangedvariabel POSITION_NONE. Lihat lebih lanjut tentang POSITION_NONE . Ini akan membatalkan getItem dan saya memiliki logika untuk membuat fragmen yang tepat di sana. Maaf, jika penjelasannya agak berantakan.

Sekali lagi terima kasih banyak kepada @wize dan @mdelolmo untuk ide aslinya.

Semoga ini bisa membantu. :)

Beritahu saya jika implementasi ini memiliki kekurangan. Itu akan sangat membantu proyek saya.

sha
sumber
0

setelah penelitian saya menemukan solusi dengan kode pendek. pertama-tama buat instance publik tentang fragmen dan hapus saja fragmen Anda di onSaveInstanceState jika fragmen tidak dibuat ulang saat perubahan orientasi.

 @Override
public void onSaveInstanceState(Bundle outState) {
    if (null != mCalFragment) {
        FragmentTransaction bt = getChildFragmentManager().beginTransaction();
        bt.remove(mFragment);
        bt.commit();
    }
    super.onSaveInstanceState(outState);
}
JosephM
sumber