Fragment onResume () & onPause () ไม่ได้ถูกเรียกบน backstack


196

ฉันมีหลายส่วนในกิจกรรม บนปุ่มคลิกฉันกำลังเริ่มแฟรกเมนต์ใหม่เพิ่มลงในแบ็คสแต็ค ฉันคาดว่าจะมี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();
  }
}

สิ่งที่ฉันคาดไว้คือ

  1. เมื่อคลิกปุ่ม, LoginFragmentได้รับการแทนที่ด้วย HomeFragment , onPause()ของLoginFragmentและonResume()ของ HomeFragmentได้รับเรียกว่า
  2. เมื่อกลับถูกกดHomeFragmentจะ poped ออกมาและLoginFragmentจะเห็นและonPause()ของHomeFragmentและonResume()ของLoginFragment ได้รับเรียกว่า

สิ่งที่ฉันได้รับคือ

  1. เมื่อคลิกที่ปุ่มHomeFragmentจะถูกแทนที่อย่างถูกต้อง LoginFragment , onResume () ของHomeFragmentเรียกว่า แต่ onPause () ของLoginFragmentไม่เคยถูกเรียก
  2. เมื่อกดกลับหน้าแรกHomeFragmentอย่างถูกต้อง popping เพื่อเปิดเผย LoginFragment , onPause () ของHomeFragmentได้รับเรียก แต่ onResume () ของLoginFragmentไม่เคยได้รับการเรียก

นี่เป็นพฤติกรรมปกติหรือไม่ ทำไมเป็นonResume()ของLoginFragmentไม่ได้รับการเรียกว่าเมื่อกดปุ่มย้อนกลับ


เพิ่มรหัสกิจกรรมที่จัดการกับส่วนย่อย
อวยพร

ฉันมีปัญหาตัวอย่างเมื่อไม่ได้รับการเรียกหยุดคุณแก้ไขปัญหานี้ได้อย่างไร
Sam

ฉันมีปัญหาเดียวกัน แต่รู้ว่าฉันใช้ ft2.add (); แทน ft2.replace () มีเพียงเหตุผลอื่นเท่านั้นที่จะเกิดขึ้นหากกิจกรรมของคุณอ้างอิงถึงส่วนย่อย (เพิ่มลงในคอลเล็กชันหรือกำหนดให้กับตัวแปรคลาส)
Friggles

