Ersetzen Sie das Fragment in einem ViewPager

268

Ich versuche Fragment mit einem ViewPagerusing the zu verwenden FragmentPagerAdapter. Was ich erreichen möchte, ist, ein Fragment auf der ersten Seite von ViewPagerdurch ein anderes zu ersetzen .

Der Pager besteht aus zwei Seiten. Der erste ist der FirstPagerFragment, der zweite ist der SecondPagerFragment. Klicken Sie auf eine Schaltfläche der ersten Seite. Ich möchte das FirstPagerFragmentdurch das NextFragment ersetzen .

Da ist mein Code unten.

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

        }
    }
}

... und hier die XML-Dateien

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>

Nun das Problem ... in welcher ID soll ich verwenden

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

?

Wenn ich verwende R.id.first_fragment_root_id, funktioniert der Ersatz, aber der Hierarchy Viewer zeigt ein seltsames Verhalten, wie unten.

Am Anfang ist die Situation

Nach dem Austausch ist die Situation

Wie Sie sehen, stimmt etwas nicht, ich erwarte, dass nach dem Ersetzen des Fragments derselbe Zustand wie im ersten Bild angezeigt wird.

Nudeln
quelle
Haben Sie dieses Layoutdiagramm irgendwie erstellt oder manuell erstellt?
Jeroen Vannevel
@ Nudeln: Ich muss das gleiche tun. Ihre Frage hat einen langen Thread mit vielen verschiedenen Antworten generiert. Was hat am Ende am besten funktioniert? Danke vielmals!
Narb
1
@JeroenVannevel Sie haben es vielleicht schon herausgefunden, aber dieses
Layoutdiagramm
Haben Sie ein
Albert

Antworten:

162

Es gibt eine andere Lösung, bei der der Quellcode von ViewPagerund nicht geändert werden FragmentStatePagerAdaptermuss und die mit der FragmentPagerAdaptervom Autor verwendeten Basisklasse funktioniert .

Ich möchte zunächst die Frage des Autors beantworten, welche ID er verwenden soll. Es ist die ID des Containers, dh die ID des Ansichtspagers selbst. Wie Sie wahrscheinlich selbst bemerkt haben, führt die Verwendung dieser ID in Ihrem Code dazu, dass nichts passiert. Ich werde erklären warum:

Um ViewPagerdie Seiten neu zu füllen, müssen Sie zunächst den Aufruf aufrufen notifyDataSetChanged(), der sich in der Basisklasse Ihres Adapters befindet.

Zweitens wird ViewPagermit der getItemPosition()abstrakten Methode überprüft, welche Seiten zerstört und welche aufbewahrt werden sollen. Die Standardimplementierung dieser Funktion wird immer zurückgegeben POSITION_UNCHANGED, wodurch ViewPageralle aktuellen Seiten beibehalten werden und Ihre neue Seite folglich nicht angehängt wird. Damit das Ersetzen von Fragmenten funktioniert, getItemPosition()muss es in Ihrem Adapter überschrieben werden und muss zurückgegeben werden, POSITION_NONEwenn es mit einem alten, ausgeblendeten Fragment als Argument aufgerufen wird.

Dies bedeutet auch, dass Ihr Adapter immer wissen muss, welches Fragment an Position 0 FirstPageFragmentoder angezeigt werden soll NextFragment. Eine Möglichkeit, dies zu tun, besteht darin, beim Erstellen einen Listener bereitzustellen FirstPageFragment, der aufgerufen wird, wenn es Zeit ist, Fragmente zu wechseln. Ich denke, dies ist eine gute Sache, damit Ihr Fragmentadapter alle Fragmentschalter und Aufrufe von ViewPagerund handhaben kann FragmentManager.

Drittens werden FragmentPagerAdapterdie verwendeten Fragmente mit einem Namen zwischengespeichert, der von der Position abgeleitet ist. Wenn sich also an Position 0 ein Fragment befand, wird es nicht ersetzt, obwohl die Klasse neu ist. Es gibt zwei Lösungen, aber die einfachste besteht darin, die remove()Funktion von zu verwenden FragmentTransaction, mit der auch das Tag entfernt wird.

Das war viel Text, hier ist Code, der in Ihrem Fall funktionieren sollte:

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

Hoffe das hilft jedem!

