Обновление фрагмента в ответ на взаимодействие с Android-навигатором

Приложение Android, над которым я работаю, имеет единственную MainActivity, и каждый экран приложения реализован как фрагмент. Каждый фрагмент создается таким образом в MainActivity как переменная частного класса:

public class MainActivity extends Activity implements MainStateListener { private FragmentManager fm = getFragmentManager(); private BrowseFragment browseFragment = BrowseFragment.newInstance(); ... 

Существует один «фрейм фрагмента», который загружает каждый фрагмент экрана. При переключении экранов в приложении этот код вызывается для загрузки фрагмента:

 FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.frag_frame, incoming); ft.addToBackStack(null); ft.commit(); fm.executePendingTransactions(); 

Каждый фрагмент экрана имеет слушатель, который позволяет фрагменту вызывать различные методы в MainActivity:

 public void onAttach(Activity activity) { super.onAttach(activity); try { mainStateListener = (MainStateListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement MainStateListener"); } } 

Проблема, которую я испытываю, заключается в обновлении аспекта фрагмента из навигационного ящика, который выходит из MainActivity. В навигационном ящике необходимо обновить фрагмент, и он использует этот код для этого:

  navigationDrawer.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { browseFragment.doSomethingOnBrowserFragment(); } }); 

Все работает нормально, пока вы не измените ориентацию. Затем текущий фрагмент экрана загружается отлично (browseFragment). Но затем, когда вы нажимаете на ящик навигации, заставляя выполнить метод doSomethingOnBrowserFragment (), я получаю исключение с нулевым указателем из-за того, что сам объект mainStateListener (прикрепленный к элементу browseFragment) равен нулю. Из того, что я знаю о жизненном цикле Fragment, эта переменная не должна быть нулевой, потому что метод onAttach () выполняется сначала перед чем-либо и устанавливает переменную mainStateListener. Также, если у меня есть кнопка на этом браузереFragment, который использует объект mainStateListener (после изменения ориентации), нажатие кнопки никогда не имеет этой ошибки нулевого указателя.

Трассировки стека:

 08-04 16:23:28.937 14770-14770/co.openplanit.totago E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: co.openplanit.totago, PID: 14770 java.lang.NullPointerException at co.openplanit.totago.MapFragment.enableOfflineMode(MapFragment.java:489) at co.openplanit.totago.MainActivity.setMapMode(MainActivity.java:663) at co.openplanit.totago.MainActivity.itineraryMapDrawerSelectItem(MainActivity.java:610) at co.openplanit.totago.MainActivity.access$200(MainActivity.java:52) at co.openplanit.totago.MainActivity$5.onItemClick(MainActivity.java:420) at android.widget.AdapterView.performItemClick(AdapterView.java:299) at android.widget.AbsListView.performItemClick(AbsListView.java:1158) at android.widget.AbsListView$PerformClick.run(AbsListView.java:2957) at android.widget.AbsListView$3.run(AbsListView.java:3850) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5103) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:606) at dalvik.system.NativeStart.main(Native Method) 

Мне кажется, проблема заключается в том, что использование навигационного ящика фактически взаимодействует с жизненным циклом browseFragment и вызывает его отключение или что-то в этом роде.

Любые предложения о том, как разрешить это, будут высоко оценены.

Сохранение ссылки на фрагмент может оставить вас не в синхронизации со ссылкой на старый фрагмент, который является отдельным в случаях, когда Fragment Manager воссоздает фрагмент для вас.

