我正在学习如何使用片段。我有三个在类顶部初始化的Fragment实例。我将片段添加到类似如下的活动中:
声明和初始化:
Fragment A = new AFragment();
Fragment B = new BFragment();
Fragment C = new CFragment();替换/添加:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, A);
ft.addToBackStack(null);
ft.commit();这些代码片段工作正常。每个片段都被附加到活动上,并被保存到后台堆栈中,没有任何问题。
因此,当我启动A、C,然后启动B时,堆栈如下所示:
| |
|B|
|C|
|A|
___当我按下“back”按钮时,B被销毁,C被恢复。
但是,当我第二次启动fragment A时,它并没有从后台堆栈恢复,而是被添加到了后台堆栈的顶部
| |
|A|
|C|
|A|
___但是我想恢复A并销毁它上面的所有片段(如果有的话)。实际上,我只是喜欢默认的后台堆栈行为。
我该如何做到这一点?
预期:(应恢复A并销毁顶部片段)
| |
| |
| |
|A|
___编辑:(由A--C建议)
这是我尝试过的代码:
private void selectItem(int position) {
Fragment problemSearch = null, problemStatistics = null;
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
String backStateName = null;
Fragment fragmentName = null;
boolean fragmentPopped = false;
switch (position) {
case 0:
fragmentName = profile;
break;
case 1:
fragmentName = submissionStatistics;
break;
case 2:
fragmentName = solvedProblemLevel;
break;
case 3:
fragmentName = latestSubmissions;
break;
case 4:
fragmentName = CPExercise;
break;
case 5:
Bundle bundle = new Bundle();
bundle.putInt("problem_no", problemNo);
problemSearch = new ProblemWebView();
problemSearch.setArguments(bundle);
fragmentName = problemSearch;
break;
case 6:
fragmentName = rankList;
break;
case 7:
fragmentName = liveSubmissions;
break;
case 8:
Bundle bundles = new Bundle();
bundles.putInt("problem_no", problemNo);
problemStatistics = new ProblemStatistics();
problemStatistics.setArguments(bundles);
fragmentName = problemStatistics;
default:
break;
}
backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();
// I am using drawer layout
mDrawerList.setItemChecked(position, true);
setTitle(title[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}问题是,当我启动A,然后启动B,然后按'back',B被删除,A被恢复。第二次按“后退”应该会退出应用程序。但是它显示了一个空白窗口,我必须按第三次才能将其关闭。
另外,当我启动A,然后是B,然后是C,然后是B ...
期望值:
| |
| |
|B|
|A|
___实际:
| |
|B|
|B|
|A|
___我应该用任何定制覆盖onBackPressed()吗?还是我遗漏了什么?
发布于 2013-08-19 12:08:07
读取documentation时,有一种方法可以基于提交提供的事务名称或id弹出后堆栈。使用名称可能更容易,因为它不需要跟踪可能发生变化的数字,并强化了“唯一的后端堆栈条目”逻辑。
由于每个Fragment只需要一个back stack条目,因此(通过getClass().getName())将back状态名称设为片段的类名。然后,在替换Fragment时,使用popBackStackImmediate()方法。如果返回true,则表示在后端堆栈中存在该片段的实例。如果不是,则实际执行片段替换逻辑。
private void replaceFragment (Fragment fragment){
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
if (!fragmentPopped){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack(backStateName);
ft.commit();
}
}编辑
问题是-当我依次启动A和B,然后按back按钮时,B被删除,A被恢复。再次按下后退按钮应该会退出应用程序。但它显示了一个空白窗口,需要再按一次才能关闭它。
这是因为FragmentTransaction正被添加到后台堆栈,以确保我们以后可以将片段弹出到顶部。解决这个问题的一个快速方法是覆盖onBackPressed()并在后栈只包含1个Fragment的情况下完成活动
@Override
public void onBackPressed(){
if (getSupportFragmentManager().getBackStackEntryCount() == 1){
finish();
}
else {
super.onBackPressed();
}
}关于重复的后台堆栈条目,如果它还没有被弹出,那么替换它的条件语句与我的原始代码片段明显不同。您所做的是添加到后台堆栈,而不管该后台堆栈是否被弹出。
像这样的东西应该更接近你想要的:
private void replaceFragment (Fragment fragment){
String backStateName = fragment.getClass().getName();
String fragmentTag = backStateName;
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment, fragmentTag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();
}
}条件发生了一些变化,因为选择相同的片段,而它是可见的,也会导致重复的条目。
实施:
我强烈建议不要像在代码中那样拆分更新后的replaceFragment()方法。所有的逻辑都包含在这个方法中,移动部件可能会导致问题。
这意味着您应该将更新后的replaceFragment()方法复制到类中,然后更改
backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();所以它很简单
replaceFragment (fragmentName);编辑#2
要在后台堆栈发生变化时更新抽屉,请创建一个方法,该方法接受片段中的内容并比较类名。如果有任何匹配,请更改标题和选择。另外,添加一个OnBackStackChangedListener,如果有有效的片段,就让它调用您的更新方法。
例如,在活动的onCreate()中,添加
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
if (f != null){
updateTitleAndDrawer (f);
}
}
});另一种方法是:
private void updateTitleAndDrawer (Fragment fragment){
String fragClassName = fragment.getClass().getName();
if (fragClassName.equals(A.class.getName())){
setTitle ("A");
//set selected item position, etc
}
else if (fragClassName.equals(B.class.getName())){
setTitle ("B");
//set selected item position, etc
}
else if (fragClassName.equals(C.class.getName())){
setTitle ("C");
//set selected item position, etc
}
}现在,每当后台堆栈发生变化时,标题和选中的位置都将反映可见的Fragment。
发布于 2017-01-01 17:29:16
我认为这个方法可以解决你的问题:
public static void attachFragment ( int fragmentHolderLayoutId, Fragment fragment, Context context, String tag ) {
FragmentManager manager = ( (AppCompatActivity) context ).getSupportFragmentManager ();
FragmentTransaction ft = manager.beginTransaction ();
if (manager.findFragmentByTag ( tag ) == null) { // No fragment in backStack with same tag..
ft.add ( fragmentHolderLayoutId, fragment, tag );
ft.addToBackStack ( tag );
ft.commit ();
}
else {
ft.show ( manager.findFragmentByTag ( tag ) ).commit ();
}
}它最初发布在This Question上
发布于 2014-10-01 19:53:54
第1步:使用activity类实现一个接口
public class AuthenticatedMainActivity extends Activity implements FragmentManager.OnBackStackChangedListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
.............
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().add(R.id.frame_container,fragment, "First").addToBackStack(null).commit();
}
private void switchFragment(Fragment fragment){
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment).addToBackStack("Tag").commit();
}
@Override
public void onBackStackChanged() {
FragmentManager fragmentManager = getFragmentManager();
System.out.println("@Class: SummaryUser : onBackStackChanged "
+ fragmentManager.getBackStackEntryCount());
int count = fragmentManager.getBackStackEntryCount();
// when a fragment come from another the status will be zero
if(count == 0){
System.out.println("again loading user data");
// reload the page if user saved the profile data
if(!objPublicDelegate.checkNetworkStatus()){
objPublicDelegate.showAlertDialog("Warning"
, "Please check your internet connection");
}else {
objLoadingDialog.show("Refreshing data...");
mNetworkMaster.runUserSummaryAsync();
}
// IMPORTANT: remove the current fragment from stack to avoid new instance
fragmentManager.removeOnBackStackChangedListener(this);
}// end if
}
}第2步:当您调用另一个片段时,添加此方法:
String backStateName = this.getClass().getName();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.addOnBackStackChangedListener(this);
Fragment fragmentGraph = new GraphFragment();
Bundle bundle = new Bundle();
bundle.putString("graphTag", view.getTag().toString());
fragmentGraph.setArguments(bundle);
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragmentGraph)
.addToBackStack(backStateName)
.commit();https://stackoverflow.com/questions/18305945
复制相似问题