I have a problem with Fragments and BroadcastManager. In my application, I switched to one main action and used the new NavigationDrawer. All content is contained in fragments.
One fragment (user search) contains two tabs (search by name, search by criteria) using the "Side Navigation" navigation template from the Android Design website: it has a ViewPager with a FragmentStatePagerAdapter.
| Main Activity | | Main Activity | ------------------- ------------------- | Search Fragment | | Search Fragment | | >Tab 1< | Tab 2 | | Tab 1 | >Tab 2< | | View Pager | | View Pager | | Fragment 1 | | Fragment 2 |
I want both tabs to use the same action menu (options): search action. But only the active fragment should react to it.
I tried different approaches, the most annoying was that I cannot easily get the current fragment from the view pager directly (without relying on the details of the non-API implementation).
An approach
Now I use LocalBroadcast to notify fragments that the search has been clicked on. Each fragment registers a small Wrapper-Receiver in onResume (and deletes it in onPause), which forwards onReceive to the fragment itself (the method shown below). I override setMenuVisibility , which is a callback that FragmentStatePagerAdpater calls Fragments to find out which fragment is active. Only a fragment that has a visible menu will respond to the broadcast.
ActionBar Tab -> ViewPager -> ViewPagerAdapter -> Fragment.setMenuVisibility ActionBar Menu Action -> Broadcast -> ... -> BroadcastReceiver -> Fragments
Both fragments trigger a fragment transaction to display the search results and add them to the back stack.
Question The fragment operation works in general, but when I rotate the device and then click search, I get an IllegalStateException (I cannot catch after onSaveInstanceState).
@Override public void onReceive(Context context, Intent intent) { if (m_menuVisible) { final String s = ((TextView) getView().findViewById(R.id.search_text)).getText().toString(); SearchResultsFragment f = new UserSearchResultsFragment(); Bundle b = new Bundle(); b.putString(SearchResultsFragment.EXTRA_SEARCHNAME, s); f.setArguments(b); FragmentTransaction transaction = getSherlockActivity().getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.fragment_container_frame, f).addToBackStack(null).commit();
I tried changing the commit to commitAllowingStateLoss , but then I get an IllegalStateException with "Activity was destroyed."
Do you know what is wrong here? Iām at a loss what to do ...
Additional code:
MainActivities onCreate (based on NavigationDrawer sample) and selectItem
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.fragment_container_layout); setupDrawer(); // Sets up the drawer layout, adapter, etc. if (savedInstanceState == null) { selectItem(0); // Selects and adds the fragment(s) for the position } // Other setup stuff } private void selectItem(int position) { // update the main content by replacing fragments Fragment f = null; switch (position) { case 0: f = ... break; case 1: ... default: throw new IllegalArgumentException("Could not find right fragment"); } f.setRetainInstance(true); m_drawerList.setItemChecked(position, true); setTitle(mDrawerTitles.get(position).titleRes); // Hide any progress bar that might be visible in the actionbar setProgressBarIndeterminateVisibility(false); // When we select something from the navigation drawer, the back stack is discarded final FragmentManager fm = getSupportFragmentManager(); fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); fm.beginTransaction().replace(R.id.fragment_container_frame, f).commit(); // update selected item and title, then close the drawer m_drawerLayout.closeDrawer(GravityCompat.START); }
FragmentStatePagerAdapter in the failed tab fragment:
protected class TabPagerAdapter extends FragmentStatePagerAdapter { private List<String> m_fragmentTags = new ArrayList<String>(); public TabPagerAdapter(FragmentManager fm) { super(fm); } public void addFragments(List<String> list) { ViewPager pager = (ViewPager) getView().findViewById(R.id.viewpager); startUpdate(pager); for (String tag : list) { m_fragmentTags.add(tag); } finishUpdate(pager); notifyDataSetChanged(); } public void addFragment(String tag) { addFragments(Collections.singletonList(tag)); } @Override public Fragment getItem(int pos) {
A split version of the project, containing only basic information, is available at https://docs.google.com/file/d/0ByjUMh5UybW7cnB4a0NQeUlYM0E/edit?usp=sharing
Play it like this: run, you will see two tabs. Select and click the Search button in transaction ActionBar ā Fragment. Use the back button, rotate the device and repeat ā Fail! [The file may not be available after the bounty has ended or an error has been detected.]
Edit : (1) Clarified that onReceive lives in the context of the fragment (2) Added code for the main activity