Đoạn onResume () & onPause () không được gọi trên backstack


194

Tôi có nhiều đoạn trong một hoạt động. Trên một nút bấm, tôi đang bắt đầu một đoạn mới, thêm nó vào backstack. Tôi tự nhiên mong đợi onPause()phương thức của Fragment hiện tại và onResume()Fragment mới sẽ được gọi. Vâng, nó không xảy ra.

Đăng nhậpFragment.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();
  }
}

Những gì tôi mong đợi, là,

  1. Khi nút được nhấn vào, LoginFragment được thay thế bằng HomeFragment , onPause()của LoginFragment , và onResume()của HomeFragment được gọi
  2. Khi trở lại được nhấn, HomeFragment được poped ra và LoginFragment được nhìn thấy, và onPause()của HomeFragmentonResume()của LoginFragment được gọi.

Những gì tôi đang nhận được là,

  1. Khi nút được nhấn vào, HomeFragment được thay thế một cách chính xác LoginFragment , onResume () của HomeFragment được gọi, nhưng onPause () của LoginFragment không bao giờ được gọi.
  2. Khi được nhấn lại, HomeFragment sẽ xuất hiện chính xác để hiển thị LoginFragment , onPause () của HomeFragment được gọi, nhưng onResume () của LoginFragment không bao giờ được gọi.

Đây có phải là hành vi bình thường? Tại sao là onResume()của LoginFragment không nhận được gọi khi tôi bấm nút quay lại.


Thêm mã hoạt động xử lý các đoạn.
blentsm

Tôi đang gặp vấn đề mẫu, tạm dừng không được gọi, bạn đã giải quyết vấn đề này như thế nào,
Sam

Tôi đã có cùng một vấn đề nhưng nhận ra tôi đang sử dụng ft2.add (); thay vì ft2.replace (). Chỉ có lý do khác là nếu hoạt động của bạn giữ một tham chiếu đến đoạn (thêm nó vào một bộ sưu tập hoặc gán nó cho một biến lớp)
Friggles

3
Tôi đang có cùng một vấn đề. Tôi nhận thấy rằng .replace () sẽ gọi các phương thức vòng đời cần thiết, nhưng về cơ bản nó sẽ phá hủy đoạn. Ngoài ra, onSaveInstanceState không được gọi. Như vậy, tôi không thể giữ trạng thái của nó. Vì vậy, tôi cần sử dụng add, nhưng onResume / Tạm dừng không được gọi là :(
ariets

FWIW, kinh nghiệm của tôi là các đoạn thư viện hỗ trợ gọi onPause và onResume khi đẩy / bật backstack, nhưng các đoạn tích hợp trong Android thì không. Chưa tìm được cách giải quyết phù hợp cho việc đó.
benkc

Câu trả lời:


185

Các mảnh onResume()hoặc onPause()sẽ chỉ được gọi khi các Hoạt động onResume()hoặc onPause()được gọi. Chúng được gắn chặt với Activity.

Đọc phần Xử lý phần Vòng đời của bài viết này .


1
Trong bài viết, người ta nói rằng "một khi hoạt động đạt đến trạng thái được nối lại, bạn có thể tự do thêm và loại bỏ các mảnh cho hoạt động. Do đó, chỉ khi hoạt động ở trạng thái được nối lại, vòng đời của một mảnh có thể thay đổi độc lập." Điều này có nghĩa là đoạn onResume có thể được gọi ngay cả khi hoạt động onResume không được gọi?
v4r

3
như đối với các đoạn gốc (không hỗ trợ) trong 4.4 (không chắc là nó đúng với các phiên bản cũ hơn), onPause () và onResume () được gọi không chỉ khi các sự kiện này xảy ra trong hoạt động, mà là khi bạn gọi thay thế () hoặc thêm () / remove () trong khi thực hiện giao dịch, vì vậy câu trả lời này gây hiểu nhầm ít nhất là cho các phiên bản gần đây của Android.
Dmide

19
Theo tài liệu đó, đoạn thực sự nên được chuyển đến trạng thái dừng khi hoán đổi vào backstack. Nhưng không chỉ onPause và onResume không được gọi, cả onStop và onStart - hoặc đối với vấn đề đó, bất kỳ phương thức vòng đời nào khác. Vì vậy, hướng dẫn chắc chắn là sai lệch.
benkc

1
Mặc dù không liên quan nhưng tôi đã tìm thấy câu hỏi này trong khi tìm kiếm vấn đề của mình onPause()để được gọi sau onSaveInstanceState()thay vì trước nó. Điều này có thể được sao chép nếu bạn để một đứa trẻ khác nhau trong tôi FragmentStatePagerAdapter(nói rằng bạn di chuyển từ trẻ từ 0 đến đứa trẻ 2, ghi chú cá nhân, điều này xảy ra bởi vì con 0 bị phá hủy khi con 2 mở )
Sufian

Không chắc chắn những gì bạn có ý nghĩa. Gọi ft.replace sẽ kích hoạt onPause (của đoạn được thay thế) và onResume (của đoạn thay thế). Điều này được thực hiện bất kể hoạt động nào ...
David Refaeli

20
  • Vì bạn đã sử dụng ft2.replace(), FragmentTransaction.remove() phương thức được gọi và Loginfragmentsẽ bị xóa. Tham khảo điều này . Vì vậy, onStop()các LoginFragmentsẽ được gọi thay vì onPause(). (Vì đoạn mới thay thế hoàn toàn cái cũ).
  • Nhưng kể từ khi bạn cũng đã sử dụng ft2.addtobackstack(), tình trạng của Loginfragmentsẽ được lưu dưới dạng bó và khi bạn bấm lại nút từ HomeFragment, onViewStateRestored()sẽ được gọi tiếp theo onStart()của LoginFragment. Vì vậy, cuối cùng onResume()sẽ không được gọi.

1
onViewStateRestoredđược gọi nếu bạn cósetRetainInstance(true)
Farid

14

Đây là phiên bản mạnh mẽ hơn của câu trả lời của Gor (sử dụng Fragments.size () không đáng tin cậy do kích thước không bị giảm sau khi mảnh vỡ được bật lên)

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. 
                    }
                }
            }
        }
    });

