219

我在一个活动中有多个片段。在一个按钮上单击我开始一个新的片段,将它添加到后台堆栈。我自然希望调用onPause()current Fragment 和onResume()new Fragment 的方法。那么它没有发生。

登录片段.java

public class LoginFragment extends Fragment{
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
      final FragmentManager mFragmentmanager =  getFragmentManager();

      Button btnHome  = (Button)view.findViewById(R.id.home_btn);
      btnHome.setOnClickListener(new View.OnClickListener() {
        public void onClick(View view){
           HomeFragment fragment    = new HomeFragment();
           FragmentTransaction ft2   =  mFragmentmanager.beginTransaction();
           ft2.setCustomAnimations(R.anim.slide_right, R.anim.slide_out_left
                    , R.anim.slide_left, R.anim.slide_out_right);
           ft2.replace(R.id.middle_fragment, fragment);
           ft2.addToBackStack(""); 
           ft2.commit();    
         }
      });
  }

  @Override
  public void onResume() {
     Log.e("DEBUG", "onResume of LoginFragment");
     super.onResume();
  }

  @Override
  public void onPause() {
    Log.e("DEBUG", "OnPause of loginFragment");
    super.onPause();
  }
}

HomeFragment.java

public class HomeFragment extends Fragment{
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
  }

  @Override
  public void onResume() {
     Log.e("DEBUG", "onResume of HomeFragment");
     super.onResume();
  }

  @Override
  public void onPause() {
     Log.e("DEBUG", "OnPause of HomeFragment");
     super.onPause();
  }
}

我所期待的是,

  1. 单击按钮时,LoginFragmentHomeFragment替换 ,LoginFragmentonPause()和HomeFragment 调用onResume()
  2. 当按下返回时,会弹出 HomeFragment并看到LoginFragmentonPause(),并调用HomeFragmentLoginFragmentonResume()

我得到的是,

  1. 单击按钮时,HomeFragment正确替换 LoginFragment,调用 HomeFragment 的 onResume() ,但从不调用 LoginFragmentonPause() 。
  2. 按下后,HomeFragment正确弹出以显示 LoginFragment, HomeFragment 的 onPause()被调用,但 LoginFragment 的 onResume()永远不会被调用。

这是正常的行为吗?为什么当我按下后退按钮时没有调用onResume()LoginFragment

4

20 回答 20

199

只有在调用活动或时才会调用片段onResume()或。它们与.onPause()onResume()onPause()Activity

阅读本文的处理片段生命周期部分

于 2013-04-27T13:55:01.720 回答
23
  • 由于您已使用ft2.replace(),FragmentTransaction.remove() 方法被调用并且Loginfragment将被删除。参考这个。所以onStop()ofLoginFragment将被调用而不是onPause(). (因为新片段完全取代了旧片段)。
  • 但是由于你也使用过ft2.addtobackstack(),所以状态Loginfragment会被保存为一个bundle,当你点击返回按钮from时HomeFragmentonViewStateRestored()会调用of后面onStart()LoginFragment。所以最终onResume()不会被调用。
于 2013-04-27T13:45:07.710 回答
17

这是我更强大的 Gor 答案版本(使用 Fragments.size() 是不可靠的,因为在弹出片段后大小没有减少)

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager() != null) {

                Fragment topFrag = NavigationHelper.getCurrentTopFragment(getFragmentManager());

                if (topFrag != null) {
                    if (topFrag instanceof YourFragment) {
                        //This fragment is being shown. 
                    } else {
                        //Navigating away from this fragment. 
                    }
                }
            }
        }
    });

和“getCurrentTopFragment”方法:

