首页 文章

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

提问于
浏览
169

我在一个活动中有多个片段 . 在按钮上单击我开始一个新片段,将其添加到backstack . 我自然期望调用当前片段的 onPause() 方法和新片段的 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();
  }
}

我所期待的是,

  • 单击按钮时, LoginFragment 替换为 HomeFragmentonPause()LoginFragmentonResume()HomeFragment 调用

  • 当按下后退时, HomeFragment 被输出并且看到 LoginFragment ,并且_2452100_的_2452100_和 onResume() 被_2552202_调用 .

我得到的是,

  • 单击按钮时, HomeFragment 正在替换 LoginFragmentHomeFragment 的onResume()被调用,但 LoginFragment 的onPause()从未被调用 .

  • 当后退时, HomeFragment 正确弹出以显示 LoginFragmentHomeFragment 的onPause()被调用,但 LoginFragment 的onResume()从未被调用 .

这是正常行为吗?当按下后退按钮时,为什么 LoginFragmentonResume() 未被调用 .

17 回答

  • 1

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

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

  • 1

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

    阅读Handling the Fragment Lifecycle section of this article .

  • 18
    • 由于您已使用 ft2.replace() ,因此将调用 FragmentTransaction.remove() 方法并删除 Loginfragment . 请参阅this . 所以 LoginFragmentLoginFragment 将被调用而不是 onPause() . (因为新片段完全取代旧片段) .

    • 但由于您还使用了 ft2.addtobackstack()Loginfragment 的状态将保存为一个包,当您单击 HomeFragment 的后退按钮时,将调用 onViewStateRestored() ,然后调用 LoginFragmentonStart() . 所以最终 onResume() 将不会被调用 .

  • 158

    如果你真的想要替换其他片段中的片段,你应该使用Nested Fragments .

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

    final FragmentManager mFragmentmanager =  getFragmentManager();
    

    final FragmentManager mFragmentmanager =  getChildFragmentManager();
    
  • 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 的需求将会消失 .

  • -2

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

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

  • 0

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

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

    一般来说,您需要一个FragmentActivity类,从而将每个Fragment添加到FragmentManager . 在Fragment类中执行此操作是 not possible .

  • 3

    如果以XML格式添加片段,则可以像预期的那样触发 . 这个问题记录了这个问题 . FragmenManager replace makes overlay

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

    getFragmentManager().beginTransation().
        add(R.id.middle_fragment, new MiddleFragment()).commit();
    
  • 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();
    }
    
  • 0

    我在子片段中做了什么:

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

    然后在ParentFragment上覆盖onResume

  • 0

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

  • 0

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

    public void onDestroyView(){
    super.onDestroyView    
    }
    

    出于同样的目的..

  • 0

    Follow the below steps, and you shall get the needed answer

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

  • 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() .

  • 8

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

    // 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后提交事务

相关问题