3
ฉันมีปัญหาเดียวกัน ฉันสังเกตเห็นว่า. แทนที่ () จะเรียกวิธีการวงจรชีวิตที่จำเป็น แต่มันทำลายส่วน นอกจากนี้ onSaveInstanceState จะไม่ถูกเรียกใช้ เช่นนี้ฉันไม่สามารถรักษาสถานะไว้ได้ ดังนั้นฉันต้องใช้เพิ่ม แต่ไม่มีการเรียก onResume / Pause :(
ariets

FWIW ประสบการณ์ของฉันคือการสนับสนุนแฟรกเมนต์ไลบรารีเรียก onPause และ onResume เมื่อกด / popping backstack แต่ชิ้นส่วนในตัว Android ไม่มี ยังไม่พบวิธีแก้ปัญหาที่เหมาะสมสำหรับสิ่งนั้น
benkc

คำตอบ:


187

ชิ้นส่วนonResume()หรือonPause()จะถูกเรียกเฉพาะเมื่อมีกิจกรรมonResume()หรือonPause()ถูกเรียก Activityพวกเขาจะคู่แน่นไป

อ่านการจัดการส่วนเศษวงจรชีวิตของบทความนี้


1
ในบทความมีการกล่าวว่า "เมื่อกิจกรรมมาถึงสถานะที่กลับสู่สถานะเดิมคุณสามารถเพิ่มและนำส่วนย่อยออกจากกิจกรรมได้อย่างอิสระดังนั้นเฉพาะเมื่อกิจกรรมอยู่ในสถานะที่กลับมาทำงานอีกครั้งวงจรชีวิตของการเปลี่ยนแปลงชิ้นส่วนสามารถทำได้อย่างอิสระ" สิ่งนี้หมายความว่าแฟรกเมนต์ onResume สามารถถูกเรียกใช้แม้ว่ากิจกรรม onResume จะไม่ถูกเรียก?
v4r

3
สำหรับชิ้นส่วนเนทีฟ (ไม่รองรับ) ใน 4.4 (ไม่แน่ใจว่าเป็นจริงสำหรับเวอร์ชันเก่า) onPause () และ onResume () ถูกเรียกไม่เพียง แต่เมื่อเหตุการณ์เหล่านี้เกิดขึ้นในกิจกรรม แต่ตัวอย่างเช่นเมื่อคุณโทรแทนที่ () หรือเพิ่ม () / remove () ในขณะที่ทำธุรกรรมดังนั้นคำตอบนี้ทำให้เข้าใจผิดอย่างน้อยสำหรับ Android เวอร์ชันล่าสุด
Dmide

19
ตามเอกสารนั้นชิ้นส่วนควรจะถูกย้ายไปที่สถานะหยุดจริงเมื่อเปลี่ยนเป็น backstack แต่ไม่เพียง แต่เป็น onPause และ onResume เท่านั้นที่ไม่ได้รับการโทรรวมทั้ง onStop และ onStart - หรือสำหรับวิธีการอื่น ๆ ตลอดอายุการใช้งาน ดังนั้นคู่มือจึงทำให้เข้าใจผิดอย่างแน่นอน
benkc

1
แม้ว่าจะไม่เกี่ยวข้องกัน แต่ฉันพบคำถามนี้ในขณะที่ค้นหาปัญหาในonPause()การโทรหาonSaveInstanceState()แทนที่จะเป็นก่อนหน้านั้น สิ่งนี้สามารถทำซ้ำได้หากคุณมีลูกคนอื่นในฉันFragmentStatePagerAdapter(พูดว่าคุณย้ายจากเด็ก 0 ไปเป็นเด็ก 2 บันทึกตนเองด้วยสิ่งนี้เกิดขึ้นเพราะเด็ก 0 ถูกทำลายเมื่อเด็ก 2 เปิด )
Sufian

ไม่แน่ใจคุณหมายถึงอะไร. การเรียก ft.replace ควรเรียกใช้ onPause (ของแฟรกเมนต์ที่ถูกแทนที่) และ onResume (ของแฟรกเมนต์ที่แทนที่) สิ่งนี้จะกระทำโดยไม่คำนึงถึงกิจกรรมใด ๆ ...
David Refaeli

20
  • เนื่องจากคุณได้ใช้ft2.replace(), FragmentTransaction.remove() วิธีการที่เรียกว่าและLoginfragmentจะถูกลบออก อ้างถึงนี้ ดังนั้นonStop()การLoginFragmentจะถูกเรียกแทนonPause()จะถูกเรียกแทน(เนื่องจากชิ้นส่วนใหม่แทนที่ส่วนเก่าอย่างสมบูรณ์)
  • แต่เนื่องจากคุณได้นอกจากนี้ยังใช้ft2.addtobackstack()รัฐที่Loginfragmentจะถูกบันทึกเป็นมัดและเมื่อคุณคลิกปุ่มย้อนกลับจากHomeFragment, onViewStateRestored()จะถูกเรียกตามด้วยของonStart() LoginFragmentดังนั้นในที่สุดonResume()จะไม่ถูกเรียก

1
onViewStateRestoredถูกเรียกว่าถ้าคุณมีsetRetainInstance(true)
Farid

14

ต่อไปนี้เป็นคำตอบของ 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;
}

9

ถ้าคุณอยากที่จะเปลี่ยนชิ้นส่วนภายในส่วนอื่น ๆ ที่คุณควรใช้ซ้อนเศษ

ในรหัสของคุณคุณควรแทนที่

final FragmentManager mFragmentmanager =  getFragmentManager();

กับ

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
            }

        }
    });

แต่ระวังถ้ามีมากกว่าหนึ่งชิ้นมองเห็นได้


ขอขอบคุณการทำงาน แต่ถ้ามีเพียงหนึ่งส่วนจะมองเห็นได้ (เช่นนั้นViewPagerมีเศษจะอ้างอิงถึงชิ้นส่วนของมัน)
CoolMind

3

ฉันมีรหัสคล้ายกับของคุณมากและถ้ามันใช้งานได้บน Pause () และ 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

ส่วนบนสร้างมุมมอง:

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

 }

2

สิ่งที่ฉันทำในชิ้นส่วนของเด็ก:

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

แล้วแทนที่ onResume บน ParentFragment


1
คุณไม่ควรเรียกวิธีการวงจรชีวิตด้วยตนเองโดยเฉพาะอย่างยิ่งภายในซึ่งกันและกัน
breakline

@breakline เทคนิคนี้ใช้ได้ผล คุณมีวิธีอื่นไหม
Vikash Parajuli

ใช่คุณควรเพิ่มการติดตั้งใช้งานของคุณเองเพราะระบบเรียกว่าวงจรชีวิตยังถูกเรียกใช้และถ้าคุณเรียกวิธีการวงจรชีวิตในแต่ละอื่น ๆ เช่นนี้คุณอาจ (และเป็นไปได้มากที่สุด) ทำให้เกิดปัญหาในภายหลัง
breakline

