为什么使用 FragmentTransaction add() 方法在每个片段中调用 onCreateOptionsMenu()?

Ric*_*cky 5 android android-fragments

我一直在试图弄清楚这一点,尽管阅读了很多书,但我肯定对片段在这里的工作方式有误解,或者我遇到了错误。

我有一个活动,其中包含一个FrameLayout.

创建活动时,我添加FragmentA.

    if (savedInstanceState == null) {            
        Fragment newFragment = FragmentA.newInstance();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.add(R.id.fragment_container, newFragment, FRAGMENT_A_TAG).commit();
    }
Run Code Online (Sandbox Code Playgroud)

FragmentAonCreateOptionsMenu方法按照我的预期被调用(虽然,奇怪的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();
Run Code Online (Sandbox Code Playgroud)

现在问题来了。在这个阶段,我希望只调用FragmentB's onCreateOptionsMenu。但事实并非如此。FragmentA'sonCreateOptionsMenu被调用,然后是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();

    }

}
Run Code Online (Sandbox Code Playgroud)

小智 3

如果您致电:

ft.add(R.id.container, fragmentA, "tag").addToBackStack(null).commit();
Run Code Online (Sandbox Code Playgroud)

FragmentA 将保持“恢复”状态,并且其菜单将“膨胀”。那么如果你打电话:

ft.add(R.id.container, fragmentB, "tag").addToBackStack(null).commit();
Run Code Online (Sandbox Code Playgroud)

FragmentA 仍处于“已恢复”状态,现在 FragmentB 也处于“已恢复”状态,并且其菜单也将“膨胀”。

当您需要更新视图时,您应该使用 view.invalidate(),该方法将“重绘”视图。

我说过了...

如果你的MainActivity有菜单,它会调用invalidateOptionsMenu()来重绘MainActivity的菜单并绘制FragmentA的菜单。添加FragmentB后,工具栏需要添加其菜单,因此它将调用invalidateOptionsMenu()来重绘MainActivity和FragmentA菜单,并绘制FragmentB的菜单。这就是为什么每次更改 Fragment 时都会调用它,因为菜单视图需要重新绘制。

如果您使用,则不会发生这种情况

ft.replace(...)
Run Code Online (Sandbox Code Playgroud)

因为第一个 Fragment 将被销毁。

希望有助于理解。


记录您的 MainActivity 的 onCreateOptionsMenu ,并且每次添加具有菜单的片段时也会被调用。