Crash after adding item to action bar's options menu from Fragment followed by orientation change
Asked Answered
R

3

7

I'm using ActionBarSherlock and ViewPagerIndicator to display Fragments as tabs. One of those Fragments adds items to ActionBar:

private String[] mapNames;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
    // init an String array `mapNames` which is used when populating submenu
    // ...
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {

    inflater.inflate(R.menu.fragment_maps, menu);

    SubMenu mapNamesMenu = menu.findItem(R.id.map_names).getSubMenu();
    mapNamesMenu.clear();
    for (int i=0; i<mapNames.length; i++) {
        mapNamesMenu.add(1, i, Menu.NONE, mapNames[i]);
    }

    super.onCreateOptionsMenu(menu, inflater);
}

and in res/menu/fragment_maps.xml I have

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/map_names"
        android:title="Maps"
        android:icon="@drawable/maps_32"
        android:showAsAction="always|withText">
        <menu>
            <item android:id="@+id/placeholder_maps" />
        </menu>
    </item>
</menu>

Everything is working fine until I rotate my phone. After orientation change this menu becomes inaccessible (nothing happens when icon is clicked). Then if I rotate my phone again I get this error:

FATAL EXCEPTION: main
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.view.ViewRoot.setView(ViewRoot.java:532)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
at android.view.Window$LocalWindowManager.addView(Window.java:424)
at android.widget.PopupWindow.invokePopup(PopupWindow.java:912)
at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:824)
at com.actionbarsherlock.internal.widget.IcsListPopupWindow.show(IcsListPopupWindow.java:226)
at com.actionbarsherlock.internal.view.menu.MenuPopupHelper.tryShow(MenuPopupHelper.java:129)
at com.actionbarsherlock.internal.view.menu.MenuPopupHelper.show(MenuPopupHelper.java:102)
at com.actionbarsherlock.internal.view.menu.ActionMenuPresenter.onSubMenuSelected(ActionMenuPresenter.java:273)
at com.actionbarsherlock.internal.view.menu.MenuBuilder.dispatchSubMenuSelected(MenuBuilder.java:263)
at com.actionbarsherlock.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:892)
at com.actionbarsherlock.internal.view.menu.ActionMenuView.invokeItem(ActionMenuView.java:510)
at com.actionbarsherlock.internal.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:145)
at android.view.View.performClick(View.java:2494)
at android.view.View$PerformClick.run(View.java:9122)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3806)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
at dalvik.system.NativeStart.main(Native Method)

Any ideas how to solve it? I'm using Android 2.3.6

Edit: see test repository

Rambort answered 20/11, 2012 at 12:23 Comment(3)
try to call setHasOptionsMenu(true); from onActivityCreated not from onCreate.Diner
@Diner It did not helpRambort
Related issue: github.com/JakeWharton/ActionBarSherlock/issues/641Mauricemauricio
E
3

I think , This is a Context problem. That's why BadTokenException is occurring.

There are many possibilities behind this exception:

1) May be you are using "this" as a context-reference at some place where it actually needs YourActivity.this or the parent activity's context.

OR

2) From the log-cat i am guessing, you are trying to display a Pop-up window.

The problem may be, you are displaying Pop-up window too early (i.e. before the Activity life cycle completes.). So wait till the activity life cycle completes.

To defer showing the popup, you can refer this link.

In-short this problem is due to the below use-case:

An activity's reference is passed to the some component (i.e. like Toast, alert dialog, pop-up etc), and activity destroyed but that component is still alive or trying to use destroyed activity's context.

So make sure that there isn't any situation like this.

Hope this will you give you some hint about solving the problem.

Ellanellard answered 19/1, 2013 at 7:32 Comment(3)
I create my Fragment like this: https://mcmap.net/q/53900/-best-practice-for-instantiating-a-new-android-fragment I don't have any references to Context in my Fragment class.Rambort
Second case: Yes, it is some kind of popup. When menu icon is clicked in actionbar, dropdown menu appears. As I said, it is working fine until orientation changes. Correct if I'm wrong, but parent activity restarts at orientation change. Does the Fragment restart at the same time by itself? Why Fragment is trying to display that menu in expanded mode after orientation change? It would be perfectly ok for me, if only menu icon is visible in that case.Rambort
Does anyone know what Android uses as Context when you create a new Fragment with constructor that has no arguments? According to documents it is the same as Fragment.instantiate (Context context, String fname, Bundle args)Rambort
Z
2

This is your MainActivity:

public class BaseSampleActivity extends SherlockFragmentActivity {

        TestFragmentAdapter mAdapter;
        ViewPager mPager;
        PageIndicator mIndicator;

        protected ListFragment mFrag;



        @Override
        public void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);

            setContentView(R.layout.themed_titles);