1

คุณง่ายไม่สามารถเพิ่มชิ้นส่วนให้กับส่วน สิ่งนี้ต้องเกิดขึ้นใน FragmentActivity ฉันคิดว่าคุณกำลังสร้าง LoginFragment ใน FragmentActivity ดังนั้นเพื่อให้การทำงานนี้คุณต้องเพิ่ม HomeFragment ผ่าน FragmentActivity เมื่อเข้าสู่ระบบปิด

ประเด็นทั่วไปคือคุณต้องการคลาส FragmentActivity จากตำแหน่งที่คุณเพิ่ม Fragment แต่ละอันเข้ากับ FragmentManager มันเป็นไปไม่ได้ที่จะทำเช่นนี้ภายในระดับส่วน


ใช่พวกเขาอยู่ในกิจกรรมชิ้นส่วน
Krishnabhadra

ถ้าแฟรกเมนต์ถูกเพิ่มแบบไดนามิกคุณสามารถเพิ่มแฟรกเมนต์ได้มากเท่าที่คุณต้องการในแฟรกเมนต์ แต่ไม่รวมอยู่ในแท็ก xml <fragment> ที่
ถูกต้อง

1

หากคุณเพิ่มส่วนใน XML คุณจะไม่สามารถสลับชิ้นส่วนแบบไดนามิกได้ เกิดอะไรขึ้นกับพวกเขามากเกินไปดังนั้นพวกเขาจึงไม่ต้องยิงอย่างที่คาดไว้ มีการบันทึกปัญหาไว้ในคำถามนี้FragmenManager แทนที่ทำให้ภาพซ้อนทับ

เปลี่ยน 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();
}

1

จากคำตอบของ@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ในบางกรณีจะหายไป


1

ฉันใช้ในกิจกรรมของฉัน - KOTLIN

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

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

0

แฟรกเมนต์ต้องถูกฝังอยู่ในกิจกรรมเสมอและวงจรชีวิตของแฟรกเมนต์จะได้รับผลกระทบโดยตรงจากวงจรชีวิตของโฮสต์กิจกรรม ตัวอย่างเช่นเมื่อกิจกรรมถูกหยุดชั่วคราวดังนั้นชิ้นส่วนทั้งหมดจะอยู่ในนั้นและเมื่อกิจกรรมถูกทำลายดังนั้นชิ้นส่วนทั้งหมดจะถูก


0

onPause() วิธีการทำงานในชั้นเรียนกิจกรรมที่คุณสามารถใช้:

public void onDestroyView(){
super.onDestroyView    
}

เพื่อจุดประสงค์เดียวกัน ..


0

ทำตามขั้นตอนด้านล่างและคุณจะได้รับคำตอบที่จำเป็น

1- สำหรับทั้งสองแฟรกเมนต์ให้สร้างพาเรนต์นามธรรมใหม่
2- เพิ่มวิธีนามธรรมแบบกำหนดเองที่ควรนำมาใช้โดยทั้งสองวิธี
3- เรียกมันจากอินสแตนซ์ปัจจุบันก่อนแทนที่ด้วยอันที่สอง


มันช่วยได้อย่างไรในสถานการณ์ที่ผู้ใช้ส่งคืนจากแฟรกเมนต์ 2 เป็นแฟรกเมนต์ 1
CoolMind

0

การเรียกร้อง ft.replaceควรเรียกใช้ onPause (ของแฟรกเมนต์ที่ถูกแทนที่) และ onResume (ของแฟรกเมนต์ที่แทนที่)

ฉันสังเกตเห็นว่าโค้ดของคุณขยายlogin_fragmentตามส่วนของบ้านและจะไม่ส่งคืนมุมมองใน onCreateView หากสิ่งเหล่านี้เป็นความผิดพลาดคุณสามารถแสดงให้เห็นว่าชิ้นส่วนเหล่านี้ถูกเรียกจากภายในกิจกรรมของคุณได้อย่างไร


นี่ไม่ใช่คำตอบจะเกิดอะไรขึ้นถ้า "เพิ่ม" เป็นสิ่งจำเป็นสำหรับการออกแบบของใครบางคน!
Farid

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 () จึงถูกเรียกอีกด้วย


นี่ไม่ใช่คำตอบจะเกิดอะไรขึ้นถ้า "เพิ่ม" เป็นสิ่งจำเป็นสำหรับการออกแบบของใครบางคน!
Farid

-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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.