wize
quelle
24
funktioniert auch bei mir, aber wie implementiere ich die Zurück-Schaltfläche, um das erste Fragment anzuzeigen?
user1159819
6
Gute Erklärung, aber könnten Sie den vollständigen Quellcode dafür veröffentlichen, insbesondere die Klassen FirstPageFragment und SecondPageFragment.
Georgiecasey
6
Wie füge ich den FirstPageFragmentListener dem Bundle in newInstance () hinzu?
Miguel
4
Können Sie den Code hinzufügen, den Sie mit dem FirstPageFragment.newInstance()Listener-Parameter behandeln?
MiguelHincapieC
6
Dies funktioniert nur, wenn ich beim Entfernen des Fragments commitNow () anstelle von commit () aufrufe. Ich bin mir nicht sicher, warum mein Anwendungsfall anders ist, aber hoffentlich spart dies jemandem Zeit.
Ian Lovejoy
37

Ab dem 13. November 2012 scheint das Ersetzen von Fragmenten in einem ViewPager viel einfacher geworden zu sein. Google hat Android 4.2 mit Unterstützung für verschachtelte Fragmente veröffentlicht. Es wird auch in der neuen Android Support Library v11 unterstützt, sodass dies bis auf 1.6 zurückreicht

Es ist der normalen Art, ein Fragment zu ersetzen, sehr ähnlich, außer dass Sie getChildFragmentManager verwenden. Es scheint zu funktionieren, außer dass der verschachtelte Fragment-Backstack nicht geöffnet wird, wenn der Benutzer auf die Schaltfläche "Zurück" klickt. Gemäß der Lösung in dieser verknüpften Frage müssen Sie popBackStackImmediate () im untergeordneten Manager des Fragments manuell aufrufen. Sie müssen also onBackPressed () der ViewPager-Aktivität überschreiben, in der Sie das aktuelle Fragment des ViewPager abrufen und getChildFragmentManager (). PopBackStackImmediate () darauf aufrufen.

Es ist auch etwas schwierig, das Fragment anzuzeigen, das gerade angezeigt wird. Ich habe diese schmutzige "android: switcher: VIEWPAGER_ID: INDEX" -Lösung verwendet, aber Sie können auch alle Fragmente des ViewPager selbst verfolgen, wie in der zweiten Lösung auf dieser Seite erläutert .

Hier ist mein Code für einen ViewPager mit 4 ListViews mit einer Detailansicht, die im ViewPager angezeigt wird, wenn der Benutzer auf eine Zeile klickt und die Schaltfläche "Zurück" funktioniert. Ich habe der Kürze halber versucht, nur den relevanten Code einzuschließen. Hinterlasse also einen Kommentar, wenn du die vollständige App auf GitHub hochladen willst.

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
quelle
7
Kannst du deinen Code auf github hochladen? Vielen Dank.
Gautham
4
@georgiecasey: Ich habe API 17, Android 4.2 heruntergeladen, mit der ich getChildFragmentManager () verwenden konnte. Aber ich muss etwas in Ihrer Lösung vermissen, da sich jetzt alte und neue Fragmente auf dem Bildschirm überlappen. Hinweis: Ich versuche, Ihre Lösung zusammen mit dem TabsAdapter-Code des Google-Beispiels von developer.android.com/reference/android/support/v4/view/… zu verwenden . Vielen Dank für Vorschläge und / oder Referenzcode.
gcl1
27
Worauf bezieht sich R.id.products_list_linear? Bitte verlinken Sie wie angeboten auf Ihren Code.
Howettl
1
@howettl Die ID verweist auf das äußere Fragmentelement. Der Schlüssel zur Lösung dieses Problems ist die Verwendung verschachtelter Fragmente (auf diese Weise müssen Sie den Viewpager-Adapter nicht manipulieren).
Indrek Kõue
2
Hat es das jemals bis zum Github geschafft?
Baruch
32

Basierend auf der Antwort von @wize, die ich hilfreich und elegant fand, konnte ich teilweise das erreichen, was ich wollte, da ich wollte, dass die Fähigkeit zum ersten Fragment zurückkehrt, das einmal ersetzt wurde. Ich habe es geschafft, seinen Code ein bisschen zu modifizieren.

Dies wäre der 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();
}