Và phương thức 'getCienTopFragment':

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;
}

9

Nếu bạn thực sự muốn thay thế mảnh vỡ bên trong mảnh khác, bạn nên sử dụng Mảnh vỡ lồng .

Trong mã của bạn, bạn nên thay thế

final FragmentManager mFragmentmanager =  getFragmentManager();

với

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
            }

        }
    });

nhưng hãy cẩn thận nếu nhìn thấy nhiều hơn một mảnh


Cảm ơn, nó hoạt động, nhưng nếu chỉ có một mảnh được nhìn thấy (vì vậy, ViewPagervới các mảnh sẽ tham chiếu đến các mảnh của nó).
CoolMind

3

Tôi có một mã rất giống với mã của bạn và nếu nó hoạt động trênPause () và onResume (). Khi thay đổi đoạn, các chức năng này được kích hoạt tương ứng.

Mã trong đoạn:

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

}

Đăng nhập khi thay đổi đoạn:

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

Đoạn trênCreateView:

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;
}

Hành động khi tôi quay lại (trong hoạt động nơi tôi tải đoạn):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

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

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

 }

2

Những gì tôi làm trong mảnh con:

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

Và sau đó ghi đè lênResume trên ParentFragment


1
Bạn không bao giờ nên gọi các phương thức vòng đời theo cách thủ công, đặc biệt là bên trong nhau
vỡ

@breakline kỹ thuật này hoạt động. Bạn có cách nào khác không?
Vikash Parajuli

Có, bạn nên thêm triển khai của riêng mình để gọi vì các phương thức vòng đời cũng được hệ thống gọi và nếu bạn gọi các phương thức vòng đời bên trong nhau như thế này thì bạn có thể (và rất có thể sẽ) gây ra các sự cố sau này.
vỡ

1

Bạn đơn giản không thể thêm một mảnh vào một mảnh. Điều này cần phải xảy ra trong FragmentActivity. Tôi giả sử bạn đang tạo LoginFragment trong FragmentActivity, vì vậy để hoạt động này, bạn cần thêm HomeFragment thông qua FragmentActivity khi đóng đăng nhập.

Điểm chung là bạn cần một lớp FragmentActivity từ đó bạn thêm từng Fragment vào FragmentManager. Không thể làm điều này trong một lớp Fragment.