//This adapter can load as many fragment as you want with different content, see later                  
                    mAdapter = new TestFragmentAdapter(getSupportFragmentManager());

                    mPager = (ViewPager)findViewById(R.id.pager);
                    mPager.setAdapter(mAdapter);
                    mPager.setCurrentItem(1);
                    mIndicator = (TitlePageIndicator)findViewById(R.id.indicator);
                    mIndicator.setViewPager(mPager);


        }

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            super.onCreateOptionsMenu(menu);
   //This show how to set up a Searhbar         
             SearchView searchView = new SearchView(getSupportActionBar().getThemedContext());
            searchView.setQueryHint("Procure pelo nome");
            searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {

                @Override
                public boolean onQueryTextSubmit(String query) {

                    // TODO Auto-generated method stub
    //              Intent search = new Intent(getApplicationContext(), SearchableActivity.class);
    //              search.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    //              search.putExtra("query", query);
    //              startActivity(search);

                    return true;
                }

                @Override
                public boolean onQueryTextChange(String newText) {
                    // TODO Auto-generated method stub
                    return false;
                }
            });

            menu.add("Search")
                .setIcon(R.drawable.ic_search_inverse)
                .setActionView(searchView)
                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
   //This is how to set up a SubMenu         
            SubMenu subMenu1 = menu.addSubMenu("Action Item");
            subMenu1.add(0, 1, 0, "Sample");
            subMenu1.add(0, 2, 0, "Menu");
            subMenu1.add(0, 3, 0, "Sair");

            MenuItem subMenu1Item = subMenu1.getItem();
            subMenu1Item.setIcon(R.drawable.ic_title_share_default);
            subMenu1Item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);




            return true;
        }



        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
  //This is a switch case to do when the SubMenu is clicked.              
                case 1:
                    Toast.makeText(BaseSampleActivity.this, "Now "+item.getItemId(), Toast.LENGTH_SHORT).show();
                    return true;
                case 2:
                    Toast.makeText(BaseSampleActivity.this, "Now = "+item.getItemId(), Toast.LENGTH_SHORT).show();
                    return true;
                case 3:
                    Toast.makeText(BaseSampleActivity.this, "Now = "+item.getItemId(), Toast.LENGTH_SHORT).show();

                    return true;

            }
            return super.onOptionsItemSelected(item);
        }


    }

This is your FragmentPagerAdapter:

 class TestFragmentAdapter extends FragmentPagerAdapter implements IconPagerAdapter {
//Here you set up the title of each fragment, its in portuguese.
        protected static final String[] CONTENT = new String[] { "CATEGORIAS", "PRINCIPAL", "AS MELHORES", };
        protected static final int[] ICONS = new int[] {
                R.drawable.perm_group_calendar,
                R.drawable.perm_group_camera,
                R.drawable.perm_group_device_alarms,
        };

        private int mCount = CONTENT.length;

        public TestFragmentAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {Fragment f = null;
        switch(position){
        case 0:
        {
//Here you can set up a lot of diferent fragment content, here I just created a simple //arraylistfragment
        f = new ArrayListFragment();
        // set arguments here, if required
        Bundle args = new Bundle();
        args.putInt(ArrayListFragment.ARG_position, position);
        f.setArguments(args);
        break;
        }
        case 1:
        {
            f = new ArrayListFragment();
            // set arguments here, if required
            Bundle args = new Bundle();
            f.setArguments(args);
            break;
        }
        case 2:
        {   
            f = new ArrayListFragment();
            // set arguments here, if required
            Bundle args = new Bundle();
            f.setArguments(args);
            break;
        }   
        default:
          throw new IllegalArgumentException("not this many fragments: " + position);
        }


        return f;
        }

        @Override
        public int getCount() {
            return mCount;
        }

        @Override
        public CharSequence getPageTitle(int position) {
          return TestFragmentAdapter.CONTENT[position % CONTENT.length];
        }



        @Override
        public int getIconResId(int index) {
          return ICONS[index % ICONS.length];
        }

        public void setCount(int count) {
            if (count > 0 && count <= 10) {
                mCount = count;
                notifyDataSetChanged();
            }
        }
    }

I just done an easy sample that you can easy understand how to implements ActionBarSherlock and ViewPagerIndicator.

I´d like to upload this to github, but it will take a while to understand how to do it, maybe you can teach me later.

Then I upload this in 4shared. http://www.4shared.com/rar/zOWrvmyu/ViewpagerandSherlock.html

If you have any question, ask me later.

Zephyrus answered 23/1, 2013 at 2:57 Comment(2)
You should give more detail here, regardless. Not everyone is willing to download your sample from a hosting site and answers with only links and without explanation don't stand well if that link dies.Maritamaritain
It looks like you are populating ActionBar contents directly from Activity. I need to add that SubMenu via Fragment, since I have multiple tabs and that menu is relevant only to one tab.Rambort
R
1

This worked for me but I don't know if there are some side effects.

Add this to Fragment's onCreate

setRetainInstance(true);

and this to AndroidManifest.xml to Activity containing the Fragment

android:configChanges="orientation"
Rambort answered 23/1, 2013 at 9:33 Comment(2)
The second one is not needed since you are retaining fragment across configuration changes. Bug you mentioned has something in common with dialogs inside sherlock library, so retaining instance is workaround but not a fix.Mauricemauricio
I know this is just a workaround so I have not marked this as accepted answer. :) I may have found one draw back from this approach: android:showAsAction="always|withText" that text is sometimes visible and other times not no matter what orientation is at the moment. I tried invalidateOptionsmenu()but it did not help.Rambort

© 2022 - 2024 — McMap. All rights reserved.