Intereting Posts
Аутентификация Facebook без кнопки входа в систему Не удалось создать Android AVD из-за настроек Target и CPU / ABI? Как вызвать функцию родительской активности из ASyncTask? Как вы добавляете конфигурацию, специфичную для конкретной сборки, в APK? Как предоставить файлы данных для тестов на андроид Диалоговое обрезание с помощью AppCompat-v7 22 приводит к уродливым теням на api <21 Почему время отклика (для Rest Call) медленнее в Android по сравнению с ПК? Добавление растрового изображения водяного знака поверх видео в android: MediaMuxer 4.3 или ffmpeg Как использовать RecyclerView внутри NestedScrollView? Является ли разработка Android на C / C ++ быстрее, чем Java? Android Bitmap.createScaledBitmap бросает java.lang.OutOfMemoryError в основном на Jelly Bean 4.1 Сбой сертификата ssl от Charles не удалось из-за сбоев сети Добавление элементов в Endless Scroll RecyclerView с помощью ProgressBar внизу Динамическая настройка содержимого и макета виджета до размера, определяемого пользователем путем изменения размера. Android Android масштабирование изображений до плотности экрана

OnCreateOptionsMenu вызывается слишком много раз в ActionBar, используя вкладки

Вот моя проблема. У меня есть приложение, в котором я использую ActionBar Sherlock с вкладками, фрагменты с меню опций. Каждый раз, когда я вращаю эмулятор, меню добавляются для всех фрагментов, даже тех, которые были hidded / removed (я попробовал оба).

Это настройка: One FragmentActivity, которая имеет 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); 

Все вкладки используют один и тот же прослушиватель:

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

Каждый подкласс FragmentListBase имеет свое собственное меню, и поэтому все 3 подкласса имеют:

  setHasOptionsMenu(true); 

И соответствующие

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

Когда я запускаю приложение, я вижу, что onCreateOptionsMenu вызывается несколько раз для всех разных фрагментов.

Я полностью в тупике.

Я пробовал разместить большинство кода, насколько это возможно, не подавляя, если вы обнаружите, что чего-то не хватает, пожалуйста, сообщите.

[Edit] Я добавил больше записей, и оказывается, что фрагмент прикрепляется дважды (или больше) к вращению. Одна вещь, которую я замечаю, заключается в том, что все вызывается несколько раз, за ​​исключением метода onCreate (), который вызывается только один раз.

 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 

[Редактировать 2]

Хорошо, я начал отслеживать обратно в Android-код и нашел здесь эту часть (которую я редактировал, чтобы сократить этот пост).

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

Проблема в том, что mAdded действительно имеет в нем несколько экземпляров FragmentList1, поэтому метод onCreateOptionsMenu () «правильно» вызывается 3 раза, но для разных экземпляров класса FragmentList1. Я не понимаю, почему этот класс добавляется несколько раз … Но это адский пример.

Solutions Collecting From Web of "OnCreateOptionsMenu вызывается слишком много раз в ActionBar, используя вкладки"

Кажется, я нашел проблему (проблемы). Я говорю о проблемах, потому что на вершине множества меню теперь есть Исключение.

1) призыв к

  bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 

Который после вызовов addTab () имеет побочный эффект вызова onTabSelected (). Мой вкладчик TabListener добавит FragmentList1 в FragmentManager

2) вращение устройства уничтожит действие, как ожидалось, но не уничтожит Фрагменты. Когда новая активность создается после вращения, она выполняет две функции:

  1. Создайте еще один набор фрагментов, которые он добавит к FragmentManager. Это то, что вызывало множество меню
  2. Вызовите onTabSelected (через setNavigationMode ()), который выполнил бы следующий код:

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

В принципе, если фрагмент уже находится в FragmentManager, его не нужно добавлять, просто покажите его. Но есть проблема. Это не тот же Фрагмент! Это фрагмент, созданный более ранним экземпляром Activity. Поэтому он попытался бы прикрепить и показать этот вновь созданный фрагмент, который вызовет исключение

Решение.

Было несколько действий, чтобы исправить все это.

1) Я переместил setNavigationMode () над addTab () s.

2) вот как я теперь создаю свои вкладки:

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

Поэтому после создания Activity я должен проверить, находятся ли фрагменты уже в FragmentManager. Если я использую эти экземпляры, если нет, то создаю новые. Это делается для всех трех вкладок.

Возможно, вы заметили, что есть два похожих ярлыка: m_fragment.LIST_TAG и FragmentList1.LIST_TAG_STATIC. Ах, это прекрасно … (<- сарказм)

В ordrer для использования моего TagListener полиморфно я объявил следующую нестационарную переменную в базовом классе:

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

Он назначается изнутри потомков и позволяет мне смотреть в FragmentManager для разных потомков FragmentListBase.

Но мне также нужно искать конкретных потомков до их создания (потому что мне нужно знать, нужно ли их создавать или нет), поэтому мне также нужно объявить следующую статическую переменную.

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

Достаточно сказать, что я разочарован тем, что никто не придумал это простое и изящное решение (<- более сарказм)

Большое спасибо Джейку Уортону, который потратил время, чтобы посмотреть на это для меня 🙂

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

Это будет сохранять / восстанавливать отдельные состояния каждого из фрагментов при вращении.


Другим простым изменением, которое вы, возможно, захотите сделать, является вызов transaction.replace(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG) на вкладке, выбранный обратный вызов, и избавление от содержимого в невыбранном transaction.replace(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG) .

У меня были подобные проблемы с «штабелируемыми» меню при вращении. Я не использую вкладки, но я использую ViewPager с FragmentStatePagerAdapter, поэтому я не могу повторно использовать свои фрагменты. После того, как я ударил головой 2 дня, я нашел очень простое решение. Действительно, проблема, похоже, onCreateOptionsMenu с onCreateOptionsMenu вызываемой несколько раз. Этот небольшой фрагмент кода заботится (маскирует?) О всех проблемах:

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

Просто достаточно отметить ваши разочарования в полиморфных тегах.

Объявите свой базовый класс следующим образом:

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

Теперь объявите свои подкласс следующим образом:

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

Теперь полиморфный способ получить тег экземпляра выглядит так:

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

Получить тег статически так:

 FragmentList1.LIST_TAG; 

То, что сработало для меня, было перемещение setHasMenuOptions (true) в вызывающую деятельность, то есть активность, в которой был объявлен фрагмент. Ранее я использовал его в методе onCreate фрагмента.

Вот фрагмент кода:

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

По крайней мере, на SDK сотовой связи проблема решена путем добавления

 android:configChanges="orientation" 

К объявлению активности в вашем файле AndroidManifest.xml. Вы по-прежнему можете добавлять и удалять фрагменты, как показано в разделе «Добавление вкладок» в http://developer.android.com/guide/topics/ui/actionbar.html