从Fragment添加项到操作栏选项菜单后发生崩溃,并跟随方向更改

7
我正在使用ActionBarSherlockViewPagerIndicator显示片段作为选项卡。其中一个片段会向操作栏添加项目:
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);
}

而在res/menu/fragment_maps.xml中,我有以下内容:

<?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>

一切都很正常,直到我旋转手机。在方向改变后,此菜单变得无法访问(单击图标时不会发生任何事情)。然后,如果我再次旋转手机,就会出现此错误:

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)

有什么解决方法吗?我正在使用Android 2.3.6

编辑:请参见test repository


尝试从onActivityCreated而不是从onCreate调用setHasOptionsMenu(true); - Leonidos
@Leonidos 这并没有帮助到。 - Kuitsi
相关问题:https://github.com/JakeWharton/ActionBarSherlock/issues/641 - pixel
3个回答

3

我认为,这是一个上下文问题。这就是为什么会出现BadTokenException的原因。

这个异常背后有很多可能性:

1)也许你在某个地方将“this”作为上下文引用使用,但实际上需要使用YourActivity.this或父活动的上下文。

或者

2)从log-cat中猜测,你正在尝试显示一个弹出窗口。

问题可能是,你太早显示了弹出窗口(即在Activity生命周期完成之前)。因此,请等待Activity生命周期完成。

要推迟显示弹出窗口,可以参考此链接

简而言之,这个问题是由以下用例引起的:

活动的引用被传递给某个组件(例如Toast、alert dialog、pop-up等),但活动已经销毁,但该组件仍然存活或正在尝试使用已销毁的活动上下文。

因此,请确保没有这样的情况发生。

希望这能给你解决问题提供一些提示。


我像这样创建我的Fragment:https://dev59.com/questions/rGox5IYBdhLWcg3wYzQt#9245510 我的Fragment类中没有任何对Context的引用。 - Kuitsi
1
第二种情况:是的,它是某种弹出窗口。当在操作栏中点击菜单图标时,下拉菜单会出现。正如我所说,它在方向改变之前都能正常工作。如果我错了,请纠正我,但是父活动在方向改变时会重新启动。Fragment 是否会自行在同一时间重新启动?为什么 Fragment 在方向改变后尝试以展开模式显示该菜单?如果只有菜单图标可见,那对我来说就完全没问题了。 - Kuitsi
有人知道在使用没有参数的构造函数创建新Fragment时,Android使用什么作为上下文吗?根据文档,它与Fragment.instantiate(Context context, String fname, Bundle args)相同。 - Kuitsi

2

这是您的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);
        }


    }

这是您的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();
            }
        }
    }

我刚刚完成了一个简单的示例,您可以轻松了解如何实现ActionBarSherlock和ViewPagerIndicator。
我想将它上传到github,但需要一些时间来了解如何操作,也许您可以稍后教我。
然后我将其上传到4shared。http://www.4shared.com/rar/zOWrvmyu/ViewpagerandSherlock.html 如果您有任何问题,请稍后问我。

无论如何,您应该在此处提供更多详细信息。并非每个人都愿意从托管站点下载您的示例,如果链接失效,仅带有链接而没有解释的答案也不会很好地被接受。 - A--C
1
看起来你正在直接从Activity中填充ActionBar内容。我需要通过Fragment添加那个SubMenu,因为我有多个选项卡,而且那个菜单只与一个选项卡相关。 - Kuitsi

1
这对我有用,但我不知道是否会有一些副作用。
将此添加到Fragment的onCreate中。
setRetainInstance(true);

将此内容添加到包含Fragment的Activity的AndroidManifest.xml文件中

android:configChanges="orientation"

第二个不需要,因为您正在跨配置更改保留片段。您提到的错误与Sherlock库内的对话框有些共同之处,因此保留实例是一种解决方法,但并不是修复方法。 - pixel
我知道这只是一个解决方法,所以我没有把它标记为接受的答案。 :) 我可能已经发现了这种方法的一个缺点:android:showAsAction="always|withText",无论当前的方向如何,有时文本是可见的,有时则不可见。我尝试过invalidateOptionsMenu(),但没有帮助。 - Kuitsi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接