onCreateOptionsMenu使用的标签动作条中被调用次数过多 [英] onCreateOptionsMenu is being called too many times in ActionBar using tabs

查看:149
本文介绍了onCreateOptionsMenu使用的标签动作条中被调用次数过多的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是我的问题。我有一个应用程序,我现在用的ActionBar福尔摩斯与标签,片段,选项菜单。我每次旋转模拟器,菜单增加了对所有被hidded /删除(我想两者都有)。

Here is my problem. I have an app where I am using ActionBar Sherlock with tabs, fragments with option menus. Every time I rotate the emulator, menus are added for all the fragments even those that are hidded/removed (I tried both).

这是设置:一个FragmentActivity,有一个动作条与

This is the setting: One FragmentActivity, that has an ActionBar with

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

中的选项卡都使用相同的监听器:

The tabs all use the same Listener:

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个亚类有:

Each subclass of FragmentListBase has its own menu and therefore all 3 subclasses have :

  setHasOptionsMenu(true);

和相应的

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

  inflater.inflate(R.menu.il_options_menu, menu);
}

当我运行应用程序,我可以看到onCreateOptionsMenu被多次调用,对于所有的不同片段。

When I run the app I can see that the onCreateOptionsMenu is being called multiple times, for all the different fragments.

我完全难住了。

我试过张贴最code地没有被压倒,如果你发现少了东西,请大家指教。

I tried posting the most code as possible without being overwhelming, if you find that something is missing, please advise.

我增加了更多的记录,而且事实证明,该片段被连接两次(含)以上的旋转。我注意的一件事是,一切都被称为除的onCreate()方法被调用一次多次。

I added more logging, and it turns out that the fragment is being attached twice (or more) on rotation. One thing that I notice is that everything is being called multiple times except for the onCreate() method which is being called only once.

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

好吧,我开始回溯到Android的code,发现这部分在这里(我编辑,以缩短这个职位)。

Ok, I started tracing back into Android code and found this part here (that I edited to shorten this post).

/com_actionbarsherlock/src/android/support/v4/app/FragmentManager.java

/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类的不同实例。我不明白的是为什么这个类被添加多次......但是,这是一个好头地狱。

The problem is that mAdded does indeed have multiple instances of FragmentList1 in it, so the onCreateOptionsMenu() method is "correctly" being called 3 times, but for different instances of the the FragmentList1 class. What I don't understand is why that class is being added multiple times... But that is a hell of a good lead.

推荐答案

我似乎找到了(S)的问题。我说,因为在众多菜单的顶部的问题上,现在还有一个例外。

I seem to have found the problem(s). I say problem(s) because on top of the multitude of menus, there is now also an Exception.

1)在调用

  bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

其中是的的来电来addTab()具有主叫onTabSelected的副作用()。然后我TabListener将一个FragmentList1添加到FragmentManager

which is after the calls to addTab() has a side effect of calling onTabSelected(). My TabListener would then add a FragmentList1 to the FragmentManager

2)旋转设备会破坏活动如预期,但不会破坏碎片。当新的活动被旋转后创建它会做两件事情:

2) rotating the device would destroy the Activity as expected, but would not destroy the Fragments. When the new Activity is created after rotation it would do two things :

  1. 创建另一组片段,它会添加到FragmentManager的。这是什么导致大量菜单的
  2. 调用onTabSelected(通过setNavigationMode()),这将执行以下code:

  1. create another set of Fragments that it would add to the FragmentManager. This is what was causing the multitude of Menus
  2. call onTabSelected (via setNavigationMode()) which would perform the following code:

 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没有必要添加它,只是显示它。但是,这就是问题所在。这是不一样的片段!这是由活动的早期实例创建的片段。因此,它会尝试连接并显示这个新创建的片段,会导致异常

Basically if the fragment is already in the FragmentManager there is no need to add it, just show it. But there lies the problem. It's not the same Fragment! It's the Fragment that was created by the earlier instance of the Activity. So it would try to attach and show this newly created Fragment which would cause an Exception

解决方案。

有几个事情要做,以解决这一切。

There were a few things to do in order to fix all of this.

1)我搬到了setNavigationMode()以上addTab()秒。

1) I moved the setNavigationMode() above the addTab()s.

2),这是我现在该怎么创建我的标签:

2) this is how I now create my tabs:

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

所以,在活动创造我要检查,看是否该片段已在FragmentManager。如果他们是我使用的情况下,如果没有的话我创建新的。这样做是为所有三个选项卡

So upon Activity creation I have to check to see if the Fragments are already in the FragmentManager. If they are I use those instances, if not then I create new ones. This is done for all three tabs.

您可能已经注意到,有两个类似的标签:m_fragment.LIST_TAG和FragmentList1.LIST_TAG_STATIC。啊,这是可爱的...(小于 - 嘲讽)

You may have noticed that there are two similar labels: m_fragment.LIST_TAG and FragmentList1.LIST_TAG_STATIC. Ah, this is lovely... ( <- sarcasm)

在ordrer用我TagListener多态我已经宣布在基类以下的非静态变量:

In ordrer to use my TagListener polymorphically I have declared the following non static variable in the base class:

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

这是从后代内部分配的,可以让我看在FragmentManager为FragmentListBase的不同后代。

It is assigned from inside the descendents and allows me to look in the FragmentManager for the different descendents of FragmentListBase .

但我也需要寻找特定的后代,才创建了(因为我需要知道,如果我必须创建与否),所以我也必须声明如下静态变量。

But I also need to search for specific descendents BEFORE they are created (because I need to know if I must create them or not), so I also have to declare the following static variable.

public class FragmentList1 extends FragmentListBase {
   public final static String LIST_TAG_STATIC = "TAG_LIST_1";

   public FragmentList1() {
      LIST_TAG = LIST_TAG_STATIC;
   };
}

我只想说,我disapointed,没有人想出了这个简单而优雅的解决方案(小于 - 更多嘲讽)

Suffice to say that I am disapointed that nobody came up with this simple and elegant solution ( <- more sarcasm)

非常感谢给杰克沃顿商学院谁花时间来看看这对我来说:)

Thanks a lot to Jake Wharton who took the time to look at this for me :)

这篇关于onCreateOptionsMenu使用的标签动作条中被调用次数过多的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