当Fragment被替换时,清除由其添加的工具栏菜单选项

11

我设置了三个“顶级”片段,每个片段使用自己的Child Fragment Manager提供钻取式导航。这些顶级片段通过使用主Activity的Support Fragment Manager进行替换而切换。

其中一个顶级片段有一个子片段,在使用setHasOptionsMenu()onCreateOptionsMenu()添加菜单选项到工具栏/操作栏时运行良好。

现在我刚刚注意到的问题是:

当添加新的子片段并隐藏具有菜单项的子片段(并将事务添加到顶级片段的返回堆栈中)时,菜单项会消失。类似地,当撤销事务使该片段再次可见时,该菜单项会重新出现。这是期望的行为,似乎完全由Fragment框架处理。

然而,如果子片段可见(因此其菜单项出现在工具栏中),我切换至其他顶级片段时,菜单项仍然留在工具栏中

我原本希望该菜单项被清除,因为不仅属于它的子片段已被删除,甚至其父片段(即顶级片段之一)也已被完全替换(甚至没有添加到返回堆栈中,直接被替换了)。

我想我可以在传入的顶级片段已恢复时,在Activity中调用invalidateOptionsMenu(),但我觉得自动处理它的方法肯定是有的,只是我可能忽略了什么。

3个回答

13

onCreateOptionsMenu方法在每个片段创建或重新创建后都会被调用。 你需要做的是在填充新菜单XML之前清除菜单。 尝试这样做:

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState){
    super.onViewCreated(view, savedInstanceState);
    setHasOptionsMenu(true);
}

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

使用 clear() 而不是 OP 建议的 invalidateOptionsMenu() 的原因是什么? - lucidbrot
1
@lucidbrot 我使用clear()来清除堆栈菜单,因为如果你先有另一个片段带有其他菜单项,那么你将拥有所有的项目.. 你需要清除堆栈菜单。另一方面invalidateOptionsMenu()已经过时了。 - pablopatarca
我认为AppCompat版本并没有被弃用。如果您有另一个片段先出现,并替换它,则在无效时第一个菜单不应保留。 - lucidbrot
没关系,虽然它已经被弃用了,但你仍然可以使用 Activity.invalidateOptionsMenu - lucidbrot

1
最简单的方法是在你的片段中创建接口,并使用onAttach/onDetach方法检查片段的可见性:
public class QuickSetup1Fragment extends Fragment {
   private CallbackListener onCallbackListener;
   public QuickSetup1Fragment() {}

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.fragment_quick_setup1, container, false);
       return view;
   }

   @Override
   public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
      super.onViewCreated(view, savedInstanceState);
   }

   public interface CallbackListener{
      public void onAttach(); //fragment is visible
      public void onDetach(); //fragment is invisible/replace/destroy
   }

   @Override
   public void onAttach(Context context) {
       super.onAttach(context);
       try {
           onCallbackListener = (CallbackListener) context;
           onCallbackListener.onAttach();
       } catch (ClassCastException e) {
           throw new ClassCastException(context.toString()
                + " must implement CallbackListener");
       }
   }

   @Override
   public void onDetach() {
       super.onDetach();
       onCallbackListener.onDetach();
       onCallbackListener = null;
   }
}

在您的活动类上实现接口方法。
public class QuickSetupActivity extends AppCompatActivity implements QuickSetup1Fragment.CallbackListener{

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_image_view);
       Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
       setSupportActionBar(toolbar);
   }

   @Override
   protected void onPostCreate(@Nullable Bundle savedInstanceState) {
       super.onPostCreate(savedInstanceState);
   }

   @OnClick(R.id.backBtn)
   public void back(View v){
       super.onBackPressed();
   }

   @Override
   public void onAttach(){
      //do something with the menu
   }

   @Override
   public void onDetach(){
      //do something with the menu
   }
}

每次更改片段时,都会触发onAttach/onDetach。您可以在此处执行特定任务,例如管理菜单。

谢谢,我已经有一个类似的设置与我的顶级片段,它们实际上只是作为空片段,作为独立的回退堆栈从活动自己的回退堆栈中服务。这些片段与活动接口进行许多生命周期事件和诸如后退按钮按下之类的事情。我可以很容易地以这种方式使用invalidateOptionsMenu(),但我非常好奇为什么当片段被隐藏时,菜单似乎在Child Fragment Manager中自动管理,而在完全替换片段时不在Activity Frag. Manager中。我感觉我错过了什么。 - RobertoCuba

0
尝试在父片段中实现回调,在销毁子片段之前应该调用它!在回调内部放置oncreateoptions()。

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