Vâng, họ đang ở trong một hoạt động mảnh.
Krishnabhadra

nếu các đoạn được thêm vào một cách linh hoạt thì bạn có thể thêm nhiều đoạn như bạn muốn vào một đoạn nhưng không phải là các đoạn được xác định trong thẻ xml <Fragment>
Đối số bất hợp pháp

1

Nếu bạn thêm đoạn trong XML, bạn không thể trao đổi chúng một cách linh hoạt. Điều gì xảy ra là họ quá mức, vì vậy họ không tổ chức các sự kiện như mong đợi. Vấn đề được ghi lại trong câu hỏi này. FragmenManager thay thế làm lớp phủ

Biến middle_fragment thành FrameLayout và tải nó như bên dưới và các sự kiện của bạn sẽ kích hoạt.

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

1

Bạn có thể thử điều này,

Bước 1: Ghi đè phương thức Tab được chọn trong hoạt động của bạn

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

Bước 2: Sử dụng phương thức tĩnh thực hiện những gì bạn muốn trong đoạn của bạn,

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

1

Tôi sử dụng trong hoạt động của mình - KOTLINE

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

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

0

Một đoạn phải luôn được nhúng trong một hoạt động và vòng đời của đoạn bị ảnh hưởng trực tiếp bởi vòng đời của hoạt động chủ. Ví dụ: khi hoạt động bị tạm dừng, tất cả các mảnh trong đó cũng bị hủy và khi hoạt động bị hủy, tất cả các mảnh cũng vậy


0

onPause() phương thức hoạt động trong lớp hoạt động bạn có thể sử dụng:

public void onDestroyView(){
super.onDestroyView    
}

cho cùng một mục đích ..


0

Thực hiện theo các bước dưới đây và bạn sẽ nhận được câu trả lời cần thiết

1- Đối với cả hai mảnh, tạo một cha mẹ trừu tượng mới.
2- Thêm một phương thức trừu tượng tùy chỉnh nên được thực hiện bởi cả hai.
3- Gọi nó từ ví dụ hiện tại trước khi thay thế bằng cái thứ hai.


Nó giúp ích như thế nào trong tình huống khi người dùng quay lại từ đoạn 2 đến đoạn 1?
CoolMind

0

Dựa trên câu trả lời của @Gor tôi đã viết tương tự trong Kotlin. Đặt mã này trong onCreate()một hoạt động. Nó hoạt động cho một mảnh nhìn thấy được. Nếu bạn có ViewPagermảnh vỡ, nó sẽ gọi ViewPagerlà mảnh vỡ, không phải là mảnh vỡ trước đó.

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

Sau khi đọc https://medium.com/@elye.project/puheads-fragment-stack-pop-cause-su-on-toolbar-8b947c5c07c6 Tôi hiểu rằng sẽ tốt hơn trong nhiều tình huống để gắn các đoạn mới với replace, không add. Vì vậy, một nhu cầu onResumetrong một số trường hợp sẽ biến mất.


0

Gọi điện thoại ft.replace sẽ kích hoạt onPause (của đoạn được thay thế) và onResume (của đoạn thay thế).

Tôi nhận thấy rằng mã của bạn tăng cao login_fragmenttrên đoạn mã nhà và cũng không trả về các khung nhìn trong onCreateView. Nếu đây là những lỗi chính tả, bạn có thể chỉ ra cách các đoạn này được gọi từ bên trong hoạt động của bạn không?


Đây không phải là một câu trả lời, nếu "thêm" là phải cho thiết kế của ai đó?!
Farid

0

Mặc dù với các mã khác nhau, tôi gặp phải vấn đề tương tự như OP, vì ban đầu tôi đã sử dụng

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

thay vì

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

Với "thay thế" đoạn đầu tiên được tạo lại khi bạn quay lại từ đoạn thứ hai và do đó onResume () cũng được gọi.


Đây không phải là một câu trả lời, nếu "thêm" là phải cho thiết kế của ai đó?!
Farid

-2

Trong khi tạo giao dịch phân đoạn, hãy đảm bảo thêm mã sau đây.

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

Cũng đảm bảo, để cam kết giao dịch sau khi thêm nó vào backstack

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.