【问题标题】:Fragment not receiving menu callbacks片段未接收菜单回调
【发布时间】:2011-09-10 21:30:20
【问题描述】:

我有一个片段类,它扩展了Fragment 并调用setHasOptionsMenu 来参与菜单。这个类还实现了onCreateOptionsMenuonPrepareOptionsMenuonOptionsItemSelected

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        ....
}

我在我的 Activity 中使用 FragmentTransaction 动态加载此片段(扩展 FragmentActivity)。

但是,没有调用任何菜单回调(onCreateOptionsMenuonPrepareOptionsMenuonOptionsItemSelected)(我已经在这些方法中使用一些断点进行了调试)并且没有显示菜单。

我错过了什么吗?我需要在我的活动中添加一些东西吗?

我正在使用 Android 兼容性库,使用 L11 SDK 进行编译并在 Xoom 中进行测试。

编辑:我发现了问题。我的 AndroidManifest 的目标是 L11,这似乎隐藏了菜单按钮并防止调用回调。但是,如果我从清单中删除它,我会失去一些我需要的其他功能(例如列表中的激活状态)。有谁知道如何在不从清单中删除targetSdkVersion=11 的情况下解决此问题(启用菜单按钮)?

【问题讨论】:

  • 您能否确认您的片段实际上正在被加载,即在 onCreate 被调用?
  • 是的,我可以确认。我也尝试过静态加载它,意思是在活动布局中定义片段,仍然没有菜单。
  • @aromero 没有什么明显的想法。您可以在片段类中实现这些方法。我假设 onCreateView 正在返回一个膨胀的视图,以便您的片段确实有一个 UI?如果不是,则不会显示菜单。如果是这样,那么我建议调试兼容性库源以查看消息未发送的原因。
  • @PJL 感谢将尝试调试库。该片段确实有一个 UI,其他一切都运行良好。
  • 我找到了问题所在。我将编辑我的问题。

标签: android android-fragments fragment


【解决方案1】:

阿罗梅罗, 不要忘记使用方法的片段版本覆盖 onCreateOptionsMenu,类似于:

    @Override
    public void onCreateOptionsMenu (Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.queue_options, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

顺便说一句,这会进入片段,并添加到 Activity 的膨胀菜单(如果有的话)。我自己也有同样的问题,直到我弄清楚了。

【讨论】:

  • 您还需要确保调用 setHasOptionsMenu(true);在你的 onCreate 或 onCreateView
  • 这不适用于带有 viewpager 的片段内的嵌套片段!
【解决方案2】:

如果您在使用 ActionBarSherlock 时遇到此问题,则需要确保您的 Fragment 是 SherlockFragments,而不仅仅是 SupportFragments,并且您要覆盖的是

public void onPrepareOptionsMenu (com.actionbarsherlock.view.Menu menu) {

不是

public void onPrepareOptionsMenu (android.view.Menu menu) {

如果你做后者,你应该得到一些关于函数是最终的并且你无法覆盖它的警告。这是一个警告,您正在尝试覆盖错误的功能!

如果通过将类从 SherlockFragment 切换为纯 Fragment 来修复错误,则可以创建函数。 . .但它不会被调用。

【讨论】:

  • 这为我解决了这个问题,经过长时间的谷歌搜索。谢谢!
【解决方案3】:

我遇到了同样的问题,但我认为最好总结并介绍最后一步以使其正常工作:

  1. 在 Fragment 的 onCreate(Bundle savedInstanceState) 方法中添加 setHasOptionsMenu(true) 方法。

  2. 在 Fragment 中覆盖 onCreateOptionsMenu(Menu menu, MenuInflater inflater)(如果您想在 Fragment 的菜单中执行不同的操作)和 onOptionsItemSelected(MenuItem item) 方法。

  3. onOptionsItemSelected(MenuItem item) Activity 的方法中,确保在onOptionsItemSelected(MenuItem item) Fragment 的方法中实现菜单项操作时返回false。

一个例子:

活动

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getSupportMenuInflater();
    inflater.inflate(R.menu.main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.activity_menu_item:
        // Do Activity menu item stuff here
        return true;
    case R.id.fragment_menu_item:
        // Not implemented here
        return false;
    default:
        break;
    }

    return false;
}

片段

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
    ....
}

@Override
public void onCreateOptionsMenu(Menu menu) {
    // Do something that differs the Activity's menu here
    super.onCreateOptionsMenu(menu, inflater);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.activity_menu_item:
        // Not implemented here
        return false;
    case R.id.fragment_menu_item:
        // Do Fragment menu item stuff here
        return true;
    default:
        break;
    }

    return false;
}