public static Fragment getCurrentTopFragment(FragmentManager fm) {
    int stackCount = fm.getBackStackEntryCount();

    if (stackCount > 0) {
        FragmentManager.BackStackEntry backEntry = fm.getBackStackEntryAt(stackCount-1);
        return  fm.findFragmentByTag(backEntry.getName());
    } else {
        List<Fragment> fragments = fm.getFragments();
        if (fragments != null && fragments.size()>0) {
            for (Fragment f: fragments) {
                if (f != null && !f.isHidden()) {
                    return f;
                }
            }
        }
    }
    return null;
}
于 2016-11-24T17:41:30.230 回答
13

如果你真的想在其他片段中替换片段,你应该使用嵌套片段

在您的代码中,您应该替换

final FragmentManager mFragmentmanager =  getFragmentManager();

final FragmentManager mFragmentmanager =  getChildFragmentManager();
于 2013-04-24T15:55:58.317 回答
8
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            List<Fragment> fragments = getFragmentManager().getFragments();
            if (fragments.size() > 0 && fragments.get(fragments.size() - 1) instanceof YoureFragment){
                //todo if fragment visible
            } else {
                //todo if fragment invisible
            }

        }
    });

但要小心,如果多个片段可见

于 2016-10-13T13:47:59.540 回答
4

我有一个与您的代码非常相似的代码,如果它在 onPause () 和 onResume () 上工作。当改变片段时,这些功能分别被激活。

片段中的代码:

 @Override
public void onResume() {
    super.onResume();
    sensorManager.registerListener(this, proximidad, SensorManager.SENSOR_DELAY_NORMAL);
    sensorManager.registerListener(this, brillo, SensorManager.SENSOR_DELAY_NORMAL);
    Log.e("Frontales","resume");
}

@Override
public void onPause() {
    super.onPause();
    sensorManager.unregisterListener(this);
    Log.e("Frontales","Pause");

}

片段更改时的日志:

05-19 22:28:54.284 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:28:57.002 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:28:58.697 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:00.840 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:29:02.248 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:03.718 2371-2371/madi.cajaherramientas E/Frontales: Pause

片段 onCreateView:

View rootView;
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    rootView = inflater.inflate(R.layout.activity_proximidad, container, false);
    ButterKnife.bind(this,rootView);
    inflar();
    setTextos();
    return rootView;
}

当我脉冲回来时的动作(在我加载片段的活动中):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();

    } else {
        getFragmentManager().popBackStack();
    }

 }
于 2017-05-19T20:45:14.860 回答
4

我在我的活动中使用 - KOTLIN

supportFragmentManager.addOnBackStackChangedListener {
                val f = supportFragmentManager.findFragmentById(R.id.fragment_container)

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }
于 2019-05-10T22:20:06.957 回答
2

我在子片段中做什么:

@Override
public void onDetach() {
   super.onDetach();
   ParentFragment pf = (ParentFragment) this.getParentFragment();
   pf.onResume();
}

然后在 ParentFragment 上覆盖 onResume

于 2017-12-10T09:01:58.560 回答
2

尽管使用不同的代码,但我遇到了与 OP 相同的问题,因为我最初使用的是

fm.beginTransaction()
            .add(R.id.fragment_container_main, fragment)
            .addToBackStack(null)
            .commit();

代替

fm.beginTransaction()
                .replace(R.id.fragment_container_main, fragment)
                .addToBackStack(null)
                .commit();

使用“替换”,当您从第二个片段返回时会重新创建第一个片段,因此也会调用 onResume()。

于 2018-11-26T17:24:20.803 回答
1

如果在 XML 中添加片段,则不能动态交换它们。发生的事情是它们过度,因此它们的事件不会像人们期望的那样触发。这个问题记录在这个问题中。FragmenManager 替换使覆盖

将 middle_fragment 转换为 FrameLayout,然后像下面这样加载它,您的事件就会触发。

getFragmentManager().beginTransation().
    add(R.id.middle_fragment, new MiddleFragment()).commit();
于 2015-08-24T07:22:15.413 回答
1

你可以试试这个

第 1 步:覆盖活动中的 Tabselected 方法

