Android:保持Fragment运行

时间:2014-08-06 02:47:11

标签: android android-fragments

当我从replace拨打FragmentManager以打开新片段时,是否可以保持片段运行?

基本上我不希望在导航(通过replace方法)到另一个片段时暂停片段。

有可能吗? 或者正确的方法是,每当我需要打开它并恢复其先前的状态时,总是会实例化一个新的片段吗?

谢谢!

1 个答案:

答案 0 :(得分:9)

FragmentManger replace方法将完全破坏前一个片段,因此在每个事务onDestroyView()中,onDestroy()和onDetach()将在前一个片段上被调用。如果你想保持你的片段运行,你可以使用FragmentManger hide()和show()方法!它隐藏并显示碎片而不破坏它们。

所以首先将两个片段添加到片段管理器中,并隐藏第二个片段。

        fragmentManager.beginTransaction()
            .add(R.id.new_card_container, FragmentA)
            .add(R.id.new_card_container,FragmentB)
            .hide(FragmentB)
            .commit();

请注意,您只能在隐藏片段上调用show()。所以在这里你不能在FragmentA上调用show(),但这不是问题,因为通过隐藏和显示FragmentB你可以获得你想要的替换效果。

这是一种在片段之间来回切换的方法。

public void showOtherFragment() {

    if(FragmentB.isHidden()){
        fragmentManager.beginTransaction()
                .show(FragmentB).commit();

    } else {
        fragmentManager.beginTransaction()
                .hide(FragmentB).commit();
    }
}

现在,如果你把日志消息放在片段回调方法中,你会看到没有破坏(除了屏幕方向改变!),甚至视图也不会被破坏,因为onDistroyView没有被调用。

只有一个问题就是,第一次应用程序启动onCreateView()方法时会为每个片段调用一次(它应该是!)但是当方向更改onCreateView()时会为每个片段调用两次,这是因为片段一旦像往常一样创建,一次因为FragmentManger的附件(保存在bundle对象上)为了避免你有两个选项1)在onSaveInstaneState()回调中分离片段。

@Override
protected void onSaveInstanceState(Bundle outState) {
    fragmentManager.beginTransaction()
            .detach(FragmentA)
            .detach(FragmentB)
            .commit();

    super.onSaveInstanceState(outState);
}

它正在工作,但视图状态不会自动更新,例如,如果您有一个EditText,其文本将在每次方向更改发生时擦除。当然,您可以通过在片段中保存状态来解决这个问题,但如果使用第二个选项则不需要!

首先,我在onSaveInstaneState()方法中保存一个布尔值,以记住巫婆片段。

@Override
protected void onSaveInstanceState(Bundle outState) {
    boolean isFragAVisible = true;
    if(!FragmentB.isHidden())
        isFragAVisible = false;

    outState.putBoolean("isFragAVisible",isFragAVisible);

    super.onSaveInstanceState(outState);
}

现在在活动onCreate方法中我检查以查看savedInstanceState == null。如果是,那么如果没有活动是第二次创建的话。所以片段管理器已经包含了片段。所以我得到了片段管理器中我的片段的引用。我也确保显示正确的片段,因为它没有自动恢复。

    fragmentManager = getFragmentManager();

    if(savedInstanceState == null){

        FragmentA = new FragmentA();
        FragmentB = new FragmentB();
        fragmentManager.beginTransaction()
                .add(R.id.new_card_container, FragmentA, "fragA")
                .add(R.id.new_card_container, FragmentB, "fragB")
                .hide(FragmentB)
                .commit();

    } else {
        FragmentA = (FragmentA) fragmentManager.findFragmentByTag("fragA");
        FragmentB = (FragmentB) fragmentManager.findFragmentByTag("fragB");

        boolean isFragAVisible = savedInstanceState.getBoolean("isFragAVisible");
        if(isFragAVisible)
            fragmentManager.beginTransaction()
                    .hide(FragmentB)
                    .commit();
        else
            fragmentManager.beginTransaction()
                    .hide(FragmetA) //only if using transaction animation
                    .commit();
    }

到目前为止,如果不使用事务动画,您的片段将完美运行。如果这样做,您还需要显示和隐藏FragmentA。因此,当你想显示FragmentB时,首先隐藏FragmentA然后显示FragmentB(在同一个事务中),当你想要隐藏FragmentB时首先隐藏它并同时显示FragmentA(再次在同一个事务中)。这是我的卡片翻转动画代码(从developer.goodle.com下载)

public void flipCard(String direction) {
    int animationEnter, animationLeave;
    if(direction == "left"){
        animationEnter = R.animator.card_flip_right_in;
        animationLeave = R.animator.card_flip_right_out;
    } else {
        animationEnter = R.animator.card_flip_left_in;
        animationLeave = R.animator.card_flip_left_out;
    }

    if(cardBack.isHidden()){
        fragmentManager.beginTransaction()
                .setCustomAnimations(animationEnter, animationLeave)
                .hide(cardFront)
                .show(cardBack)
                .commit();

    } else {
        fragmentManager.beginTransaction()
                .setCustomAnimations(animationEnter,animationLeave)
                .hide(cardBack)
                .show(cardFront)
                .commit();
    }
}