我希望这会有所帮助。

干杯。

【讨论】:

    【解决方案4】:

    如果您有一个活动和一个片段,每个都加载菜单项,那么您需要特别注意您使用哪些覆盖。

    Activity 可以覆盖 onOptionsItemSelectedonMenuItemSelected,但片段只能覆盖 onOptionsItemSelected

    如果您在 Activity 中覆盖 onMenuItemSelected 并在 Fragment 中覆盖 onOptionsItemSelected,您的 Fragment 覆盖将永远不会被触发。

    相反,在活动和片段中使用 onOptionsItemSelected

    【讨论】:

    • 男人。谢谢。对我来说就是这种情况......我想我不会注意到这两个不同的钩子......
    【解决方案5】:

    您需要确保在片段中调用setHasOptionsMenu(true); onCreateonCreateView

    您还需要在片段中实现onCreateOptionsMenu 的覆盖。

    【讨论】:

      【解决方案6】:

      另一种可能的情况是,当您在每个片段中为一个共同的操作使用一个共同的 id 时;例如 R.id.action_add

      今天我遇到了这样的情况:点击选项菜单 [add] 被调用了“错误”onOptionItemSelected,因为每个片段(使用DrawerLayout 动态替换)具有相同的R.id.action_add

      短篇小说,如果你有这种情况,请检查你的片段是否可见:

      if (!isVisible()) return false;

      长篇大论,关注onOptionItemSelected链!

        MainActivity
             |
             |  onOptionItemSelected
             +-----------------------
             |     return false
             |
       MyCoolFragment1
             |
             |  onOptionItemSelected
             +-----------------------
             |     return false
             |
       MyCoolFragment2
             |
             |  onOptionItemSelected
             +-----------------------
             |     return true
             |
      [item selection handled]
      

      如果您使用(类似)这样的方式添加片段:

      getSupportFragmentManager()
         .beginTransaction()
         .replace(R.id.content_frame, MyCoolFragment1.newInstance())
         .commit() 
      

      并且您为每个片段中的公共操作(比如说 R.id.action_add)定义了相同的 id; 不要忘记将这一行添加到每个: if (!isVisible()) return false;

      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          if (!isVisible()) return false; // <-- Not visible? skip!
      
          if (item.getItemId() == R.id.action_add) {
              //.TODO whatever
              return true;  //.Handled!
          }
      
          return false; //.Skip
      }
      

      【讨论】:

      • 老兄谢谢。在活动的 onOptionItemSelected 中返回 true,它没有到达 onOptionItemSelected 的片段。
      【解决方案7】:

      我在将 ViewPagerIndicator 与 ActionBarSherlock 结合使用时遇到了这个问题。虽然看起来这是fixed,但我仍然遇到了问题。我发现的解决方法是手动调用片段。

      @Override
      public boolean onOptionsItemSelected(MenuItem item)
      {
          Toast.makeText(this, "From activity", Toast.LENGTH_SHORT).show(); // TODO
          Fragment currentFragment = mAdapter.getItem(mPager.getCurrentItem());
          if (currentFragment != null && currentFragment instanceof SherlockFragment)
          {
              ((SherlockFragment)currentFragment).onOptionsItemSelected(item);
          }
          return super.onOptionsItemSelected(item);
      
      }
      

      【讨论】:

      • 我在夏洛克碎片中遇到了同样的问题。你把这个功能放在哪里?在活动中还是在片段中?
      • 我原本在activity里面有这段代码,调用了fragment,但这实际上是没有必要的。在片段的 onCreateView 或 onCreate 方法中只需调用 setHasOptionsMenu(true);然后,当您覆盖 onOptionsItemSelected 和 onCreateOptionsMenu 时,它们会被正确调用。
      【解决方案8】:

      我发现了问题。 AndroidManifest 的目标是 SDK 11,这似乎隐藏了菜单按钮并防止调用回调。我认为这破坏了在 Android 3.0 中似乎被操作栏取代的菜单按钮的兼容性

      【讨论】:

      • @aromro 如上所述,我的目标是 SDK 11 没有问题。您的最低 SDK 版本设置为多少?
      • @PJL minSdkVersion 为 8(因为我使用的是 C2DM)。
      【解决方案9】:

      我认为你已经在extends Fragment 的类中实现了onCreateOptionsMenuonPrepareOptionsMenuonOptionsItemSelected。尝试在加载此片段的 Activity 类中执行此操作

      【讨论】:

      • 是的,这些方法是在 Fragment 类中实现的。 Activity 是一个“多窗格”活动,这意味着只有在显示特定片段时才应显示菜单。本例实现了Fragment类developer.android.com/resources/samples/ApiDemos/src/com/…中的onCreateOptionsMenu@
      • 你可以很高兴地将菜单方法放在扩展 Fragment 的类中。
      • 是的 PJL,你说得对,我们可以把这个方法放在那里。但是一旦我尝试在片段类中它对我不起作用,当我更改为主要活动时它起作用了。可能是我做错了什么。
      【解决方案10】:

      我遇到了同样的问题,对我有用的解决方案是:

      1. 删除或评论任何 onOptionsItemSelected() ,onM​​enuItemSelected() 甚至 onPrepareOptionMenu() 并仅留在 Activity onCreateOptionsMenu() 中:

        @Override
        public boolean onCreateOptionsMenu(Menu menu){
        MenuInflater inflater=getMenuInflater();
        inflater.inflate(R.layout.menu, menu);
        return true;
        }
        
      2. 在 Fragment 类中,在 onCreateView() 中,放入:

        setHasOptionsMenu(true);
        
      3. 在 Fragment 类中添加:

        @Override
         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu,inflater);      
         }
        
        @Override
         public boolean onOptionsItemSelected(MenuItem item){           
                 switch(item.getItemId()){
                 case R.id.action_insert:
                    //doing stuff
                 return true;
                 }
                 return false;
             }
        

      在 Android 4.4 上测试并运行

      【讨论】:

        【解决方案11】:

        来自 android 开发者网站 - link

        注意:如果你从片段中膨胀菜单项,通过片段 类的 onCreateOptionsMenu() 回调,系统调用 当用户选择其中之一时,该片段的 onOptionsItemSelected() 那些项目。但是,该活动有机会处理该事件 首先,系统首先调用 onOptionsItemSelected() 活动,在为片段调用相同的回调之前。确保 活动中的任何片段也有机会处理 回调,始终将调用传递给超类作为默认值 行为而不是在您不处理该项目时返回 false

        因此Marco HC 是最好的答案。

        【讨论】:

        • 如果活动有一个开关处理与片段相同的项目,则将调用活动。
        【解决方案12】:

        如果您的工具栏是在父活动 xml 中定义的,请确保您在片段中执行此操作

        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            ....
            Toolbar toolbar = (Toolbar)getActivity().findViewById(R.id.toolbar);
            ((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
            setHasOptionsMenu(true);
        }
        

        当然,像下面这样覆盖 onCreateOptionsMenu

        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            inflater.inflate(R.menu.edit_menu, menu);
            super.onCreateOptionsMenu(menu, inflater);
        }
        

        这是唯一对我有用的解决方案!

        【讨论】:

          猜你喜欢
          • 2019-02-03
          • 2012-08-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多