简体   繁体   English

片段 onResume() 和 onPause() 未在后台调用

[英]Fragment onResume() & onPause() is not called on backstack

I have multiple fragment inside an activity.我在一个活动中有多个片段。 On a button click I am starting a new fragment, adding it to backstack.单击按钮,我将开始一个新片段,并将其添加到后台堆栈。 I naturally expected the onPause() method of current Fragment and onResume() of new Fragment to be called.我自然希望调用当前 Fragment 的onPause()方法和新 Fragment 的onResume()方法。 Well it is not happening.好吧,这没有发生。

LoginFragment.java登录片段.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 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();
  }
}

What I expected, was,我所期望的是,

  1. When button is clicked, LoginFragment gets replaced with HomeFragment , onPause() of LoginFragment , and onResume() of HomeFragment gets called单击按钮时, LoginFragment被替换为HomeFragmentLoginFragmentonPause()HomeFragmentonResume()被调用
  2. When back is pressed, HomeFragment is poped out and LoginFragment is seen, and onPause() of HomeFragment and onResume() of LoginFragment gets called.当按下返回键时,弹出 HomeFragment并看到LoginFragment ,调用HomeFragmentonPause()LoginFragmentonResume()

What I am getting is,我得到的是,

  1. When button is clicked, HomeFragment is correctly replacing LoginFragment , onResume() of HomeFragment is called, but onPause() of LoginFragment is never called.单击按钮时, HomeFragment正确替换LoginFragment ,调用HomeFragment的 onResume() ,但永远不会调用LoginFragment的 onPause() 。
  2. When back pressed, HomeFragment is correctly popping to reveal LoginFragment , onPause() of HomeFragment gets called, but onResume() of LoginFragment never gets called.按下后退时, HomeFragment正确弹出以显示LoginFragment , HomeFragment 的 onPause() 被调用,但LoginFragment的 onResume()永远不会被调用。

Is this the normal behaviour?这是正常行为吗? Why is onResume() of LoginFragment not getting called when I press the back button.为什么当我按下后退按钮时, LoginFragmentonResume()没有被调用。

The fragments onResume() or onPause() will be called only when the Activities onResume() or onPause() is called.仅当调用活动onResume()onPause()时,才会调用片段onResume()onPause() They are tightly coupled to the Activity .它们与Activity紧密耦合。

Read the Handling the Fragment Lifecycle section of this article .阅读本文的处理片段生命周期部分

  • Since you have used ft2.replace() , FragmentTransaction.remove() method is called and the Loginfragment will be removed.由于您使用ft2.replace() ,因此将调用FragmentTransaction.remove()方法并删除Loginfragment Refer to this .参考这个 So onStop() of LoginFragment will be called instead of onPause() .因此将调用LoginFragmentonStop()而不是onPause() (As the new fragment completely replaces the old one). (因为新片段完全取代了旧片段)。
  • But since you have also used ft2.addtobackstack() , the state of the Loginfragment will be saved as a bundle and when you click back button from HomeFragment , onViewStateRestored() will be called followed by onStart() of LoginFragment .但是由于您还使用ft2.addtobackstack()Loginfragment的状态将保存为一个包,当您从HomeFragment单击后退按钮时, onViewStateRestored()将被调用,然后是LoginFragmentonStart() So eventually onResume() won't be called.所以最终不会调用onResume()

Here's my more robust version of Gor's answer (using fragments.size()is unreliable due to size not being decremented after fragment is popped)这是我更强大的 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. 
                    }
                }
            }
        }
    });

And the 'getCurrentTopFragment' method:和“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;
}

If you really want to replace fragment inside other fragment you should use Nested Fragments .如果你真的想替换其他片段中的片段,你应该使用Nested Fragments

In your code you should replace在您的代码中,您应该替换

final FragmentManager mFragmentmanager =  getFragmentManager();

with

final FragmentManager mFragmentmanager =  getChildFragmentManager();
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
            }

        }
    });

but be careful if more than one fragment visible但如果可见不止一个碎片,请小心

I have a code very similar to yours and if it works onPause () and onResume ().我有一个与您的代码非常相似的代码,如果它可以在 onPause () 和 onResume () 上运行。 When changing the fragment, these functions are activated respectively.更改片段时,将分别激活这些功能。

