onCreateOptionsMenu wird zu viele Male in ActionBar mit Tabs aufgerufen

Hier ist mein Problem. Ich habe eine App, wo ich mit ActionBar Sherlock mit Tabs, Fragmente mit Optionsmenüs. Jedes Mal, wenn ich den Emulator drehe, werden Menüs für alle Fragmente hinzugefügt, auch diejenigen, die hidded / entfernt sind (ich habe beide versucht).

Dies ist die Einstellung: Eine FragmentActivity, die eine ActionBar mit hat

  • FindViewById für MenuItem gibt null zurück
  • ActionBar - benutzerdefinierte Ansicht mit zentrierten ImageView, Action Items auf Seiten
  • Konvertieren eines ArrayAdapter in CursorAdapter für die Verwendung in einem SearchView
  • Unbestimmte Fortschritte in der Sherlock Action Bar zeigen
  • Einstellen von splitActionBarWhenNarrow von Java auf Android
  • ActionBar oder ActionBarSherlock - Ausblenden / Anzeigen der ActionBar
  • final ActionBar bar = getSupportActionBar(); bar.addTab(bar.newTab() .setText("1") .setTabListener(new MyTabListener(new FragmentList1()))); bar.addTab(bar.newTab() .setText("2") .setTabListener(new MyTabListener(new FragmentList2()))); bar.addTab(bar.newTab() .setText("3") .setTabListener(new MyTabListener(new FragmentList3()))); bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); bar.setDisplayShowHomeEnabled(true); bar.setDisplayShowTitleEnabled(true); 

    Die Tabs verwenden den gleichen Listener:

     private class MyTabListener implements ActionBar.TabListener { private final FragmentListBase m_fragment; public MyTabListener(FragmentListBase fragment) { m_fragment = fragment; } public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager(); FragmentTransaction transaction = fragmentMgr.beginTransaction(); transaction.add(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG); transaction.commit(); } public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager(); FragmentTransaction transaction = fragmentMgr.beginTransaction(); transaction.remove(m_fragment); transaction.commit(); } public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { } } 

    Jede Unterklasse von FragmentListBase hat ein eigenes Menü und daher haben alle 3 Unterklassen:

      setHasOptionsMenu(true); 

    und das entsprechende

     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { Log.d(TAG, "OnCreateOptionsMenu"); inflater.inflate(R.menu.il_options_menu, menu); } 

    Wenn ich die App ausführe, kann ich sehen, dass das onCreateOptionsMenu mehrmals aufgerufen wird, für alle Fragmente.

    Ich bin total stumped

    Ich habe versucht, den meisten Code wie möglich zu schreiben, ohne überwältigend zu sein, wenn Sie feststellen, dass etwas fehlt, bitte raten Sie.

    [Bearbeiten] Ich habe mehr Protokollierung hinzugefügt, und es stellt sich heraus, dass das Fragment zweimal (oder mehr) bei der Rotation angebracht wird. Eine Sache, die ich bemerke, ist, dass alles mehrmals genannt wird, außer für die onCreate () – Methode, die nur einmal aufgerufen wird.

     06.704:/WindowManager(72): Setting rotation to 0, animFlags=0 06.926:/ActivityManager(72): Config changed: { scale=1.0 imsi=310/260 loc=en_US touch=3 keys=1/1/2 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=35} 07.374:/FragmentList1(6880): onAttach 07.524:/FragmentList1(6880): onCreateView 07.564:/FragmentList1(6880): onAttach 07.564:/FragmentListBase(6880): onCreate 07.564:/FragmentList1(6880): OnCreateOptionsMenu 07.574:/FragmentList1(6880): OnCreateOptionsMenu 07.604:/FragmentList1(6880): onCreateView 

    [Bearbeiten 2]

    Ok, ich habe angefangen, wieder in Android-Code und fand diesen Teil hier (das ich bearbeitet, um diesen Beitrag zu verkürzen).

    /com_actionbarsherlock/src/android/support/v4/app/FragmentManager.java

     public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { if (mActive != null) { for (int i=0; i<mAdded.size(); i++) { Fragment f = mAdded.get(i); if (f != null && !f.mHidden && f.mHasMenu) { f.onCreateOptionsMenu(menu, inflater); } } } 

    Das Problem ist, dass mAdded tatsächlich mehrere Instanzen von FragmentList1 hat, also die onCreateOptionsMenu () – Methode wird "richtig" 3 mal genannt, aber für verschiedene Instanzen der FragmentList1 Klasse. Was ich nicht verstehe ist, warum diese Klasse mehrfach hinzugefügt wird … Aber das ist eine Hölle von einem guten Vorsprung.

  • unbestimmtProgressBar in ActionBar Styling - Polsterung Problem
  • Wie hat es Google geschafft? Slide ActionBar in der Android-Anwendung
  • ActionBarSherlock mit benutzerdefiniertem RelativeLayout zeigt keinen Titeltext an
  • Bewegen Sie die Aktionsleiste sherlock mit dem Schubladen-Navigationsmenü
  • Fehler: In <declare-styleable> SherlockSpinner kann das Attribut android nicht gefunden werden: popupPromptView
  • Import von Google-Play-Services lib in Intellij IDEA 12 (und 13)
  • 6 Solutions collect form web for “onCreateOptionsMenu wird zu viele Male in ActionBar mit Tabs aufgerufen”

    Ich scheine das Problem gefunden zu haben. Ich sage Problem (s), weil auf der Oberseite der Menge der Menüs gibt es jetzt auch eine Ausnahme.

    1) das rufen an

      bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 

    was nach dem Aufruf von addTab () ist, hat einen Nebeneffekt des Aufrufs vonTabSelected (). Mein TabListener würde dann dem FragmentManager eine FragmentList1 hinzufügen

    2) das Drehen des Gerätes würde die Aktivität wie erwartet zerstören, würde aber die Fragmente nicht zerstören. Wenn die neue Aktivität nach der Drehung erstellt wird, würde es zwei Dinge tun:

    1. Erstellen Sie einen anderen Satz von Fragmenten, die es dem FragmentManager hinzufügen würde. Das ist es, was die Menge der Menüs verursacht hat
    2. call onTabSelected (via setNavigationMode ()), die den folgenden Code ausführen würde:

        if (null != fragmentMgr.findFragmentByTag(m_fragment.LIST_TAG)) { transaction.attach(m_fragment); transaction.show(m_fragment); } else { transaction.add(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG); } 

    Grundsätzlich, wenn das Fragment bereits im FragmentManager ist, gibt es keine Notwendigkeit, es hinzuzufügen, zeig es einfach. Aber da liegt das problem Es ist nicht das gleiche Fragment! Es ist das Fragment, das von der früheren Instanz der Aktivität erstellt wurde. So würde es versuchen, dieses neu erstellte Fragment anzubieten und zu zeigen, das eine Ausnahme verursachen würde

    Die Lösung.

    Es gab ein paar Dinge zu tun, um all dies zu beheben.

    1) Ich habe den setNavigationMode () über die addTab () s verschoben.

    2) das ist, wie ich jetzt meine tabs erstellen:

      FragmentListBase fragment = (FragmentListBase)fragmentMgr.findFragmentByTag(FragmentList1.LIST_TAG_STATIC); if (null == fragment) { fragment = new FragmentList1(); } bar.addTab(bar.newTab() .setText("1") .setTabListener(new MyTabListener(fragment))); 

    Also nach Aktivität Schöpfung muss ich überprüfen, ob die Fragmente bereits im FragmentManager sind. Wenn sie sind, benutze ich diese Fälle, wenn nicht, dann schaffe ich neue. Dies geschieht für alle drei Registerkarten.

    Sie haben vielleicht bemerkt, dass es zwei ähnliche Labels gibt: m_fragment.LIST_TAG und FragmentList1.LIST_TAG_STATIC. Ah, das ist schön … (<- Sarkasmus)

    In ordrer, um meine TagListener polymorph zu verwenden, habe ich die folgende nicht-statische Variable in der Basisklasse deklariert:

     public class FragmentListBase extends Fragment { public String LIST_TAG = null; } 

    Es ist von innen in den Nachkommen zugewiesen und erlaubt mir, in den FragmentManager für die verschiedenen Nachkommen von FragmentListBase zu schauen.

    Aber ich muss auch nach bestimmten Nachkommen suchen, bevor sie erstellt werden (weil ich wissen muss, ob ich sie erstellen oder nicht), also muss ich auch die folgende statische Variable deklarieren.

     public class FragmentList1 extends FragmentListBase { public final static String LIST_TAG_STATIC = "TAG_LIST_1"; public FragmentList1() { LIST_TAG = LIST_TAG_STATIC; }; } 

    Es genügt zu sagen, dass ich enttäuscht bin, dass niemand mit dieser einfachen und eleganten Lösung (<- mehr Sarkasmus)

    Vielen Dank an Jake Wharton, der sich die Zeit genommen hat, das für mich zu sehen 🙂

     public FragmentListBase() { setRetainInstance(true); setHasOptionsMenu(true); } 

    Dies wird die einzelnen Zustände jedes der Fragmente bei der Rotation retten / wiederherstellen.


    Eine weitere einfache Änderung, die Sie vielleicht machen möchten, ist das Aufrufen von transaction.replace(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG) in der Registerkarte ausgewählt Rückruf und loszuwerden der Inhalt im nicht ausgewählten Callback.

    Ich hatte diese sehr ähnlichen Probleme mit "stapelbaren" Menüs auf Rotation. Ich benutze keine Tabs, aber ich benutze ViewPager mit FragmentStatePagerAdapter, damit ich meine Fragmente nicht wirklich wiederverwenden kann. Nachdem ich meinen Kopf für 2 Tage geschlagen habe, fand ich eine sehr einfache Lösung. In der Tat scheint das Problem mit onCreateOptionsMenu das mehrfach genannt wird. Dieser kleine Code-Snippet kümmert sich um alle Probleme:

     /** to prevent multiple calls to inflate menu */ private boolean menuIsInflated; @Override public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { if (!menuIsInflated) { inflater.inflate(R.menu.job_details_fragment_menu, menu); menuIsInflated = true; } } 

    Nur eine ganz Notiz auf deinem polymorphen Tag Frustrationen.

    Deklariere deine Basisklasse so:

     public abstract class ListFragmentBase { protected abstract String getListTag(); } 

    Jetzt erklären Sie Ihre Unterklassen so etwas wie folgt:

     public class FragmentList1 extends ListFragmentBase { public static final String LIST_TAG = "TAG_LIST_1"; @Override protected String getListTag() { return LIST_TAG; } } 

    Jetzt ist die polymorphe Art, die Instanz-Tag zu bekommen, wie folgt:

     ListFragmentBase frag = new FragmentList1(); frag.getListTag(); 

    Holen Sie sich den Tag statisch wie so:

     FragmentList1.LIST_TAG; 

    Was für mich gearbeitet hat, bewegte die setHasMenuOptions (true) auf die rufende Aktivität, dh die Aktivität, in der das Fragment deklariert wurde. Ich hatte es vorher in der OnCreate-Methode des Fragments.

    Hier ist das Code-Snippet:

     @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); ForecastFragment forecastFragment = new ForecastFragment(); forecastFragment.setHasOptionsMenu(true); fragmentTransaction.add(R.id.fragment, forecastFragment); fragmentTransaction.commit(); } 

    Zumindest auf Honeycomb verwandten SDK's das Problem wird durch Hinzufügen gelöst

     android:configChanges="orientation" 

    zu der Aktivitätsdeklaration in Ihrer AndroidManifest.xml Datei. Sie können noch Fragmente hinzufügen und entfernen, wie im Abschnitt "Tabs hinzufügen" von http://developer.android.com/guide/topics/ui/actionbar.html gezeigt

    Das Android ist ein Google Android Fan-Website, Alles über Android Phones, Android Wear, Android Dev und Android Spiele Apps und so weiter.