Um die Ersetzung durchzuführen, definieren Sie einfach ein statisches Feld des Typs CalendarPageFragmentListenerund initialisieren Sie es mit den newInstanceMethoden der entsprechenden Fragmente und rufen Sie FirstFragment.pageListener.onSwitchToNextFragment()bzw. NextFragment.pageListener.onSwitchToNextFragment()auf.

mdelolmo
quelle
Wie führen Sie den Austausch durch? Es ist nicht möglich, den Listener zu initialisieren, ohne die onSwitchToNext-Methode zu überschreiben.
Leandros
1
Verlierst du nicht den in mFragmentAtPos0 gespeicherten Status, wenn du das Gerät drehst?
13.
@seb, ich habe diese Lösung tatsächlich verbessert, um mFragmentAtPos0beim Speichern des Aktivitätsstatus die Referenz zu speichern. Es ist nicht die eleganteste Lösung, funktioniert aber.
Mdelolmo
Sie können meine Antwort sehen . Es ersetzt Fragmente und kehrt zum ersten zurück.
Lyd
@mdelolmo Könnten Sie sich diese Frage zu ViewPager ansehen? Vielen Dank, dass Sie stackoverflow.com/questions/27937250/…
Hoa Vu
21

Ich habe eine Lösung implementiert für:

  • Dynamischer Fragmentaustausch innerhalb der Registerkarte
  • Pflege des Verlaufs pro Registerkarte
  • Arbeiten mit Orientierungsänderungen

Die Tricks, um dies zu erreichen, sind die folgenden:

  • Verwenden Sie die notifyDataSetChanged () -Methode, um die Fragmentersetzung anzuwenden
  • Verwenden Sie den Fragment-Manager nur für die Backstage und nicht für den Austausch von Fragmenten
  • Pflegen Sie den Verlauf mithilfe des Erinnerungsmusters (Stapel)

Der Adaptercode lautet wie folgt:

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

Wenn Sie zum ersten Mal alle Registerkarten hinzufügen, müssen Sie die Methode createHistory () aufrufen, um den anfänglichen Verlauf zu erstellen

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

Jedes Mal, wenn Sie ein Fragment durch eine bestimmte Registerkarte ersetzen möchten, rufen Sie auf: replace (endgültige int-Position, endgültige Klasse fragmentClass, endgültige Bundle-Argumente)

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

Bei gedrücktem Zurück müssen Sie die Methode back () aufrufen:

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

Die Lösung funktioniert mit der Sherlock-Aktionsleiste und mit der Wischgeste.

mspapant
quelle
Es ist toll! Könntest du es auf den Github legen?
Nicramus
Diese Implementierung ist die sauberste aller hier bereitgestellten und behebt jedes einzelne Problem. Es sollte die akzeptierte Antwort sein.
Dazedviper
Hatte jemand Probleme mit dieser Implementierung? Ich habe 2 davon. 1. Der erste Rücken registriert sich nicht - ich muss zweimal zurück klicken, damit er sich registriert. 2. Nachdem ich (zweimal) auf Tab a zurück
geklickt habe
7

tl; dr: Verwenden Sie ein Hostfragment, das für das Ersetzen des gehosteten Inhalts verantwortlich ist und den Verlauf der Rücknavigation verfolgt (wie in einem Browser).

Da Ihr Anwendungsfall aus einer festen Anzahl von Registerkarten besteht, funktioniert meine Lösung gut: Die Idee ist, den ViewPager mit Instanzen einer benutzerdefinierten Klasse zu füllen HostFragment, die in der Lage ist, den gehosteten Inhalt zu ersetzen und den eigenen Verlauf der Rücknavigation beizubehalten . Um das gehostete Fragment zu ersetzen, rufen Sie die Methode auf 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();
    }
}

Diese Methode ersetzt lediglich das Rahmenlayout durch die ID R.id.hosted_fragmentdurch das für die Methode bereitgestellte Fragment.

In meinem Tutorial zu diesem Thema finden Sie weitere Details und ein vollständiges Arbeitsbeispiel für GitHub!

marktani
quelle
Obwohl dies ungeschickter aussieht, ist dies meiner Meinung nach der beste (in Bezug auf Struktur und Wartungsfreundlichkeit) Weg, dies zu tun.
Sira Lam
Am Ende stoße ich gegen eine NPE. : / Kann die Ansicht, die ersetzt werden soll, nicht finden
Fariz Fakkel
6

Einige der vorgestellten Lösungen haben mir sehr geholfen, das Problem teilweise zu lösen, aber es fehlt noch eine wichtige Sache in den Lösungen, die in einigen Fällen zu unerwarteten Ausnahmen und Inhalten schwarzer Seiten anstelle von Fragmentinhalten geführt hat.

Die FragmentPagerAdapter- Klasse verwendet die Element-ID, um zwischengespeicherte Fragmente in FragmentManager zu speichern . Aus diesem Grund müssen Sie auch die Methode getItemId (int position) überschreiben , damit sie z. B. position für Seiten der obersten Ebene und 100 + position für Detailseiten zurückgibt . Andernfalls würde das zuvor erstellte Fragment der obersten Ebene anstelle des Fragments auf Detailebene aus dem Cache zurückgegeben.

Außerdem teile ich hier ein vollständiges Beispiel für die Implementierung tabulatorähnlicher Aktivitäten mit Fragment- Seiten mithilfe von ViewPager und Registerkarten-Schaltflächen mithilfe von RadioGroup , die das Ersetzen von Seiten der obersten Ebene durch detaillierte Seiten ermöglichen und auch die Schaltfläche "Zurück" unterstützen. Diese Implementierung unterstützt nur eine Ebene des Backstapelns (Artikelliste - Artikeldetails), die Implementierung des Backstackings auf mehreren Ebenen ist jedoch unkompliziert. Dieses Beispiel funktioniert in normalen Fällen recht gut, außer dass eine NullPointerException ausgelöst wird, wenn Sie beispielsweise zur zweiten Seite wechseln, das Fragment der ersten Seite ändern (obwohl es nicht sichtbar ist) und zur ersten Seite zurückkehren. Ich werde eine Lösung für dieses Problem veröffentlichen, sobald ich es herausgefunden habe:

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
quelle
Hmm, es gibt ein anderes Problem mit diesem Code. Wenn ich eine Aktivität aus einem Fragment starten möchte, das entfernt und erneut hinzugefügt wurde, wird die folgende Fehlermeldung angezeigt: "Fehler beim Speichern: Aktiv NewFirstFragment {405478a0} hat den Index: -1 gelöscht" (vollständige Stapelverfolgung: nopaste.info/547a23dfea.html) ). Wenn ich mir die Android-Quellen anschaue, stelle ich fest, dass es etwas mit Backstack zu tun hat, aber ich weiß noch nicht, wie ich es beheben soll. Hat jemand eine Ahnung?
Blackhex
2
Cool mit <code> mFragmentManager.beginTransaction (). Detach (old_fragment) .attach (fragment) .commit (); </ code> anstelle von <pre> mFragmentManager.beginTransaction (). SetTransition (FragmentTransaction.TRANSIT_FRAGMENT_OPEN) .remove ( old_fragment) .add (mContainer.getId (), fragment) .commit (); </ pre> im obigen Beispiel scheint die beiden Probleme zu beheben :-).
Blackhex
1
Wenn Sie diese Methode verwenden, stellen Sie sicher, dass Android Ihre Aktivität beim Drehen des Geräts nicht neu erstellt. Andernfalls wird der PagerAdapter neu erstellt und Sie verlieren Ihre mBackFragments. Dies wird auch den FragmentManager ernsthaft verwirren. Die Verwendung des BackStack vermeidet dieses Problem auf Kosten einer komplizierteren Implementierung von PagerAdapter (dh Sie können nicht von FragmentPagerAdapter erben.)
Norman
6

Ich habe einen ViewPager mit 3 Elementen und 2 Unterelementen für Index 2 und 3 erstellt und hier, was ich tun wollte.

Geben Sie hier die Bildbeschreibung ein

Ich habe dies mit Hilfe früherer Fragen und Antworten von StackOverFlow implementiert und hier ist der Link.

ViewPagerChildFragments

Rukmal Dias
quelle
Ihr Projekt schlägt fehl, wenn Sie das Gerät drehen.
Dory
6

So ersetzen Sie ein Fragment in einem ViewPagerSie können Quellcodes bewegen ViewPager, PagerAdapterund FragmentStatePagerAdapterfolgenden Code - Klassen in Ihrem Projekt und fügen.

