片段未收到菜单回调

41

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

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

我正在使用在Activity中加载Fragment的FragmentTransaction,但是菜单回调(onCreateOptionsMenu, onPrepareOptionsMenuonOptionsItemSelected)没有被调用(我已经通过这些方法设置了一些断点),并且菜单也没有显示。

我错过了什么吗?我需要在我的Activity中添加什么吗?

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

编辑:我已经找到问题所在。我的AndroidManifest文件的目标版本是L11,这似乎隐藏了菜单按钮并阻止了回调函数的调用。然而,如果我从清单中删除targetSdkVersion=11,那么我会失去一些我需要的其他功能(例如列表中的激活状态)。有没有人知道如何解决这个问题(启用菜单按钮)而不必从Manifest中删除targetSdkVersion=11


你能确认你的片段是否真正被加载了吗?例如,在onCreate中是否被调用? - PJL
是的,我可以确认。此外,我已经尝试静态加载它,也就是在活动布局中定义片段,但仍然没有菜单。 - aromero
@PJL 谢谢,我会尝试调试这个库。这个片段确实有一个用户界面,其他一切都正常工作。 - aromero
我找到了问题。我会编辑我的问题。 - aromero
@PJL 你是在使用SDK 11进行编译吗?你是在使用Xoom进行测试吗?在我的情况下,只有移除targetSdkVersion=11才会有所不同。 - aromero
显示剩余5条评论
12个回答

29

Aromero, 别忘了使用片段版本的onCreateOptionsMenu方法进行重写,类似于这样:

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

顺便说一下,这是放置在片段中的,它会添加到活动的膨胀菜单中(如果有的话)。我自己也遇到了同样的问题,直到我弄清楚这个。

Kim


12
请确保在您的onCreate或onCreateView中调用setHasOptionsMenu(true)。 - Nelson Ramirez
5
在带有ViewPager的片段内部嵌套的片段中,此方法无法正常工作! - LOG_TAG

9
如果您在使用ActionBarSherlock时遇到了这个问题,您需要确保您的Fragments是SherlockFragments并不只是SupportFragments,并且您所覆盖的内容是:
public void onPrepareOptionsMenu (com.actionbarsherlock.view.Menu menu) {

不是

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

如果你采用后一种方法,你应该会得到某种关于函数被声明为final且无法覆盖的警告。这是一个警告,告诉你正在尝试覆盖错误的函数!
如果你通过将类从SherlockFragment切换为普通的Fragment来修复错误,你可以创建该函数......但它不会被调用。

这对我解决了问题,经过长时间的搜索。谢谢! - Darkhogg

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

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

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

  3. onOptionsItemSelected(MenuItem item)活动方法中,当菜单项操作将在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;
}

我希望这对您有所帮助。

祝好!


6

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

活动可以覆盖onOptionsItemSelectedonMenuItemSelected,但是片段只能覆盖onOptionsItemSelected

如果您在活动中覆盖onMenuItemSelected并在片段中覆盖onOptionsItemSelected,则您的片段覆盖将永远不会被触发。

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


男士。谢谢。这对我来说确实是这种情况...我不认为我会注意到两个不同的钩子... - tobi.b

5

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

此外,你还需要在片段内实现对onCreateOptionsMenu的重写。


4
另一个可能的情况是,当您在每个片段中使用一个通用操作的通用ID时,例如R.id.action_add。
今天我遇到了这样的情况:点击选项菜单[add]会调用“错误”的onOptionItemSelected,因为每个片段(使用DrawerLayout动态替换)都有相同的R.id.action_add。
简短地说,如果您遇到此类情况,请始终检查您的片段是否可见:
如果(!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。 - shekhar g h

3

当我使用ViewPagerIndicator和ActionBarSherlock一起使用时,我遇到了这个问题。尽管看起来已经修复了,但我仍然遇到了问题。我发现的解决方法是手动调用片段。

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

}

我在Sherlock Fragments中遇到了同样的问题。你把这个函数放在哪里?是在Activity还是Fragment中? - Paschalis
5
我原本将这段代码放在活动中,调用片段,但实际上这并不是必要的。在片段的onCreateView或onCreate方法中,只需调用setHasOptionsMenu(true); 然后当您重写onOptionsItemSelected和onCreateOptionsMenu时,它们会被正确调用。 - AlexUT

2
我已经找到了问题所在。AndroidManifest的目标SDK是11,这似乎隐藏了菜单按钮并阻止了回调的调用。我认为这破坏了菜单按钮的兼容性,因为在Android 3.0中似乎被动作栏取代了。

@aromro 如上所述,我正在针对SDK 11进行开发,没有问题。您的最小SDK版本设置是多少? - PJL
@PJL minSdkVersion 是8(因为我正在使用C2DM)。 - aromero

1

我认为你已经在继承自Fragment的类中实现了onCreateOptionsMenuonPrepareOptionsMenuonOptionsItemSelected方法。尝试将这些方法实现到加载该片段的活动类中。


是的,这些方法都在Fragment类中实现。Activity是一个“多窗格”活动,这意味着菜单只应在显示特定片段时显示。此示例在Fragment类中实现了onCreateOptionsMenu方法。http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentMenu.html - aromero
你可以很愉快地将菜单方法放在扩展Fragment的类中。 - PJL
是的,PJL,你说得对,我们可以把这些方法放在那里。但是当我尝试在片段类中使用它时,它没有起作用,当我改为主活动时它才起作用。也许我做错了什么。 - Labeeb Panampullan

1

来自Android开发者网站 - 链接

注意:如果您从片段(Fragment)中膨胀(inflate)菜单项,通过片段类的onCreateOptionsMenu()回调函数,当用户选择其中一个菜单项时,系统将为该片段调用onOptionsItemSelected()。但是,活动(activity)先有机会处理事件,因此系统首先调用活动上的onOptionsItemSelected(),然后才调用相同回调函数的片段。为确保活动中的任何片段也有机会处理回调,请始终将调用传递给超类作为默认行为,而不是在不处理该项时返回false

因此,Marco HC 是所有答案中最好的答案。


如果活动处理与片段相同的开关,则将调用活动。 - Renato Probst

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