Code in fragment:片段中的代码:

 @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");

}

Log when change of fragment:片段更改时记录:

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

Fragment onCreateView:片段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;
}

Action when I pulse back (in the activity where I load the fragment):当我脉冲回来时的动作(在我加载片段的活动中):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

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

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

 }

I use in my activity - KOTLIN我在我的活动中使用 - KOTLIN

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

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

What i do in child fragment:我在子片段中做什么:

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

And then override onResume on ParentFragment然后覆盖 ParentFragment 上的 onResume

If you add the fragment in XML, you can't swap them dynamically.如果您在 XML 中添加片段,则无法动态交换它们。 What happens is they overly, so they events don't fire as one would expect.发生的事情是他们过度,所以他们的事件不会像人们预期的那样触发。 The issue is documented in this question.这个问题记录在这个问题中。 FragmenManager replace makes overlay FragmenManager 替换使覆盖

Turn middle_fragment into a FrameLayout, and load it like below and your events will fire.将 middle_fragment 变成一个 FrameLayout,然后像下面那样加载它,您的事件就会触发。

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

You can try this,你可以试试这个,

Step1: Override the Tabselected method in your activity第 1 步:重写 Activity 中的 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());
}

Step 2: Using static method do what you want in your fragment,第 2 步:使用静态方法在片段中执行您想要的操作,

public static void fragmentChanged()
{
    Toast.makeText(actvity, "Fragment Changed", Toast.LENGTH_SHORT).show();
}

onPause() method works in activity class you can use: onPause()方法适用于您可以使用的活动类:

public void onDestroyView(){
super.onDestroyView    
}

for same purpose..为了同样的目的..

Based on the answer of @Gor I wrote similar in Kotlin.根据@Gor的回答,我在 Kotlin 中写了类似的。 Place this code in onCreate() of an activity.将此代码放在活动的onCreate()中。 It works for one fragment visible.它适用于一个可见的片段。 If you have ViewPager with fragments, it will call ViewPager 's fragment, not a previous one.如果您有带有片段的ViewPager ,它将调用ViewPager的片段,而不是之前的片段。

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

After reading https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6 I understood that it would be better in many situations to attach new fragments with replace , not add .阅读https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6后,我明白在很多情况下用replace附加新片段会更好,而不是add . So a need in onResume in some cases will disappear.因此在某些情况下onResume中的需求将消失。

for me, the cause was using navGraph to switch between fragments对我来说,原因是使用 navGraph 在片段之间切换

app:navGraph

create the fragment like this (in Kotlin) and then all the lifecycle functions get called像这样创建片段(在 Kotlin 中),然后调用所有生命周期函数

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

You simple can't add a fragment to a fragment.不能简单地将片段添加到片段。 This needs to happen in the FragmentActivity.这需要在 FragmentActivity 中发生。 I assume you are creating the LoginFragment in a FragmentActivity, so in order to get this working you need to add the HomeFragment via the FragmentActivity when the login closes.我假设您正在 FragmentActivity 中创建 LoginFragment,因此为了使其正常工作,您需要在登录关闭时通过 FragmentActivity 添加 HomeFragment。

The general point is that you need a FragmentActivity class from where you add each Fragment to the FragmentManager.总的来说,您需要一个 FragmentActivity 类,您可以从中将每个 Fragment 添加到 FragmentManager。 It is not possible to do this inside a Fragment class.在 Fragment 类中不可能这样做。

A fragment must always be embedded in an activity and the fragment's lifecycle is directly affected by the host activity's lifecycle.片段必须始终嵌入到活动中,片段的生命周期直接受宿主活动生命周期的影响。 For example, when the activity is paused, so are all fragments in it, and when the activity is destroyed, so are all fragments比如当activity暂停的时候,里面所有的fragment也都暂停了,当activity销毁的时候,所有的fragment也都暂停了

Follow the below steps, and you shall get the needed answer按照以下步骤操作,您将获得所需的答案

1- For both fragments, create a new abstract parent one. 1- 对于这两个片段,创建一个新的抽象父片段。
2- Add a custom abstract method that should be implemented by both of them. 2- 添加一个自定义的抽象方法,它们都应该实现。
3- Call it from the current instance before replacing with the second one. 3- 在替换为第二个实例之前从当前实例调用它。

