问题
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. Well it is not happening.
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();
}
}
What I expected, was,
- When button is clicked, LoginFragment gets replaced with
HomeFragment,
onPause()
of LoginFragment, andonResume()
of HomeFragment gets called - When back is pressed, HomeFragment is poped out and LoginFragment is
seen, and
onPause()
of HomeFragment andonResume()
of LoginFragment gets called.
What I am getting is,
- When button is clicked, HomeFragment is correctly replacing LoginFragment, onResume() of HomeFragment is called, but onPause() of LoginFragment is never called.
- When back pressed, HomeFragment is correctly popping to reveal LoginFragment, onPause() of HomeFragment gets called, but onResume() of LoginFragment never gets called.
Is this the normal behaviour? Why is onResume()
of LoginFragment not getting called when I press the back button.
回答1:
The fragments onResume()
or onPause()
will be called only when the Activities onResume()
or onPause()
is called.
They are tightly coupled to the Activity
.
Read the Handling the Fragment Lifecycle section of this article.
回答2:
- Since you have used
ft2.replace()
,FragmentTransaction.remove()
method is called and theLoginfragment
will be removed. Refer to this. SoonStop()
ofLoginFragment
will be called instead ofonPause()
. (As the new fragment completely replaces the old one). - But since you have also
used
ft2.addtobackstack()
, the state of theLoginfragment
will be saved as a bundle and when you click back button fromHomeFragment
,onViewStateRestored()
will be called followed byonStart()
ofLoginFragment
. So eventuallyonResume()
won't be called.
回答3:
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)
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:
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;
}
回答4:
If you really want to replace fragment inside other fragment you should use Nested Fragments.
In your code you should replace
final FragmentManager mFragmentmanager = getFragmentManager();
with
final FragmentManager mFragmentmanager = getChildFragmentManager();
回答5:
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
回答6:
I have a code very similar to yours and if it works onPause () and 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:
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();
}
}
回答7:
You simple can't add a fragment to a fragment. This needs to happen in the 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.
The general point is that you need a FragmentActivity class from where you add each Fragment to the FragmentManager. It is not possible to do this inside a Fragment class.
回答8:
If you add the fragment in XML, you can't swap them dynamically. 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
Turn middle_fragment into a FrameLayout, and load it like below and your events will fire.
getFragmentManager().beginTransation().
add(R.id.middle_fragment, new MiddleFragment()).commit();
回答9:
You can try this,
Step1: Override the Tabselected method in your activity
@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,
public static void fragmentChanged()
{
Toast.makeText(actvity, "Fragment Changed", Toast.LENGTH_SHORT).show();
}
回答10:
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
回答11:
I use in my activity - KOTLIN
supportFragmentManager.addOnBackStackChangedListener {
val f = supportFragmentManager.findFragmentById(R.id.fragment_container)
if (f?.tag == "MyFragment")
{
//doSomething
}
}
回答12:
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
回答13:
onPause()
method works in activity class you can use:
public void onDestroyView(){
super.onDestroyView
}
for same purpose..
回答14:
Follow the below steps, and you shall get the needed answer
1- For both fragments, create a new abstract parent one.
2- Add a custom abstract method that should be implemented by both of them.
3- Call it from the current instance before replacing with the second one.
回答15:
Based on the answer of @Gor I wrote similar in Kotlin. Place this code in onCreate()
of an activity. It works for one fragment visible. If you have ViewPager
with fragments, it will call ViewPager
's fragment, not a previous one.
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
. So a need in onResume
in some cases will disappear.
回答16:
Calling ft.replace
should trigger the onPause (of the replaced fragment) and onResume (of the replacing fragment).
I do notice that your code inflates login_fragment
on the home fragment, and also doesn't return the views in the onCreateView. If these are typos, can you show how these fragments are being called from within your activity?
回答17:
Although with different code, I experienced the same problem as the OP, because I originally used
fm.beginTransaction()
.add(R.id.fragment_container_main, fragment)
.addToBackStack(null)
.commit();
instead of
fm.beginTransaction()
.replace(R.id.fragment_container_main, fragment)
.addToBackStack(null)
.commit();
With "replace" the first fragment gets recreated when you return from the second fragment and therefore onResume() is also called.
回答18:
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
来源:https://stackoverflow.com/questions/11326155/fragment-onresume-onpause-is-not-called-on-backstack