Очистить опции меню панели инструментов, добавленные фрагментом при его замене

У меня есть набор из 3 фрагментов «верхнего уровня», каждый из которых использует свои собственные диспетчеры дочерних фрагментов, чтобы предложить навигацию по детализации. Эти фрагменты верхнего уровня переключаются путем замены с помощью диспетчера фрагментов поддержки основного действия.

У одного из этих фрагментов верхнего уровня есть дочерний фрагмент, который добавляет пункт меню на панель инструментов/панель действий с помощью setHasOptionsMenu() и onCreateOptionsMenu(), и это прекрасно работает.

Теперь проблема, которую я только что заметил, заключается в следующем:

Когда добавляется новый дочерний фрагмент и дочерний фрагмент с пунктом меню скрыт (и транзакция добавляется в задний стек фрагмента верхнего уровня), пункт меню исчезает. Точно так же, когда фрагмент снова становится видимым при отмене транзакции, пункт меню возвращается. Это желаемое поведение, и, похоже, оно полностью обрабатывается фреймворком Fragment.

ОДНАКО, если дочерний фрагмент виден (и, следовательно, его пункт меню присутствует на панели инструментов) и я переключаю фрагменты верхнего уровня, пункт меню остается на панели инструментов.

Я ожидал, что пункт меню будет очищен, так как не только дочерний фрагмент, которому он принадлежит, был сброшен, но даже его родительский фрагмент (один из фрагментов верхнего уровня) также был полностью заменен (даже не добавлен в backstack, сразу заменили).

Я полагаю, что мог бы просто вызывать invalidateOptionsMenu() для действия всякий раз, когда входящий фрагмент верхнего уровня возобновляется, но я чувствую, что мне не хватает чего-то, чтобы он обрабатывался автоматически, как при навигации внутри фрагмента верхнего уровня.


person RobertoCuba    schedule 14.04.2017    source источник


Ответы (3)


Метод 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);
}
person pablopatarca    schedule 19.09.2017
comment
В чем причина использования clear() вместо invalidateOptionsMenu(), как предлагает OP? - person lucidbrot; 29.01.2019
comment
@lucidbrot Я использую clear () для очистки меню стека, потому что, если у вас есть другой фрагмент сначала с другими элементами меню, у вас будут все элементы ... вам нужно очистить меню стека. С другой стороны, invalidateOptionsMenu() устарела. - person pablopatarca; 31.01.2019
comment
Я думаю, что версия AppCompat не устарела. И если у вас есть другой фрагмент сначала и заменить его, первое меню не должно остаться, если вы сделаете недействительным - person lucidbrot; 31.01.2019
comment
Неважно, это устарело, но вы можете использовать Activity.invalidateOptionsMenu - person lucidbrot; 08.02.2019

Самый простой способ — создать интерфейс в вашем фрагменте и проверить видимость фрагмента с помощью метода 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. Здесь вы можете выполнять определенные задачи, например управлять меню.

person Amad Yus    schedule 14.04.2017
comment
Спасибо, у меня уже есть аналогичная установка с моими фрагментами верхнего уровня, которые на самом деле являются просто пустыми фрагментами, которые служат независимыми стопками из собственной стопки активности. Эти фрагменты взаимодействуют с Activity для многих событий жизненного цикла и таких вещей, как нажатие кнопки «Назад». Я мог бы легко использовать invalidateOptionsMenu() таким образом, но мне очень любопытно, почему меню автоматически управляется в диспетчере дочерних фрагментов, когда фрагмент просто скрыт, но не во фрагменте активности. Диспетчер при полной замене фрагмента. Я чувствую, что что-то упускаю. - person RobertoCuba; 14.04.2017

Попробуйте реализовать обратный вызов в родительском фрагменте, который следует вызывать перед уничтожением дочернего фрагмента! Внутри этого обратного вызова поместите oncreateoptions()

person Sadham Hussain    schedule 14.04.2017