Решение состоит в том, чтобы найти фрагмент в настоящее время в frag_frame, в псевдокоде:

 Fragment fragment = fm.findFragmentById(R.id.frag_frame); if(fragment instanceof BrowseFragment) { // do your stuff } 

Просто замените onclick listener кодом на это в mainactivity.

Ошибка возникает из-за ошибки NullPointerException,

Причина: Нулевой указатель, проходящий взамен второго столбца.

Решение: инициируйте класс фрагмента (новый фрагмент ())

 case R.id.home: hfragment = new homefragment(); FragmentTransaction hfragmentTransaction= getSupportFragmentManager().beginTransaction(); hfragmentTransaction.replace(R.id.frame, hfragment); hfragmentTransaction.commit(); //do ur task here or in fragment class return true; case R.id.notification: return true; default: Toast.makeText(getApplicationContext(),"Somethings Wrong",Toast.LENGTH_SHORT).show(); return true; 

Я замечаю, что вы в основном кэшируете фрагменты из-за кода:

 public class MainActivity extends Activity implements MainStateListener { private FragmentManager fm = getFragmentManager(); private BrowseFragment browseFragment = BrowseFragment.newInstance(); 

Но реализация этого может быть сложной задачей. Либо вы создаете код / ​​класс, который управляет этими фрагментами, например, используя Array фрагментов, или используйте класс, например FragmentPagerAdapter .

Если я могу предположить, не кешируйте фрагменты, так как вы должны понимать его жизненный цикл, кеширование – хорошая идея, только если макет фрагмента сложный. Просто просто создайте новый экземпляр этого кода в своем public void onItemClick() как в предложении Google @ Создание навигационного ящика , если вы его не читали. Фрагмент кода на веб-странице:

 private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { selectItem(position); } } private void selectItem(int position) { // Create a new fragment and specify the planet to show based on position Fragment fragment = new PlanetFragment(); Bundle args = new Bundle(); args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); fragment.setArguments(args); // Insert the fragment by replacing any existing fragment FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.content_frame, fragment) .commit(); 

Примечание. Новый экземпляр фрагмента выполняется с помощью new PlanetFragment() .

Я думаю, что может случиться так, что в вашем браузере Fragment вашей деятельности может быть иначе, чем BrowseFragment, который отображается менеджером фрагментов (который, кажется, работает нормально, как вы сказали, если вы нажмете кнопку в этом фрагменте).

При вращении активность создаст новый экземпляр BrowseFragment для вашей переменной browseFragment (которая не привязана к действию) – private BrowseFragment browseFragment = BrowseFragment.newInstance () запускается каждый раз, когда создается действие, но диспетчер фрагментов будет повторно использовать СУЩЕСТВУЮЩИЙ Экземпляр BrowseFragment, на который ваша переменная не указывает. Повторное использование BrowseFragment будет привязано и запустит этот код для обновления mainStateListener, неиспользуемый новый элемент browseFragment не будет привязан к действию, если вы не запустите фрагментированное приложение, которое его добавляет, поэтому mainStateListener в нем будет null (неинициализирован).

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

т.е.

 private static final String BROWSE_TAG = "browseFrag"; ft.replace(R.id.frag_frame, browseFragment, BROWSE_TAG); navigationDrawer.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Fragment browseFragment = fm.findFragmentByTag(BROWSE_TAG); if (browseFragment != null) { browseFragment.doSomethingOnBrowserFragment(); } } }); 

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

Пожалуйста, проверьте мой ответ здесь: IllegalStateException: не удается выполнить это действие после onSaveInstanceState с помощью ViewPager

Intereting Posts
Android: BroadcastReceiver при вставке / извлечении устройства USB-штекер в / из USB-порта Вспышка вспышки Nexus 4 не срабатывает, когда режим фокусировки установлен на непрерывное изображение Параметры Retrofit – @Body нельзя использовать с форматированием или многочастной кодировкой Проблема с развертыванием на App Engine – вы должны войти в систему для выполнения этого действия Android-Как обнаружить в резюме, что мое приложение было убито VM? UIL, Picasso – изображения в адаптере всегда перезагружаются при остановке прокрутки Android Fragment getActivity () = null Чтение файла в андроиде – разрешение файла запрещено Actionbarsherlock – изменить цвет строки панели действий Как правильно установить точку останова во вложенном классе с помощью JDB? Android ToggleButton Android Gradle Apache HttpClient не существует? Ориентация макета в коде Диалоговое взаимодействие с панелью действий MotionEvent GetY () и getX () возвращают неверные значения