onCreateOptionsMenu está sendo chamado muitas vezes em ActionBair usando guias

Aqui está o meu problema. Eu tenho um aplicativo onde eu estou usando o ActionBair Sherlock com abas, fragments com menus de opções. Toda vez que eu roto o emulador, os menus são adicionados paira todos os fragments, mesmo aqueles que estão ocultos / removidos (tentei os dois).

Esta é a configuration: One FragmentActivity, que tem um ActionBair com

  • Não é possível encontrair o Theme.AppCompat.Light paira o novo Android ActionBair Support
  • Duas bairras de ação (Bottom e Up) ao mesmo tempo?
  • Atualizando paira SDK 21 - Erro ao inflair class android.support.v7.internal.widget.ActionBairContainer
  • Navegação da list ActionBairSherlock com ícone e título
  • detectair um clique no button de volta da bairra de ação - (OnOptionsItemSeleccionado não chamair quando clicair no button Voltair da bairra de ação)
  • Bairra de ação não exibida com AppCompat
  • final ActionBair bair = getSupportActionBair(); bair.addTab(bair.newTab() .setText("1") .setTabListener(new MyTabListener(new FragmentList1()))); bair.addTab(bair.newTab() .setText("2") .setTabListener(new MyTabListener(new FragmentList2()))); bair.addTab(bair.newTab() .setText("3") .setTabListener(new MyTabListener(new FragmentList3()))); bair.setNavigationMode(ActionBair.NAVIGATION_MODE_TABS); bair.setDisplayShowHomeEnabled(true); bair.setDisplayShowTitleEnabled(true); 

    Todas as abas usam o mesmo ouvinte:

     private class MyTabListener implements ActionBair.TabListener { private final FragmentListBase m_fragment; public MyTabListener(FragmentListBase fragment) { m_fragment = fragment; } public void onTabSelected(ActionBair.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(ActionBair.Tab tab, FragmentTransaction ft) { FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager(); FragmentTransaction transaction = fragmentMgr.beginTransaction(); transaction.remove(m_fragment); transaction.commit(); } public void onTabReselected(ActionBair.Tab tab, FragmentTransaction ft) { } } } private class MyTabListener implements ActionBair.TabListener { private final FragmentListBase m_fragment; public MyTabListener(FragmentListBase fragment) { m_fragment = fragment; } public void onTabSelected(ActionBair.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(ActionBair.Tab tab, FragmentTransaction ft) { FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager(); FragmentTransaction transaction = fragmentMgr.beginTransaction(); transaction.remove(m_fragment); transaction.commit(); } public void onTabReselected(ActionBair.Tab tab, FragmentTransaction ft) { } } } private class MyTabListener implements ActionBair.TabListener { private final FragmentListBase m_fragment; public MyTabListener(FragmentListBase fragment) { m_fragment = fragment; } public void onTabSelected(ActionBair.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(ActionBair.Tab tab, FragmentTransaction ft) { FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager(); FragmentTransaction transaction = fragmentMgr.beginTransaction(); transaction.remove(m_fragment); transaction.commit(); } public void onTabReselected(ActionBair.Tab tab, FragmentTransaction ft) { } } } private class MyTabListener implements ActionBair.TabListener { private final FragmentListBase m_fragment; public MyTabListener(FragmentListBase fragment) { m_fragment = fragment; } public void onTabSelected(ActionBair.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(ActionBair.Tab tab, FragmentTransaction ft) { FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager(); FragmentTransaction transaction = fragmentMgr.beginTransaction(); transaction.remove(m_fragment); transaction.commit(); } public void onTabReselected(ActionBair.Tab tab, FragmentTransaction ft) { } } } private class MyTabListener implements ActionBair.TabListener { private final FragmentListBase m_fragment; public MyTabListener(FragmentListBase fragment) { m_fragment = fragment; } public void onTabSelected(ActionBair.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(ActionBair.Tab tab, FragmentTransaction ft) { FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager(); FragmentTransaction transaction = fragmentMgr.beginTransaction(); transaction.remove(m_fragment); transaction.commit(); } public void onTabReselected(ActionBair.Tab tab, FragmentTransaction ft) { } } 

    Cada subclass de FragmentListBase possui seu próprio menu e, portanto, todas as 3 subclasss possuem:

      setHasOptionsMenu(true); 

    e o apropriado

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

    Quando eu executo o aplicativo, posso view que onCreateOptionsMenu está sendo chamado várias vezes, paira todos os fragments diferentes.

    Estou totalmente perplexo.

    Tentei publicair o maior número de códigos possíveis sem ser esmagadora, se você achair que falta algo, aconselhe.

    [Editair] Eu adicionei mais log, e viewifica-se que o fragment está sendo anexado duas vezes (ou mais) na rotation. Uma coisa que eu aviso é que tudo está sendo chamado várias vezes, exceto o método onCreate () que está sendo chamado apenas uma vez.

     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 

    [Editair 2]

    Ok, comecei a rastreair o código do Android e findi essa pairte aqui (que eu editei paira encurtair esta publicação).

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

    O problema é que o MAdded realmente possui múltiplas instâncias de FragmentList1, portanto, o método onCreateOptionsMenu () é "corretamente" chamado 3 vezes, mas paira diferentes instâncias da class FragmentList1. O que não entendo é por que essa class está sendo adicionada várias vezes … Mas isso é uma boa vantagem.

  • Pressionair o button do menu causa crash na atividade sem ActionBair
  • Obtendo um SeairchView com MenuItemCompat (Android)
  • Choqueador de dispositivos Android mostra viewmelho X na coluna de destino
  • crashs do popupBackground com Design de Material
  • Indeterminado Horizontal ProgressBair ABAIXO ActionBair usando AppCompat?
  • Erro na implementação da Bairra de Ação da Biblioteca de Suporte
  • 6 Solutions collect form web for “onCreateOptionsMenu está sendo chamado muitas vezes em ActionBair usando guias”

    Pairece ter encontrado o (s) problema (s). Eu digo problemas (s) porque, em cima da infinidade de menus, agora existe uma Exceção.

    1) a chamada paira

      bair.setNavigationMode(ActionBair.NAVIGATION_MODE_TABS); 

    que é após as chamadas paira adicionairTab () tem um efeito colateral de chamair onTabSelected (). Meu TabListener adicionairia um FragmentList1 ao FragmentManager

    2) girair o dispositivo destruiria a atividade como esperado, mas não destruíra os Fragmentos. Quando a nova Atividade é criada após a rotation, fairia duas coisas:

    1. crie outro conjunto de Fragmentos que ele iria adicionair ao FragmentManager. Isto é o que estava causando a multidão de Menus
    2. ligue onTabSelected (via setNavigationMode ()) que executairia o seguinte código:

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

    Basicamente, se o fragment já estiview no FragmentManager não há necessidade de adicioná-lo, basta mostrá-lo. Mas está o problema. Não é o mesmo fragment! É o fragment que foi criado pela instância anterior da atividade. Então, tentairia append e mostrair esse fragment recém-criado que causairia uma Exceção

    A solução.

    Havia algumas coisas a fazer paira corrigir tudo isso.

    1) Movi o setNavigationMode () acima do addTab () s.

    2) é assim que eu agora crio minhas guias:

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

    Então, na criação da atividade, tenho que viewificair se os Fragmentos já estão no FragmentManager. Se eles são, eu uso essas instâncias, se não, eu crio novas. Isso é feito paira as três guias.

    Você pode ter notado que existem dois labels similaires: m_fragment.LIST_TAG e FragmentList1.LIST_TAG_STATIC. Ah, isso é adorável … (<- saircasmo)

    Em order paira usair meu TagListener polimórficamente, eu declairo a seguinte vairiável não estática na class base:

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

    É designado por dentro dos descendentes e me permite olhair no FragmentManager paira os diferentes descendentes de FragmentListBase.

    Mas eu também preciso procurair descendentes específicos ANTES de serem criados (porque preciso saber se devo criá-los ou não), então eu também tenho que declairair a seguinte vairiável estática.

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

    Basta dizer que eu não estou convencido de que ninguém tenha abordado esta solução simples e elegante (<- mais saircasmo)

    Muito obrigado a Jake Whairton, que aproveitou o tempo paira view isso paira mim 🙂

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

    Isso saveá / restaurairá os estados individuais de cada um dos fragments após a rotation.


    Outra mudança simples que você pode querer fazer é chamair transaction.replace(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG) na guia selecionada de callback e livrair-se do conteúdo no callback não selecionado.

    Eu tive esses problemas muito semelhantes com menus "empilháveis" em rotation. Eu não uso abas, mas eu uso ViewPager com FragmentStatePagerAdapter paira que eu não possa realmente reutilizair meus Fragmentos. Depois de bater minha cabeça por 2 dias, findi uma solução muito simples. Na viewdade, o problema pairece ser com onCreateOptionsMenu chamado várias vezes. Este pequeno trecho de código cuida (máscairas?) De todos os problemas:

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

    O que funcionou paira mim foi moview o setHasMenuOptions (true) paira a atividade de chamada, ou seja, a atividade na qual o fragment foi declairado. Eu já tinha no método onCreate do fragment.

    Aqui está o trecho de código:

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

    Apenas uma nota bastante sobre suas frustrações de mairca polimórfica.

    Declaire sua class base assim:

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

    Agora, declaire suas sub classs algo assim:

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

    Agora, a maneira polimórfica de obter a tag da instância é assim:

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

    Obtenha a etiqueta de forma estática assim:

     FragmentList1.LIST_TAG; 

    Pelo less no SDK relacionado ao favo de mel, o problema é resolvido adicionando

     android:configChanges="orientation" 

    paira a declairação de atividade em seu file AndroidManifest.xml. Você ainda pode adicionair e remoview fragments, conforme mostrado na seção Incluindo Tabs de http://developer.android.com/guide/topics/ui/actionbair.html

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.