@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    // When the given tab is selected, switch to the corresponding page in
    // the ViewPager.
    try {
    if(MyEventsFragment!=null && tab.getPosition()==3)
    {
        MyEvents.fragmentChanged();
    }
    }
    catch (Exception e)
    {

    }
    mViewPager.setCurrentItem(tab.getPosition());
}

第 2 步:使用静态方法在片段中执行您想要的操作,

public static void fragmentChanged()
{
    Toast.makeText(actvity, "Fragment Changed", Toast.LENGTH_SHORT).show();
}
于 2016-01-20T12:25:31.900 回答
1

onPause()方法适用于您可以使用的活动类:

public void onDestroyView(){
super.onDestroyView    
}

出于同样的目的。。

于 2017-04-02T13:44:28.733 回答
1

根据@Gor的回答,我在 Kotlin 中写了类似的内容。将此代码放入onCreate()活动中。它适用于一个可见的片段。如果你有ViewPager片段,它会调用ViewPager'片段,而不是以前的片段。

supportFragmentManager.addOnBackStackChangedListener {
    supportFragmentManager.fragments.lastOrNull()?.onResume()
}

阅读https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6后,我了解到在许多情况下将新片段附加到replace,而不是add. 所以onResume在某些情况下需要会消失。

于 2018-07-20T12:43:17.963 回答
1

对我来说,原因是使用 navGraph 在片段之间切换

app:navGraph

像这样(在 Kotlin 中)创建片段,然后调用所有生命周期函数

val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.nav_host_fragment, fragment)
transaction.addToBackStack(null)
transaction.commit()
于 2021-06-01T14:39:37.320 回答
0

您不能简单地将片段添加到片段中。这需要在 FragmentActivity 中发生。我假设您正在 FragmentActivity 中创建 LoginFragment,因此为了使其正常工作,您需要在登录关闭时通过 FragmentActivity 添加 HomeFragment。

一般的一点是,您需要一个 FragmentActivity 类,从中将每个 Fragment 添加到 FragmentManager。在 Fragment 类中不可能做到这一点。

于 2012-11-07T20:53:05.697 回答
0

片段必须始终嵌入到活动中,并且片段的生命周期直接受宿主活动生命周期的影响。比如activity暂停的时候,里面的所有fragment也暂停,activity销毁的时候,所有的fragment也暂停

于 2014-07-16T11:23:16.260 回答
0

按照以下步骤,您将得到所需的答案

1- 对于两个片段,创建一个新的抽象父片段。
2-添加一个应该由他们俩实现的自定义抽象方法。
3-在用第二个替换之前从当前实例调用它。

于 2018-04-09T21:09:33.310 回答
0

调用ft.replace应该触发(被替换片段的)onPause 和(被替换片段的)onResume。

我确实注意到您的代码login_fragment在主片段上膨胀,并且也没有返回 onCreateView 中的视图。如果这些是拼写错误,你能说明这些片段是如何从你的活动中被调用的吗?

于 2018-11-20T09:44:01.570 回答
0

当片段出现/消失时,保证调用两种可能的覆盖方法。

oncreateview/ondestroyview - 每当片段可见性发生变化时都会调用它

onpause/onresume - 当应用程序被外部关闭或您使用片段替换时调用

如果您希望像我一样创建一个钩子,您可以在片段关闭时收到通知,并在不创建重复调用的情况下捕获任何一种情况,因为有时两者都可以调用。

尝试扩展片段类并在两个覆盖中添加一个布尔标志,这样你就可以确定打开和关闭而不重复

于 2021-10-26T02:10:20.857 回答
-3

创建片段事务时,请确保添加以下代码。

// Replace whatever is in the fragment_container view with this fragment, 
// and add the transaction to the back stack 
transaction.replace(R.id.fragment_container, newFragment); 
transaction.addToBackStack(null); 

还要确保在将事务添加到后台堆栈后提交事务

于 2013-03-07T13:25:17.450 回答