in ViewPager:

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

in 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()stellt sicher, dass nach dem nächsten Aufruf von getItem()newFragment getFragmentPosition()die Position des Fragments in Ihrem Adapter zurückgegeben wird.

Um nun Fragmente zu ersetzen, rufen Sie auf

mAdapter.replaceFragmetns(mViewPager, oldFragment, newFragment);

Wenn Sie an einem Beispielprojekt interessiert sind, fragen Sie mich nach den Quellen.

AndroidTeam bei Mail.Ru
quelle
Wie sollten die Methoden getFragmentPosition () und handleGetItemInbalidated () aussehen? Ich kann getItem () nicht aktualisieren, um NewFragment zurückzugeben. Bitte helfen Sie.
Hallo! Das Testprojekt finden Sie unter diesem Link files.mail.ru/eng?back=%2FEZU6G4
AndroidTeam At Mail.Ru
1
Ihr Testprojekt schlägt fehl, wenn Sie das Gerät drehen.
14.
1
@ AndroidTeamAtMail.Ru der Link scheint abgelaufen zu sein. Bitte teilen Sie den Code
Sunny
Sie können meine Antwort sehen . Es ersetzt Fragmente und kehrt zum ersten zurück.
Lyd
4

Funktioniert hervorragend mit der AndroidTeam-Lösung, aber ich stellte fest, dass ich die Fähigkeit brauchte, ähnlich wie zurück zu gehen FrgmentTransaction.addToBackStack(null) . Wenn ich dies jedoch nur hinzufüge, wird das Fragment nur ersetzt, ohne den ViewPager zu benachrichtigen. Wenn Sie die bereitgestellte Lösung mit dieser geringfügigen Verbesserung kombinieren, können Sie zum vorherigen Status zurückkehren, indem Sie lediglich die onBackPressed()Methode der Aktivität überschreiben . Der größte Nachteil ist, dass jeweils nur einer zurückgesetzt wird, was zu mehreren Rückklicks führen kann

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

Hoffe das hilft jemandem.

Auch was das getFragmentPosition()angeht, ist es ziemlich getItem()umgekehrt. Sie wissen, welche Fragmente wohin gehen. Stellen Sie einfach sicher, dass Sie die richtige Position zurückgeben. Hier ein Beispiel:

    @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;
    }
Kenner
quelle
3

In Ihrer onCreateViewMethode containerist eigentlich eine ViewPagerInstanz.

Also einfach anrufen

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

ändert das aktuelle Fragment in Ihrem ViewPager.

Ivan Bajalovic
quelle
1
Nach stundenlangem Suchen war es diese eine Codezeile, die ich brauchte, damit alle meine Registerkarten richtig funktionierten. vpViewPager.setCurrentItem(1);. Ich ging das Beispiel jeder Person durch, wobei jedes Mal nichts passierte, bis ich endlich zu Ihrem kam. Danke dir.
Brandon
1
umwerfend ... das ist unglaublich !! Mir war nicht klar, dass die Containervariable der Viewpager selbst war. Diese Antwort muss zuerst untersucht werden, bevor alle anderen längeren beantwortet werden. Außerdem werden keine Listener-Variablen an den Fragmentkonstruktor übergeben, was die gesamte Implementierung vereinfacht, wenn Android Fragmente mit dem Standardkonstruktor no-arg automatisch neu erstellt, wenn sich die Konfiguration ändert.
Kevin Lee
3
Wechselt dies nicht nur zu einer vorhandenen Registerkarte? Wie hängt das mit dem Ersetzen von Fragmenten zusammen?
siehe
2
Stimmen Sie mit @behelit überein, dies beantwortet die Frage nicht, es wird nur ViewPagerauf eine andere Seite verschoben , es wird kein Fragment ersetzt.
Yoann Hercouet
2

Hier ist meine relativ einfache Lösung für dieses Problem. Der Schlüssel zu dieser Lösung zu verwenden , FragmentStatePagerAdapteranstatt FragmentPagerAdapterwie die erstere für Sie entfernen werden nicht verwendete Fragmente , während die später noch ihre Instanzen behalten. Das zweite ist die Verwendung von POSITION_NONEin getItem (). Ich habe eine einfache Liste verwendet, um meine Fragmente zu verfolgen. Meine Anforderung bestand darin, die gesamte Liste der Fragmente auf einmal durch eine neue Liste zu ersetzen. Die folgende Liste konnte jedoch leicht geändert werden, um einzelne Fragmente zu ersetzen:

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
quelle
2

