PopBackStack 但保留 android 中的第一个片段
PopBackStack but keep the first fragment in android
我在做分片交易,后台是这样的:
fragA => fragB => fragC => fragD
从 fragD
回来后,我想 return 到 fragA
fragD => onBackPress => fragA
所以,我尝试了如下代码:
getChildFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
但是它清除了所有的后台堆栈,我怎样才能将第一个片段保留在后台堆栈中?非常感谢
因为 "back stack" 具有类似堆栈的行为...后进先出...您添加到返回堆栈的最后一个片段将首先从返回堆栈中弹出。您将需要通过指定自己的行为来手动实现所需的行为。使用 FragmentManager
class 方法并不难。
如果您 "tag" 您将片段添加到交易中...
fragmentTransaction.add(new FragmentA(), "FragmentA_Tag");
您稍后可以确定在按下后退按钮时显示哪个片段...
FragmentA f = fragmentManager.findFragmentByTag("FragmentA_Tag");
if(f != null){
f.show();
}
您如何确定显示哪个片段完全取决于您。您可以跟踪当前可见片段,也可以使用 Fragment
class 的 isHidden
方法...顺便说一句,我在这里谈论的是原生片段,不支持库的片段.
后台包含有关交易的记录,而不是片段本身。
所以你不应该将第一个事务的记录 (null -> fragA) 添加到 backstack。
并且所有其他交易的记录都应该添加到后台。
在这种情况下,那么你 preformpopBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
android 删除了除 fragA 之外的所有片段,因为没有任何关于 fragA 是如何添加的记录。
例如您可以执行以下操作:
- 添加 fragA 但不将其添加到 backStack。所以它总是在
activity,并且不会对后退按钮做出反应。
- 当您打开 fragD 时,您应该 clear fragment BackStack。因此,当您从 D frag 按下后退按钮时,您会返回到 A。
P.S。还有其他方法可以做你想做的事。这取决于...
几天前,我开始学习 Android 中的片段。我也遇到了这个问题。在这里,我展示了我的解决方案以及我如何解决这个问题。如果我的代码不正确,请修复。此时我们有什么?活动,许多片段及其后台。我们想从抽屉菜单中打开每个片段,并从后台清除所有其他片段。但是,我们必须只持有一个 Home 片段。当我们停留在 Home fragment 上并且用户按下后退按钮时,应用程序将关闭。
Activity.class
protected void onCreate(Bundle savedInstanceState)
{
...
// adding Home fragment without adding transaction into backstack
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.container, HomeFragment.newInstance("args"), null);
ft.commit();
}
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish();
}
}
public void addFragmentFromMenu(Fragment fragment){
String backStateName = fragment.getClass().getName();
clearBackStack();
FragmentManager manager = getSupportFragmentManager();
if(manager.getBackStackEntryCount()> 0)
{
boolean fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped && manager.findFragmentByTag(backStateName) == null) {
//fragment not in back stack, create it.
addFragment(fragment, manager, backStateName);
}
}
else // no fragments
{
addFragment(fragment, manager, backStateName);
}
}
public void addFragment(Fragment fragment, FragmentManager manager, String backStateName)
{
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.container, fragment, backStateName);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();
}
public void clearBackStack() {
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
然后点击抽屉菜单项
@Override
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.nav_camera) {
addFragmentFromMenu(CameraFragment.newInstance("cam1", "cam2"));
} else if (id == R.id.nav_gallery) {
addFragmentFromMenu(TestFragment.newInstance("test1","test2"));
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
我做了以下方法。清除所有片段然后添加第一个主页片段
FragmentManager fm = getActivity().getSupportFragmentManager();
fm.popBackStack(Constants.TAG_HOME, FragmentManager.POP_BACK_STACK_INCLUSIVE);
((MainActivity) activity).manageFragment(new HomeFragment(), Constants.TAG_HOME);
// write down below function in main activity
public void manageFragment(Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (!fragmentManager.popBackStackImmediate(tag, 0)) {
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment, tag);
ft.addToBackStack(tag);
ft.commit();
}
}
1) 使用以下代码添加第一个片段
android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
if (fm.findFragmentById(R.id.fragment_container) != null) {
ft.hide(fm.findFragmentById(R.id.fragment_container));
}
ft.add(R.id.fragment_container, new OneFragment(),OneFragment.class.getCanonicalName())
.addToBackStack(OneFragment.class.getCanonicalName()).commit();
2) 使用以下代码从第一个片段添加第二个片段
android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
if (fm.findFragmentById(R.id.fragment_container) != null) {
ft.hide(fm.findFragmentById(R.id.fragment_container));
}
ft.add(R.id.fragment_container,new TwoFragment(),TwoFragment.class.getCanonicalName())
.addToBackStack(TwoFragment.class.getCanonicalName()).commit();
3) 使用以下代码从第二个片段添加第三个片段
android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
if (fm.findFragmentById(R.id.fragment_container) != null) {
ft.hide(fm.findFragmentById(R.id.fragment_container));
}
ft.add(R.id.fragment_container, new ThreeFragment(),ThreeFragment.class.getCanonicalName())
.addToBackStack(ThreeFragment.class.getCanonicalName()).commit();
4) 使用以下代码从第三个片段添加第四个片段
android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
if (fm.findFragmentById(R.id.fragment_container) != null) {
ft.hide(fm.findFragmentById(R.id.fragment_container));
}
ft.add(R.id.fragment_container, new FourFragment(),ThreeFragment.class.getCanonicalName())
.addToBackStack(FourFragment.class.getCanonicalName()).commit();
5) onBackPressed() 请写下代码
@Override
public void onBackPressed() {
hideKeyboard(MainActivity.this);
Fragment currentFragment = this.getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (currentFragment.getClass().getName().equalsIgnoreCase(FourFragment.class.getName())) { // Using this code come from third fragment to first fragment
Fragment f = this.getSupportFragmentManager().findFragmentByTag(TwoFragment.class.getCanonicalName());
if (f != null) {
this.getSupportFragmentManager().popBackStackImmediate(f.getClass().getCanonicalName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}else {
super.onBackPressed();
}
}
看了很多帖子,我是这样想的:
在 fragC => fragD 方法中,做两个事务:
1 清除返回堆栈,fragC => fragA
2 fragA => fragD
但是这样一来,fragA原来的状态可能会被破坏
public static void changeFragCtoD(FragmentManager fm, Fragment fragD){
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction
.replace(R.id.containerViewId, new fragAClass())
.commit();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction
.replace(R.id.containerViewId, fragD)
.addToBackStack(fragD.getClass().getName())
.commit();
}
现在按回 fragD 会回到 fragA。
记住我的话,完整的 FragmentTransaction
被添加到后台堆栈,而不仅仅是片段,这意味着即使您在单个事务中添加和删除片段,调用 poBackStack()
也会反转完整的事务。在其参数中传递标签会弹出所有交易,直到标记的交易甚至弹出标记的交易(如果 FragmentManager.POP_BACK_STACK_INCLUSIVE
添加到参数)
所以更多的是关于你如何添加它而不是你如何删除它。
参见 Performing Fragment Transactions
我知道现在回答您的问题为时已晚,但这可能对其他人有所帮助。
由于这几天一直在寻找解决方案...我已经使用for循环解决了这个问题,我认为这是最简单的方法。
首先,这样初始化一个整数
int count = getSupportFragmentManager().getBackStackEntryCount();
其中 getBackStackEntryCount() 将计算事务数。
然后在调用 fragD 中的 FragmentTransaction 方法之前添加这个 for 循环
for (int i = 0; i < count; i++){
getSupportFragmentManager().popBackStack();
}
for 循环将为您完成任务,popBackStack() 将 return 您转到 fragA
看起来像这样
int count = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < count; i++){
getSupportFragmentManager().popBackStack();
}
fragmentTransaction.replace(R.id.frame_layout, new FoodFragment(), "Food").addToBackStack(null);
fragmentTransaction.commit();
您可以覆盖 onbackpressed(Mainactivity)并使用片段管理器的 getBackStackEntryCount(),您可以检查它是否不等于 1 并仅在该条件下弹出堆栈。
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
if (!(getSupportFragmentManager().getBackStackEntryCount() == 1)) {
getSupportFragmentManager().popBackStack() ;
}
}
}
我在做分片交易,后台是这样的:
fragA => fragB => fragC => fragD
从 fragD
回来后,我想 return 到 fragAfragD => onBackPress => fragA
所以,我尝试了如下代码:
getChildFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
但是它清除了所有的后台堆栈,我怎样才能将第一个片段保留在后台堆栈中?非常感谢
因为 "back stack" 具有类似堆栈的行为...后进先出...您添加到返回堆栈的最后一个片段将首先从返回堆栈中弹出。您将需要通过指定自己的行为来手动实现所需的行为。使用 FragmentManager
class 方法并不难。
如果您 "tag" 您将片段添加到交易中...
fragmentTransaction.add(new FragmentA(), "FragmentA_Tag");
您稍后可以确定在按下后退按钮时显示哪个片段...
FragmentA f = fragmentManager.findFragmentByTag("FragmentA_Tag");
if(f != null){
f.show();
}
您如何确定显示哪个片段完全取决于您。您可以跟踪当前可见片段,也可以使用 Fragment
class 的 isHidden
方法...顺便说一句,我在这里谈论的是原生片段,不支持库的片段.
后台包含有关交易的记录,而不是片段本身。
所以你不应该将第一个事务的记录 (null -> fragA) 添加到 backstack。
并且所有其他交易的记录都应该添加到后台。
在这种情况下,那么你 preformpopBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
android 删除了除 fragA 之外的所有片段,因为没有任何关于 fragA 是如何添加的记录。
例如您可以执行以下操作:
- 添加 fragA 但不将其添加到 backStack。所以它总是在
activity,并且不会对后退按钮做出反应。 - 当您打开 fragD 时,您应该 clear fragment BackStack。因此,当您从 D frag 按下后退按钮时,您会返回到 A。
P.S。还有其他方法可以做你想做的事。这取决于...
几天前,我开始学习 Android 中的片段。我也遇到了这个问题。在这里,我展示了我的解决方案以及我如何解决这个问题。如果我的代码不正确,请修复。此时我们有什么?活动,许多片段及其后台。我们想从抽屉菜单中打开每个片段,并从后台清除所有其他片段。但是,我们必须只持有一个 Home 片段。当我们停留在 Home fragment 上并且用户按下后退按钮时,应用程序将关闭。
Activity.class
protected void onCreate(Bundle savedInstanceState)
{
...
// adding Home fragment without adding transaction into backstack
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.container, HomeFragment.newInstance("args"), null);
ft.commit();
}
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish();
}
}
public void addFragmentFromMenu(Fragment fragment){
String backStateName = fragment.getClass().getName();
clearBackStack();
FragmentManager manager = getSupportFragmentManager();
if(manager.getBackStackEntryCount()> 0)
{
boolean fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped && manager.findFragmentByTag(backStateName) == null) {
//fragment not in back stack, create it.
addFragment(fragment, manager, backStateName);
}
}
else // no fragments
{
addFragment(fragment, manager, backStateName);
}
}
public void addFragment(Fragment fragment, FragmentManager manager, String backStateName)
{
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.container, fragment, backStateName);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();
}
public void clearBackStack() {
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
然后点击抽屉菜单项
@Override
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.nav_camera) {
addFragmentFromMenu(CameraFragment.newInstance("cam1", "cam2"));
} else if (id == R.id.nav_gallery) {
addFragmentFromMenu(TestFragment.newInstance("test1","test2"));
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
我做了以下方法。清除所有片段然后添加第一个主页片段
FragmentManager fm = getActivity().getSupportFragmentManager();
fm.popBackStack(Constants.TAG_HOME, FragmentManager.POP_BACK_STACK_INCLUSIVE);
((MainActivity) activity).manageFragment(new HomeFragment(), Constants.TAG_HOME);
// write down below function in main activity
public void manageFragment(Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (!fragmentManager.popBackStackImmediate(tag, 0)) {
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment, tag);
ft.addToBackStack(tag);
ft.commit();
}
}
1) 使用以下代码添加第一个片段
android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
if (fm.findFragmentById(R.id.fragment_container) != null) {
ft.hide(fm.findFragmentById(R.id.fragment_container));
}
ft.add(R.id.fragment_container, new OneFragment(),OneFragment.class.getCanonicalName())
.addToBackStack(OneFragment.class.getCanonicalName()).commit();
2) 使用以下代码从第一个片段添加第二个片段
android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
if (fm.findFragmentById(R.id.fragment_container) != null) {
ft.hide(fm.findFragmentById(R.id.fragment_container));
}
ft.add(R.id.fragment_container,new TwoFragment(),TwoFragment.class.getCanonicalName())
.addToBackStack(TwoFragment.class.getCanonicalName()).commit();
3) 使用以下代码从第二个片段添加第三个片段
android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
if (fm.findFragmentById(R.id.fragment_container) != null) {
ft.hide(fm.findFragmentById(R.id.fragment_container));
}
ft.add(R.id.fragment_container, new ThreeFragment(),ThreeFragment.class.getCanonicalName())
.addToBackStack(ThreeFragment.class.getCanonicalName()).commit();
4) 使用以下代码从第三个片段添加第四个片段
android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
if (fm.findFragmentById(R.id.fragment_container) != null) {
ft.hide(fm.findFragmentById(R.id.fragment_container));
}
ft.add(R.id.fragment_container, new FourFragment(),ThreeFragment.class.getCanonicalName())
.addToBackStack(FourFragment.class.getCanonicalName()).commit();
5) onBackPressed() 请写下代码
@Override
public void onBackPressed() {
hideKeyboard(MainActivity.this);
Fragment currentFragment = this.getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (currentFragment.getClass().getName().equalsIgnoreCase(FourFragment.class.getName())) { // Using this code come from third fragment to first fragment
Fragment f = this.getSupportFragmentManager().findFragmentByTag(TwoFragment.class.getCanonicalName());
if (f != null) {
this.getSupportFragmentManager().popBackStackImmediate(f.getClass().getCanonicalName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}else {
super.onBackPressed();
}
}
看了很多帖子,我是这样想的:
在 fragC => fragD 方法中,做两个事务:
1 清除返回堆栈,fragC => fragA
2 fragA => fragD
但是这样一来,fragA原来的状态可能会被破坏
public static void changeFragCtoD(FragmentManager fm, Fragment fragD){
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction
.replace(R.id.containerViewId, new fragAClass())
.commit();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction
.replace(R.id.containerViewId, fragD)
.addToBackStack(fragD.getClass().getName())
.commit();
}
现在按回 fragD 会回到 fragA。
记住我的话,完整的 FragmentTransaction
被添加到后台堆栈,而不仅仅是片段,这意味着即使您在单个事务中添加和删除片段,调用 poBackStack()
也会反转完整的事务。在其参数中传递标签会弹出所有交易,直到标记的交易甚至弹出标记的交易(如果 FragmentManager.POP_BACK_STACK_INCLUSIVE
添加到参数)
所以更多的是关于你如何添加它而不是你如何删除它。
参见 Performing Fragment Transactions
我知道现在回答您的问题为时已晚,但这可能对其他人有所帮助。 由于这几天一直在寻找解决方案...我已经使用for循环解决了这个问题,我认为这是最简单的方法。
首先,这样初始化一个整数
int count = getSupportFragmentManager().getBackStackEntryCount();
其中 getBackStackEntryCount() 将计算事务数。
然后在调用 fragD 中的 FragmentTransaction 方法之前添加这个 for 循环
for (int i = 0; i < count; i++){
getSupportFragmentManager().popBackStack();
}
for 循环将为您完成任务,popBackStack() 将 return 您转到 fragA
看起来像这样
int count = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < count; i++){
getSupportFragmentManager().popBackStack();
}
fragmentTransaction.replace(R.id.frame_layout, new FoodFragment(), "Food").addToBackStack(null);
fragmentTransaction.commit();
您可以覆盖 onbackpressed(Mainactivity)并使用片段管理器的 getBackStackEntryCount(),您可以检查它是否不等于 1 并仅在该条件下弹出堆栈。
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
if (!(getSupportFragmentManager().getBackStackEntryCount() == 1)) {
getSupportFragmentManager().popBackStack() ;
}
}
}