【问题标题】:Fragment backstack and toggle片段回栈和切换
【发布时间】:2016-05-26 09:40:01
【问题描述】:

我有一个特殊的用例,我需要在两个片段之间切换。我遇到的问题是,对于第二个片段,我需要保持它的状态,而唯一可行的方法是将其添加到 BackStack。

我依靠支持片段管理器来替换片段:

public void toggle() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
    if (fragment instanceof FragmentB && null != fragmentA) {
        // fragment B is visible - we should show fragment A

        getSupportFragmentManager().beginTransaction()
                                   .setCustomAnimations(R.anim.frag_fade_in, R.anim.frag_fade_out,
                                                        R.anim.frag_fade_in, R.anim.frag_fade_out)
                                   .replace(R.id.fragment_container, fragmentA)
                                   .commit();
    } else if (fragment instanceof FragmentA && null != fragmentB) {
        // fragment A is visible - we should show fragment B

         boolean isRestored = false;
         fragment = getSupportFragmentManager().findFragmentByTag(TAG_FRAG_B);
            if (null != fragment) {
                // Restore fragment state from the BackStack
                fragmentB = (FragmentB) fragment;
                isRestored = true;
            }

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction()
                                                                         .setCustomAnimations(R.anim.frag_fade_in,
                                                                                              R.anim.frag_fade_out,
                                                                                              R.anim.frag_fade_in,
                                                                                              R.anim.frag_fade_out);

        transaction.replace(R.id.fragment_container, fragmentB, TAG_FRAG_B);
        if(!isRestored){
          transaction.addToBackStack(TAG_FRAG_B)
        }
        transaction.commit();
    } else {
        // Just pop any fragments that were added - usually we won't get in here
        getSupportFragmentManager().popBackStack();
    }
}

结合 onBackPressed() 覆盖:

@Override
public void onBackPressed() {   
  if (isCurrentFragmentB()) {
      toggle();
  } else {
      // Back key was pressed and we are on fragment A - at this state we simply want to go back to the
      // previous section
      super.onBackPressed();
  }
}

使用这个实现,我确保我重用了片段 B 并保持它的状态,这样它就不会看起来像是每次都是从头开始创建的。我还确保当我返回时,我只能从片段 B 到 A,而不是从片段 A 到 B。

我遇到的问题是,当通过片段管理器调用super.onBackPressed();并添加多个片段(实际上是替换,因为我一次只想要一个活动片段)时,它会抛出异常:

java.lang.IllegalStateException: Fragment already added: FragmentA{af9c26b #0 id=0x7f0e00d3}

仅当活动片段为 FragmentA 时才会发生这种情况。我怀疑这是因为 BackStack 实现,但正如我所说,我只希望保留第二个。

我该如何解决这个问题?我错过了什么?

【问题讨论】:

    标签: android android-fragments fragmenttransaction fragment-backstack


    【解决方案1】:

    我已经设法解决这个问题,虽然它有点老套。

    因为我需要保持FragmentB的状态,所以我不得不将它添加到BackStack中,但这实际上会影响调用onBackPressed()时反转的转换。

    为了避免这种情况,我不得不更新后压的逻辑并手动处理这种情况

    @Override
    public void onBackPressed() {   
      if (isCurrentFragmentB()) {
          toggle();
      } else if (isCurrentFragmentA()) {
          getSupportFragmentManager().popBackStackImmediate(TAG_FRAG_A, FragmentManager.POP_BACK_STACK_INCLUSIVE);
          // Special case - because we added the fragment B to the BackStack in order to easily resume it's state,
          // this will fail as it will actually try to add fragment A again to the fragment manager (it
          // will try to reverse the last transaction)
          super.finish();
      } else {
          // Usual flow - let the OS decide what to do
          super.onBackPressed();
      }
    }
    

    另外,我还稍微优化了切换方法:

    public void toggle() {
        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
    
        @SuppressLint("CommitTransaction") FragmentTransaction transaction =
                    getSupportFragmentManager().beginTransaction()
                                               .setCustomAnimations(R.anim.frag_fade_in, R.anim.frag_fade_out,
                                                                    R.anim.frag_fade_in, R.anim.frag_fade_out);
    
        if (fragment instanceof FragmentB && null != fragmentA) {
            // fragment B is visible - we should show fragment A
    
            fragment = getSupportFragmentManager().findFragmentByTag(TAG_FRAG_A);
            if (null != fragment) {
                // Restore fragment state from the BackStack
                fragmentA = (FragmentA) fragment;
            }
    
            // Replace current fragment with fragment A and commit the transaction
            transaction.replace(R.id.fragment_container, fragmentA, TAG_FRAG_A).commit();
        } else if (fragment instanceof FragmentA && null != fragmentB) {
            // fragment A is visible - we should show fragment B
    
             fragment = getSupportFragmentManager().findFragmentByTag(TAG_FRAG_B);
                if (null != fragment) {
                    // Restore fragment state from the BackStack
                    fragmentB = (FragmentB) fragment;
                }
    
             // Replace current fragment with fragment B
             transaction.replace(R.id.fragment_container, fragmentB, TAG_FRAG_B);
    
             if (null == fragment) {
                  // No entry of the fragment B in the BackStack, we want to add it for future uses
                  transaction.addToBackStack(TAG_FRAG_B);
              }
    
              // Commit the transaction
              transaction.commit();
        } else {
            // Just pop any fragments that were added - usually we won't get in here
            getSupportFragmentManager().popBackStack();
        }
    }
    

    我希望这可以帮助其他需要类似流程的人。

    PS:我要保留的片段是SupportMapFragment,这样我的地图就不会在每次我想显示时都重新绘制、重新居中并填充数据。

    【讨论】:

      猜你喜欢
      • 2019-07-11
      • 1970-01-01
      • 2011-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-16
      相关资源
      最近更新 更多