在我使用FragmentManager
打开新的Fragment时,是否可以让旧的Fragment继续运行?
基本上,我希望在切换到其他Fragment时(通过replace
方法),不要暂停旧的Fragment。
这种情况是否可行? 或者说,正确的做法是每次需要打开Fragment时都实例化一个新的Fragment,并恢复其之前的状态吗?
谢谢!
在我使用FragmentManager
打开新的Fragment时,是否可以让旧的Fragment继续运行?
基本上,我希望在切换到其他Fragment时(通过replace
方法),不要暂停旧的Fragment。
这种情况是否可行? 或者说,正确的做法是每次需要打开Fragment时都实例化一个新的Fragment,并恢复其之前的状态吗?
谢谢!
FragmentManger替换方法会完全销毁先前的片段,因此在每次事务中,onDestroyView()、onDestroy()和onDetach()都将调用先前的片段。如果您想保持片段运行,则可以使用FragmentManger的隐藏和显示方法而不是销毁它们!它会隐藏和显示片段而不会销毁它们。
所以首先将两个片段添加到fragment manager中,并且还要隐藏第二个片段。
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,每次发生方向更改时,文本都会被清除。当然,您可以通过在片段中保存状态来简单地解决这个问题,但如果使用第二个选项,则无需执行此操作!
首先,在onSaveInstanceState()方法中保存一个Boolean值以记住显示的片段。
@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();
}
}