片段onResume()&在backstack上不调用onPause()

时间:2012-07-04 09:23:32

标签: android android-fragments onresume back-stack

我在一个活动中有多个片段。在按钮上单击我开始一个新片段,将其添加到backstack。我自然希望调用当前Fragment的onPause()方法和新Fragment的onResume()。好吧,它没有发生。

LoginFragment.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. 单击按钮时, LoginFragment 将替换为 HomeFragment LoginFragment onPause()onResume() HomeFragment 被调用
  2. 按下后退时, HomeFragment 将被激活, LoginFragment 为 看到了 HomeFragment onPause() LoginFragment onResume() 被召唤。
  3. 我得到的是,

    1. 单击按钮时, HomeFragment 正在替换 LoginFragment ,调用 HomeFragment 的onResume(),但onPause() 永远不会调用 LoginFragment
    2. 按下后, HomeFragment 会正确弹出以显示 LoginFragment HomeFragment 的onPause()被调用,但onResume() LoginFragment 永远不会被调用。
    3. 这是正常行为吗?当我按下后退按钮时,为什么没有调用 LoginFragment onResume()

19 个答案:

答案 0 :(得分:159)

只有在调用活动onResume()onPause()时才会调用片段onResume()onPause()。 它们与Activity紧密耦合。

阅读Handling the Fragment Lifecycle section of this article

答案 1 :(得分:18)

  • 由于您使用了ft2.replace()FragmentTransaction.remove() 方法被调用,Loginfragment将被删除。 请参阅this。 因此onStop() LoginFragment将被调用而不是onPause()。 (作为新的 片段完全取代旧片段。)
  • 但是既然你也有 使用ft2.addtobackstack()Loginfragment的状态将是 保存为捆绑包,当您单击HomeFragment的后退按钮时, onViewStateRestored()将跟随onStart() LoginFragment。因此最终不会调用onResume()

答案 2 :(得分:10)

这里是我更强大的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;
}

答案 3 :(得分:8)

如果你真的想要替换另一个片段中的片段,你应该使用Nested Fragments

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

final FragmentManager mFragmentmanager =  getFragmentManager();

final FragmentManager mFragmentmanager =  getChildFragmentManager();

答案 4 :(得分:3)

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
            }

        }
    });

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

答案 5 :(得分:2)

我有一个非常类似于你的代码,如果它适用于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();
    }

 }

答案 6 :(得分:1)

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

一般来说,您需要一个FragmentActivity类,从而将每个Fragment添加到FragmentManager。在Fragment类中执行此操作不可能

答案 7 :(得分:1)

如果以XML格式添加片段,则无法动态交换它们。他们过度地发生了什么,所以他们的事件并没有像人们期望的那样发射。这个问题记录了这个问题。 FragmenManager replace makes overlay

将middle_fragment转换为FrameLayout,然后像下面一样加载它,你的事件将被触发。

getFragmentManager().beginTransation().
    add(R.id.middle_fragment, new MiddleFragment()).commit();

答案 8 :(得分:1)

你可以试试这个,

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

Tableview

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

@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());
}

答案 9 :(得分:1)

我在儿童片段中做了什么:

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

然后在ParentFragment上覆盖onResume

答案 10 :(得分:0)

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

app:navGraph

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

val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.nav_host_fragment, fragment)
transaction.addToBackStack(null)
transaction.commit()

答案 11 :(得分:0)

我在活动中使用- KOTLIN

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

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }

答案 12 :(得分:0)

尽管使用不同的代码,但我遇到了与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();

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

答案 13 :(得分:0)

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

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

答案 14 :(得分:0)

基于@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中的需求在某些情况下会消失。

答案 15 :(得分:0)

按照以下步骤操作,您将获得所需的答案

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

答案 16 :(得分:0)

data new_ops04; set new_ops03; array MONTHS (24) M1-M24; AUXILIARY_COLUMN = 0; do i=1 to 24; IF MONTHS(i) ne 0 and AUXILIARY_COLUMN = 0 THEN AUXILIARY_COLUMN = MONTHS(i); end; drop i; run; 方法适用于您可以使用的活动类:

onPause()

出于同样的目的..

答案 17 :(得分:0)

片段必须始终嵌入到活动中,片段的生命周期直接受到主机活动生命周期的影响。例如,当活动暂停时,其中的所有片段也都会暂停,当活动被销毁时,所有片段也都会被删除

答案 18 :(得分:-2)

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

// 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); 

还要确保在将事务添加到backstack后提交事务