Calling ft.replace should trigger the onPause (of the replaced fragment) and onResume (of the replacing fragment).调用ft.replace应该触发 onPause(被替换的片段)和 onResume(被替换的片段)。

I do notice that your code inflates login_fragment on the home fragment, and also doesn't return the views in the onCreateView.我确实注意到您的代码在主页片段上膨胀login_fragment ,并且也没有返回 onCreateView 中的视图。 If these are typos, can you show how these fragments are being called from within your activity?如果这些是拼写错误,您能否展示如何从您的活动中调用这些片段?

I have multiple fragment inside an activity.我在一个活动中有多个片段。 On a button click I am starting a new fragment, adding it to backstack.单击按钮后,我将开始一个新片段,并将其添加到 backstack。 I naturally expected the onPause() method of current Fragment and onResume() of new Fragment to be called.我自然期待当前 Fragment 的onPause()方法和 new Fragment 的onResume()方法被调用。 Well it is not happening.嗯,它没有发生。

LoginFragment.java登录片段.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 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();
  }
}

What I expected, was,我所期望的是,

  1. When button is clicked, LoginFragment gets replaced with HomeFragment , onPause() of LoginFragment , and onResume() of HomeFragment gets called当用户点击按钮,LoginFragment获取与HomeFragment,取而代之onPause() LoginFragment的,和onResume() HomeFragment的被调用
  2. When back is pressed, HomeFragment is poped out and LoginFragment is seen, and onPause() of HomeFragment and onResume() of LoginFragment gets called.当被压回,HomeFragment是POP操作并LoginFragment可见,并onPause() HomeFragmentonResume() LoginFragment的被调用。

What I am getting is,我得到的是,

  1. When button is clicked, HomeFragment is correctly replacing LoginFragment , onResume() of HomeFragment is called, but onPause() of LoginFragment is never called.当用户点击按钮,HomeFragment正确更换LoginFragment,的onResume()的HomeFragment被调用,但在onPause(LoginFragment的)永远不会被调用。
  2. When back pressed, HomeFragment is correctly popping to reveal LoginFragment , onPause() of HomeFragment gets called, but onResume() of LoginFragment never gets called.当按下回来,HomeFragment正确弹出透露LoginFragment,在onPause()的HomeFragment被调用,但的onResume(LoginFragment的)不会被调用。

Is this the normal behaviour?这是正常行为吗? Why is onResume() of LoginFragment not getting called when I press the back button.当我按下后退按钮时,为什么没有调用LoginFragment 的onResume()

There are two possible override methods guaranteed to be called when a fragment appears/disappears.当片段出现/消失时,有两种可能的覆盖方法保证被调用。

oncreateview/ondestroyview - this is called whenever the fragment visibility changes oncreateview/ondestroyview - 每当片段可见性改变时调用

onpause/onresume - called when the app is closed by external or you use fragment replace onpause/onresume - 当应用程序被外部关闭或您使用片段替换时调用

If you wish to create a hook as I did so you can by notified whenever a fragment is closed and catch either situation without creating a duplicate call as sometimes both could be called.如果您希望像我一样创建一个挂钩,那么您可以在片段关闭时收到通知并捕获任何一种情况而无需创建重复调用,因为有时两者都可以被调用。

try extending the fragment class and add a boolean flag in both overrides so you catch the open and close for sure without duplicating尝试扩展片段类并在两个覆盖中添加一个布尔标志,这样您就可以在不重复的情况下确定打开和关闭

kotlin科特林

requireFragmentManager().addOnBackStackChangedListener {
            val fragments: List<Fragment> = requireFragmentManager().fragments
            if (fragments.isNotEmpty() && fragments[fragments.size - 1] is YOUR_FRAGMENT) {
                //todo if fragment visible
            } else {
                //todo if fragment invisible
            }
        }

While creating a fragment transaction, make sure to add the following code.创建片段事务时,请确保添加以下代码。

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

Also make sure, to commit the transaction after adding it to backstack还要确保在将事务添加到 backstack 之后提交事务

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM