#android #android-fragments
#Android #android-фрагменты
Вопрос:
Я пытался разобраться в этом и, должно быть, неправильно понимаю, как здесь работают фрагменты, несмотря на много чтения, или я сталкиваюсь с ошибкой.
У меня есть одно действие, которое содержит FrameLayout
.
При создании действия я добавляю FragmentA
.
if (savedInstanceState == null) {
Fragment newFragment = FragmentA.newInstance();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragment_container, newFragment, FRAGMENT_A_TAG).commit();
}
FragmentA
onCreateOptionsMenu
метод вызывается, как я и ожидал (хотя, как ни странно onPrepareOptionsMenu
, вызывается дважды в первый раз?).
FragmentA
создает собственное меню, в данном случае просто элемент меню. При нажатии на пункт меню событие возвращается к действию для создания FragmentB
.
Fragment newFragment = FragmentB.newInstance();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragment_container, newFragment, FRAGMENT_B_TAG);
ft.addToBackStack(null);
ft.commit();
Теперь вот в чем проблема. На этом этапе я бы ожидал FragmentB
, что будут вызываться только ‘ы onCreateOptionsMenu
. Но это не так. FragmentA
вызывается ‘s onCreateOptionsMenu
, за которым следует FragmentB
‘s .
Основное действие не имеет никакого связанного меню, только фрагменты.
Почему это?
Если я использую ft.replace(...)
, у меня нет этой проблемы. Но это означает воссоздание представления каждый раз, когда появляется FragmentB, и я пытаюсь этого избежать.
Я надеюсь, что этого достаточно, чтобы продолжить, но вот код для действия и оба фрагмента для ясности.
public class MainActivity extends AppCompatActivity {
private static String FRAGMENT_A_TAG = "FRAGMENT_A_TAG";
private static String FRAGMENT_B_TAG = "FRAGMENT_B_TAG";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
// Watch for button clicks.
Button button = (Button) findViewById(R.id.btn_switch_fragment);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
addFragmentToStack();
}
});
if (savedInstanceState == null) {
// Do first time initialization -- add initial fragment.
Fragment newFragment = FragmentA.newInstance();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragment_container, newFragment, FRAGMENT_A_TAG).commit();
}
}
private void addFragmentToStack() {
Fragment newFragment = FragmentB.newInstance();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragment_container, newFragment, FRAGMENT_B_TAG);
ft.addToBackStack(null);
ft.commit();
}
}
public class FragmentA extends Fragment {
public FragmentA() {
// Required empty public constructor
}
public static FragmentA newInstance() {
FragmentA fragment = new FragmentA();
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
System.out.println("onCreate called");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
System.out.println("onCreateView called");
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_a, container, false);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
System.out.println("onPrepareOptionsMenu called");
super.onPrepareOptionsMenu(menu);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_example_a, menu);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
public void onDetach() {
super.onDetach();
}
@Override
public void onResume() {
super.onResume();
System.out.println("onResume called");
}
}
public class FragmentB extends Fragment {
public FragmentB() {
// Required empty public constructor
}
public static FragmentB newInstance() {
FragmentB fragment = new FragmentB();
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_b, container, false);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_example_b, menu);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
public void onDetach() {
super.onDetach();
}
}
Ответ №1:
Если вы вызываете:
ft.add(R.id.container, fragmentA, "tag").addToBackStack(null).commit();
FragmentA останется «возобновленной», а ее меню будет «раздуто». Затем, если вы вызываете:
ft.add(R.id.container, fragmentB, "tag").addToBackStack(null).commit();
FragmentA по-прежнему «возобновлена», и теперь FragmentB также находится в состоянии «возобновлено», и его меню также будет «раздуто».
Когда вам нужно обновить представление, вы должны использовать view.invalidate() , и этот метод «перерисует» представление.
Так и сказал…
Если у вашего MainActivity есть меню, он вызовет invalidateOptionsMenu(), чтобы перерисовать меню MainActivity, а также нарисовать меню FragmentA. После добавления FragmentB панель инструментов должна добавить свое меню, поэтому она вызовет invalidateOptionsMenu(), чтобы перерисовать меню MainActivity и FragmentA, а также нарисовать меню FragmentB. Вот почему он вызывается каждый раз, когда вы меняете фрагмент, потому что вид меню нужно перерисовывать.
Этого не произойдет, если вы используете
ft.replace(...)
потому что первый фрагмент будет уничтожен.
Надеюсь, это поможет понять.
Регистрируйте onCreateOptionsMenu вашего MainActivity, а также будет вызываться каждый раз, когда вы добавляете фрагмент с меню.
Комментарии:
1. Привет, спасибо за ответ. Это своего рода процесс, о котором я думал. Дело в том, что в моей основной деятельности нет меню, но я зарегистрировал
onCreateOptionsMenu
и заметил, что оно было вызвано. Чтобы уточнить, когда добавляется фрагмент B (после фрагмента A), поскольку меню фрагмента B нуждается в заполнении,invalidateOptionsMenu()
вызывается в activity, который, в свою очередь, вызываетonCreateOptionsMenu
любые связанные фрагменты, это правильно?2. Во фрагментах вам нужно указать, есть ли у него меню, иначе он не будет запускать onCreateOptionsMenu. В Activities [onCreateOptionsMenu] выполняется по умолчанию до завершения выполнения метода onCreate.
3. Я понимаю это, и вы увидите, что мои фрагменты указывают свое собственное menu (
setHasOptionsMenu(true);
) , но мой вопрос в том, когда добавляется новый фрагмент, автоматически ли вызывается действиеinvalidateOptionsMenu()
и, в свою очередь, затем запускается (если фрагмент имеет набор меню)onCreateOptionsMenu()
в каждом фрагменте, который был добавлен в действие? Я сам проведу быстрый тест, вручную вызвав действияinvalidateOptionsMenu()
.4. Похоже, это происходит. Если я добавляю два фрагмента в действие, даже если действие не указывает меню, и я добавляю два фрагмента,
onPrepareOptionsMenu()
вызывается для обоих фрагментов (как я и ожидал). Если я затем вызываюgetActivity().invalidateOptionsMenu()
каждый фрагментonCreateOptionsMenu()
, вызывается.5. Да, и если в прикрепленном фрагменте нет меню, onCreateOptionsMenu вызываться не будет. Это потому, что нет необходимости перерисовывать представление. Привет из Мексики.