Ich habe auch eine Lösung gemacht, die mit Stacks funktioniert . Es ist ein modularer Ansatz, sodass Sie nicht jedes Fragment und Detailfragment in Ihrem angeben müssen FragmentPagerAdapter. Es baut auf dem Beispiel von ActionbarSherlock auf, das sich ergibt, wenn ich direkt von der Google Demo App bin.

/**
 * 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) {
    }

}

Fügen Sie dies für die Funktion der Zurück-Schaltfläche in Ihrer MainActivity hinzu:

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

Wenn Sie den Fragmentstatus speichern möchten, wenn er entfernt wird. Lassen Sie Ihr Fragment die Schnittstelle implementieren und SaveStateBundlegeben Sie in der Funktion ein Bundle mit Ihrem Sicherungsstatus zurück. Holen Sie sich das Bundle nach der Instanziierung von this.getArguments().

Sie können eine Registerkarte wie folgt instanziieren :

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

funktioniert ähnlich, wenn Sie ein Fragment über einem Tab-Stapel hinzufügen möchten. Wichtig : Ich denke, es wird nicht funktionieren, wenn Sie zwei Instanzen derselben Klasse auf zwei Registerkarten haben möchten. Ich habe diese Lösung schnell zusammen gemacht, daher kann ich sie nur teilen, ohne Erfahrung damit zu haben.

seb
quelle
Beim Drücken der Zurück-Taste wird eine Nullzeiger-Ausnahme angezeigt, und mein ersetztes Fragment ist ebenfalls nicht sichtbar. Vielleicht mache ich also etwas falsch: Was sollte der richtige Aufruf sein, um Fragmente richtig zu ändern?
Jeroen
Update: Ich habe die meisten Probleme behoben. Wenn ich jedoch nach dem Hinzufügen eines neuen Fragments einen Zurück-Button drücke und dann zu einem anderen Tab und zurück gehe, wird eine Nullzeiger-Ausnahme angezeigt. Ich habe also zwei Fragen: 1: Wie kann ich den Verlauf eines Tabs entfernen und sicherstellen, dass nur das neueste Fragment (oben auf dem Stapel) noch vorhanden ist? 2: Wie kann ich die Nullzeiger-Ausnahme entfernen?
Jeroen
@Jeroen 1: Sie können den richtigen Stapel von mTabStackMap erhalten und dann jedes TabInfo-Objekt entfernen, das unter dem Stapel liegt. 2: Sehr schwer zu sagen, stellen Sie eine Frage mit dem Stacktrace, dann könnte ich versuchen, es herauszufinden Bist du sicher, dass das kein Fehler in deinem Code ist? Geschieht dies auch, wenn Sie zu einem Tab (und dann zurück) wechseln, der sich in der direkten Nachbarschaft befindet (rechts oder links)?
25.
Ok, ich bin auf ein neues Problem gestoßen: Wenn ich meinen Bildschirm drehe, scheint der Stapel wieder leer zu sein? Es scheint den Überblick über die vorherigen Artikel zu verlieren ...
Jeroen
1
@seb danke. Ich habe diesen Code verwendet, aber verschachtelte Fragmente sind jetzt möglich, sodass dies kein Problem mehr darstellt. :)
Garcon
2

Das Ersetzen von Fragmenten in einem Viewpager ist ziemlich aufwendig, aber sehr gut möglich und kann sehr schick aussehen. Zunächst müssen Sie den Viewpager selbst das Entfernen und Hinzufügen der Fragmente übernehmen lassen. Wenn Sie das Fragment in SearchFragment ersetzen, behält Ihr Viewpager seine Fragmentansichten bei. Sie erhalten also eine leere Seite, da das SearchFragment entfernt wird, wenn Sie versuchen, es zu ersetzen.

Die Lösung besteht darin, einen Listener in Ihrem Viewpager zu erstellen, der Änderungen außerhalb des Viewpagers verarbeitet. Fügen Sie diesen Code also zuerst am unteren Rand Ihres Adapters hinzu.

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

Anschließend müssen Sie in Ihrem Viewpager eine private Klasse erstellen, die als Listener fungiert, wenn Sie Ihr Fragment ändern möchten. Zum Beispiel könnten Sie so etwas hinzufügen. Beachten Sie, dass die gerade erstellte Schnittstelle implementiert wird. Wenn Sie diese Methode aufrufen, wird der Code in der folgenden Klasse ausgeführt.

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

Hier sind zwei wichtige Dinge zu beachten:

  1. fragAt0 ist ein "flexibles" Fragment. Es kann jeden Fragmenttyp annehmen, den Sie ihm geben. Dies ermöglicht es Ihnen, Ihr bester Freund zu werden, wenn Sie das Fragment an Position 0 in das gewünschte Fragment ändern.
  2. Beachten Sie die Listener, die im Konstruktor 'newInstance (Listener) platziert sind. So rufen Sie fragment0Changed (String newFragmentIdentification) `auf. Der folgende Code zeigt, wie Sie den Listener in Ihrem Fragment erstellen.

    statisch nextFragmentListener listenerSearch;

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

Sie könnten die Änderung in Ihrem anrufen 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");
    }

}

Dies würde den Code in Ihrem Viewpager auslösen, um Ihr Fragment an Position Null fragAt0 zu schalten und ein neues searchResultFragment zu werden. Es gibt zwei weitere kleine Teile, die Sie dem Viewpager hinzufügen müssten, bevor er funktionsfähig wird.

Eine davon wäre die getItem-Override-Methode des Viewpagers.

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

    }

Ohne dieses letzte Stück würden Sie immer noch eine leere Seite bekommen. Ein bisschen lahm, aber es ist ein wesentlicher Bestandteil des viewPager. Sie müssen die getItemPosition-Methode des Viewpagers überschreiben. Normalerweise gibt diese Methode POSITION_UNCHANGED zurück, wodurch der Viewpager angewiesen wird, alles gleich zu halten, sodass getItem niemals aufgerufen wird, das neue Fragment auf der Seite zu platzieren. Hier ist ein Beispiel für etwas, das Sie tun könnten

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
}

Wie gesagt, der Code ist sehr kompliziert, aber Sie müssen grundsätzlich einen benutzerdefinierten Adapter für Ihre Situation erstellen. Die Dinge, die ich erwähnt habe, werden es ermöglichen, das Fragment zu ändern. Es wird wahrscheinlich lange dauern, bis alles eingeweicht ist, damit ich geduldig bin, aber alles wird Sinn machen. Es lohnt sich, sich die Zeit zu nehmen, da es eine wirklich gut aussehende Anwendung sein kann.

Hier ist das Nugget für die Handhabung des Zurück-Buttons. Sie fügen dies in Ihre MainActivity ein

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

Sie müssen in FragmentSearchResults eine Methode namens backPressed () erstellen, die fragment0changed aufruft. Dies in Verbindung mit dem Code, den ich zuvor gezeigt habe, wird das Drücken der Zurück-Taste behandeln. Viel Glück mit Ihrem Code, um den Viewpager zu ändern. Es erfordert viel Arbeit, und soweit ich festgestellt habe, gibt es keine schnellen Anpassungen. Wie gesagt, Sie erstellen im Grunde genommen einen benutzerdefinierten Viewpager-Adapter und lassen ihn alle erforderlichen Änderungen mithilfe von Listenern verarbeiten

user3331142
quelle
Vielen Dank für diese Lösung. Aber haben Sie nach dem Drehen des Bildschirms irgendwelche Probleme gehabt? Meine App stürzt ab, wenn ich den Bildschirm drehe und versuche, ein anderes Fragment auf derselben Registerkarte neu zu laden.
AnteGemini
Es ist schon eine Weile her, seit ich Android-Entwicklung gemacht habe, aber ich vermute, dass es ein Datenelement gibt, auf das Ihre App nicht mehr zählt. Wenn Sie drehen, wird die gesamte Aktivität neu gestartet und kann zu Abstürzen führen, wenn Sie versuchen, Ihren vorherigen Status ohne alle benötigten Daten neu zu laden (z. B. aus dem Bundle in onPause, onResume)
user3331142
2

Dies ist mein Weg, um das zu erreichen.

Zunächst einmal Add Root_fragmentinnerhalb viewPagerTab , in dem Sie Schaltfläche klicken implementieren möchten fragmentEreignis. Beispiel;

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

Zuallererst RootTabFragmentsollte FragmentLayoutfür Fragmentwechsel eingeschlossen werden.

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

Dann RootTabFragment onCreateViewimplementieren fragmentChangeSie innen für IhreFirstPagerFragment

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

Implementieren Sie danach das onClickEreignis für Ihre Schaltfläche im Inneren FirstPagerFragmentund nehmen Sie die Fragmentänderung erneut vor.

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

Hoffe das wird dir helfen, Mann.

Min Khant Lu
quelle
1

Ich habe eine einfache Lösung gefunden, die auch dann gut funktioniert, wenn Sie neue Fragmente in der Mitte hinzufügen oder das aktuelle Fragment ersetzen möchten. In meiner Lösung sollten Sie überschreiben, getItemId()welche eindeutige ID für jedes Fragment zurückgegeben werden soll. Nicht wie standardmäßig positioniert.

Da ist es:

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

Hinweis: In diesem Beispiel FirstFragmentund SecondFragmenterstreckt sich abstrakte Klasse PageFragment, wobei das Verfahren hat getPage().

Sergei Vasilenko
quelle
1

Ich mache etwas Ähnliches wie Wize, aber in meiner Antwort kannst du zwischen den beiden Fragmenten wechseln, wann immer du willst. Und mit der wize-Antwort habe ich einige Probleme beim Ändern der Ausrichtung des Bildschirms und dergleichen. So sieht der PagerAdapter aus:

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

Der Listener, den ich in der Adaptercontaineraktivität implementiert habe, um ihn beim Anhängen an das Fragment zu setzen, ist die Aktivität:

    public class PagerContainerActivity extends AppCompatActivity implements ChangeFragmentListener {

//...

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


    }

//...
}

Dann in das Fragment setzen Sie den Listener, wenn Sie einen Aufruf anhängen:

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

Und zum Schluss der Zuhörer:

public interface changeFragmentListener {

    void onChange(boolean isNextFragment);

}
Jachumbelechao nach Mantekilla
quelle
1

Ich folgte den Antworten von @wize und @mdelolmo und bekam die Lösung. Vielen Dank, Tonnen. Ich habe diese Lösungen jedoch ein wenig optimiert, um den Speicherverbrauch zu verbessern.

Probleme, die ich beobachtet habe:

Sie speichern die Instanz, Fragmentderen ersetzt wird. In meinem Fall ist es ein Fragment, das hält, MapViewund ich dachte , es ist teuer. Also behalte ich das FragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED)statt sich Fragmentselbst bei.

Hier ist meine Implementierung.

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

Demo-Link hier .. https://youtu.be/l_62uhKkLyM

Für Demozwecke werden 2 Fragmente TabReplaceFragmentund DemoTab2Fragmentan Position zwei verwendet. In allen anderen Fällen verwende ich DemoTabFragmentInstanzen.

Erläuterung:

Ich gehe Switchvon Aktivität zu DemoCollectionPagerAdapter. Basierend auf dem Status dieses Schalters wird das richtige Fragment angezeigt. Wenn der Schalter Prüfung geändert wird, ich rufe die SwitchFragListeners‘ onSwitchToNextFragmentMethode, wo ich den Wert bin Ändern pagerAdapterPosChangedder Variablen POSITION_NONE. Erfahren Sie mehr über POSITION_NONE . Dies macht das getItem ungültig und ich habe Logiken, um das richtige Fragment dort zu instanziieren. Entschuldigung, wenn die Erklärung etwas chaotisch ist.

Nochmals ein großes Dankeschön an @wize und @mdelolmo für die ursprüngliche Idee.

Hoffe das ist hilfreich. :) :)

Lassen Sie mich wissen, ob diese Implementierung Mängel aufweist. Das wird für mein Projekt sehr hilfreich sein.

sha
quelle
0

Nach Recherchen fand ich eine Lösung mit Funktionscode. Erstellen Sie zunächst eine öffentliche Instanz für ein Fragment und entfernen Sie Ihr Fragment einfach für onSaveInstanceState, wenn das Fragment bei einer Änderung der Ausrichtung nicht neu erstellt wird.

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