ทำอย่างไรถึงจะมี Fragment เพื่อลบตัวมันเองออกมานั่นคือเทียบเท่ากับ finish ()?


230

ฉันกำลังแปลงแอพให้ใช้แฟรกเมนต์โดยใช้ไลบรารีความเข้ากันได้ ขณะนี้ฉันมีกิจกรรมหลายอย่าง (ABCD) ที่เชื่อมโยงกันอีกครั้ง D มีปุ่ม 'ตกลง' ซึ่งเมื่อกดโทรเสร็จสิ้นซึ่งจะเกิดฟองขึ้นonActivityResult()จะทำลาย C และ B

สำหรับรุ่นชิ้นส่วน Honycomb ก่อนของฉันแต่ละกิจกรรมเป็นกระดาษห่อหุ้มชิ้นส่วน Af Bf Cf Df อย่างมีประสิทธิภาพ กิจกรรมทั้งหมดเปิดตัวผ่านทางstartActivityForResult()และonActivityResult()ภายในแต่ละส่วนสามารถโทรอย่างมีความสุขgetActivity().finish()

ปัญหาที่ฉันมีอยู่ใน Honeycomb version ฉันมีเพียงกิจกรรมเดียว A และแฟรกเมนต์ Bf, Cf, Df ที่โหลดโดยใช้ FragmentManagerจะเต็มไปใช้

สิ่งที่ฉันไม่เข้าใจคือสิ่งที่ต้องทำใน Df เมื่อกด 'ตกลง' เพื่อลบแฟรกเมนต์ Df, Cf และ Bf

ฉันพยายามให้ชิ้นส่วนโผล่ออกมาจากกอง แต่สิ่งนี้ทำให้เกิดข้อยกเว้น onActivityResult()ไม่มีประโยชน์เพราะฉันยังไม่ได้โหลดชิ้นส่วนที่ใช้startActivityForResult()จะไม่ได้ผลเพราะผมยังไม่ได้โหลดขึ้นโดยใช้ชิ้นส่วน

ฉันกำลังคิดเกี่ยวกับสิ่งที่ผิดทางนี้หรือไม่? ฉันควรจะใช้การเรียงลำดับของผู้ฟังที่สื่อสารกับทั้งผู้ปกครองส่วนหรือกิจกรรมเพื่อที่จะทำป๊อปโดยใช้ผู้จัดการการทำธุรกรรม?


7
สิ่งที่เกี่ยวกับ ((YourActivity) getActivity ()) onBackPressed ();
Viswanath Lekshmanan

@ViswanathLekshmanan ความคิดเห็นคำตอบของคุณมีประโยชน์สำหรับฉัน .. 1 โหวตขึ้นจากฉัน
Abhishek Singh

@AbhishekSingh ดีใจที่ได้ยิน :)
Viswanath Lekshmanan

@ViswanathLekshmanan :)
Abhishek Singh เมื่อ

คำตอบ:


62

สิ่งที่ฉันไม่เข้าใจคือสิ่งที่ต้องทำใน Df เมื่อกด 'ตกลง' เพื่อลบแฟรกเมนต์ Df, Cf และ Bf

ขั้นตอนที่ # 1: ให้ Df บอก D "yo! we OK คลิก!" ผ่านการเรียกวิธีการไม่ว่าจะในกิจกรรมของตัวเองหรือในอินสแตนซ์ของอินเทอร์เฟซที่จัดทำโดยกิจกรรม

ขั้นตอนที่ 2: มี D FragmentManagerเอาเศษผ่าน

กิจกรรมการโฮสต์ (D) เป็นสิ่งหนึ่งที่รู้ว่าส่วนใดที่อยู่ในกิจกรรม (เทียบกับการอยู่ในกิจกรรมอื่น ๆ ) ดังนั้นเหตุการณ์ในแฟรกเมนต์ที่อาจส่งผลกระทบต่อการผสมผสานแฟรกเมนต์ควรถูกถ่ายทอดไปยังกิจกรรมซึ่งจะทำให้การประสานการเคลื่อนไหวเหมาะสม


แต่ในเวอร์ชั่น Honeycomb ของฉันไม่มี D นั่นเป็นของฉันยาก มีเพียง activiy A ที่โหลดแฟรกเมนต์ Bf ซึ่งโหลด Cf ซึ่งโหลด Df โดยใช้ FragmentTransaction
PJL

1
@PJL: ขออภัยฉันหมายถึง A. นี่คือเหตุผลหนึ่งที่ใช้อินเทอร์เฟซผู้ฟังดังนั้นกิจกรรมหลายอย่างสามารถตอบสนองเหตุการณ์ "เราได้รับคลิกตกลง" จาก Df
CommonsWare

1
ในขณะที่ฉันกำลังพอร์ตฉันเรียกเมธอด listener จากแฟรกเมนต์ onActivityResult ของ Df ของแฟรกเมนต์ในกิจกรรมหลังจากนั้นฉันจึงเรียก popBackStack บน FragmentManager อย่างไรก็ตามสิ่งนี้ส่งผลให้เกิดการยกเว้น "IllegalStateException: ไม่สามารถทำการกระทำนี้หลังจาก onSaveInstanceState 'ความคิดใด ๆ เกี่ยวกับวิธีที่ฉันสามารถเอาชนะสิ่งนี้ได้อย่างไร
PJL

1
@DiegoPalomar: finish()ควรจะพอเพียง
CommonsWare

1
@ user3364963: เป็นเวลานานแล้วตั้งแต่ฉันตรวจสอบ แต่ IIRC มันถูกทำลายเมื่อมันโผล่ออกมาจากกองหลัง เพิ่มonDestroy()วิธีการในส่วนของคุณและดูว่ามันได้รับการเรียก
CommonsWare

313

แม้ว่ามันอาจไม่ใช่วิธีที่ดีที่สุด แต่สิ่งที่ใกล้เคียงที่สุดที่ฉันคิดได้คือนี่คือการสนับสนุน / ไลบรารีความเข้ากันได้

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();

หรือ

getActivity().getFragmentManager().beginTransaction().remove(this).commit();

มิฉะนั้น.

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


11
ในขณะที่วิธีการนี้ใช้งานได้หากคุณใช้ addToBackStack (null) มันจะปล่อยปุ่มตัวจัดการกลับ +1 ดังนั้นคุณต้องกดสองครั้ง
user123321

34
"ป๊อป" ชิ้นส่วนจาก FragmentManager
user123321

2
ฉันลองขั้นตอนข้างต้นแล้ว แต่ได้ให้ข้อผิดพลาดนี้ "java-lang-legalstateexception-can-not-not-perform-this-action-after-onsaveinstance" ดังนั้นที่แน่นอนฉันต้องลบส่วน
KK_07k11A0585

6
คำตอบนี้เป็นการปฏิบัติที่ไม่ดีและไม่ควรได้รับการโหวต ชิ้นส่วนนั้นไม่ได้มีไว้สำหรับการระวังตัวเองเช่นนี้ มันฆ่าการนำกลับมาใช้ใหม่ซึ่งเป็นจุดเศษ! แฟรกเมนต์ควรส่งสัญญาณกิจกรรมเพื่อลบออกด้วยวิธีการใด ๆ วิธีการติดต่อกลับเป็นวิธีที่นิยม developer.android.com/training/basics/fragments/…
colintheshots

2
@ManfredMoser ฉันไม่เห็นด้วย นี่เป็นประเด็นของคำถาม เขามีชิ้นส่วนทั้งหมดที่จะลบออก รหัสนี้ไม่มีการตรวจสอบเป็นโมฆะหรือตรวจสอบว่ามีการแนบกิจกรรม มันจะแตกในการผลิตเพราะมันขึ้นอยู่กับหลายสิ่งมากเกินไปที่ชิ้นส่วนไม่ทราบ
colintheshots

263

คุณสามารถใช้วิธีการด้านล่างทำงานได้ดี:

getActivity().getSupportFragmentManager().popBackStack();

44
คำตอบนี้เหมือนกับคำตอบที่ดีกว่าคำตอบ 10 เท่าตรงประเด็น
nikib3ro

50
นอกจากนี้ยังเลวร้ายยิ่งกว่าการออกแบบมากกว่าที่ยอมรับ 10 เท่า ส่วนที่ควรจะเป็นขนาดเล็ก "ผู้ช่วย" กับกิจกรรมและไม่ควร bei ในการควบคุมตัวเองมากกว่าหรือชิ้นส่วนอื่น ๆ
แพทริค

6
การแก้ปัญหาไม่ถูกต้องตามที่ @avalancha ชี้ให้เห็น ดูที่developer.android.com/guide/components/?hl=th
the_dark_destructor

2
ฉันใช้วิธีการนี้กับActivityResultและได้รับข้อผิดพลาด "ไม่สามารถทำการกระทำนี้หลังจาก onSaveInstanceState" ฉันจะแก้ไขมันได้อย่างไร
Jayeshkumar Sojitra

1
popBackStack()เป็นวิธีแก้ไขปัญหาเดียวที่ใช้งานได้เมื่อคุณต้องการตั้งชื่อแถบการกระทำกลับสู่สถานะก่อนหน้าหลังจากคุณลบส่วน มิฉะนั้นฉันไม่จำเป็นต้องรวมทั้งโซลูชันที่ได้รับคะแนนสูงจากstackoverflow.com/questions/13472258/และโซลูชันนี้ที่นี่เพื่อตั้งชื่อแถบการกระทำที่เหมาะสมเสมอหลังจากกรณีการใช้งานที่หลากหลาย เช่นการกดย้อนกลับการลบการเพิ่มการแทนที่และอื่น ๆ
Bevor

36

คุณควรปล่อยให้กิจกรรมจัดการกับการเพิ่มและลบแฟรกเมนต์ตามที่คอมมอนส์แวร์บอกว่าให้ใช้ผู้ฟัง นี่คือตัวอย่าง:

public class MyActivity extends FragmentActivity implements SuicidalFragmentListener {

    // onCreate etc

    @Override
    public void onFragmentSuicide(String tag) {
        // Check tag if you do this with more than one fragmen, then:
        getSupportFragmentManager().popBackStack();
    }
}

public interface SuicidalFragmentListener {
    void onFragmentSuicide(String tag);
}

public class MyFragment extends Fragment {

    // onCreateView etc

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
           suicideListener = (SuicidalFragmentListener) activity;
        } catch (ClassCastException e) {
           throw new RuntimeException(getActivity().getClass().getSimpleName() + " must implement the suicide listener to use this fragment", e);
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // Attach the close listener to whatever action on the fragment you want
        addSuicideTouchListener();
    }

    private void addSuicideTouchListener() {
        getView().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              suicideListener.onFragmentSuicide(getTag());
            }
        });
    }
}

5
สไตล์ emo ฆ่าตัวตายมาก? วิธีการเกี่ยวกับ "SelfClosing" หรือ "AutoClose" หรือ "SmartClose" (r)
DritanX

21
มันยังไม่ปิดมันเป็นDYING FOREVER ;-(
Blundell

3
นี่เป็นวิธีที่สะอาดกว่าคำตอบอื่น ๆ กิจกรรมสร้างและนำเสนอชิ้นส่วนและควรควบคุมวงจรชีวิตของมัน เมื่อมีสิ่งใดเกิดขึ้นซึ่งบ่งชี้ว่าไม่ควรอยู่ในส่วนที่ขาดอีกต่อไปมันควรบอกกิจกรรมว่าและปล่อยให้กิจกรรมลบออก
Christopher Pickslay

7
ในทางเทคนิคถ้าเราควรจะมีกิจกรรมฆ่าชิ้นส่วนชิ้นส่วนนั้นจะไม่ฆ่าตัวตาย กิจกรรมเป็นการฆาตกรรม
Casey Murray

17

ในกิจกรรม / AppCompatActivity:

@Override
public void onBackPressed() {
    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
        // if you want to handle DrawerLayout
        mDrawerLayout.closeDrawer(GravityCompat.START);
    } else {
        if (getFragmentManager().getBackStackEntryCount() == 0) {
            super.onBackPressed();
        } else {
            getFragmentManager().popBackStack();
        }
    }
}

แล้วโทรในส่วน:

getActivity().onBackPressed();

หรือชอบที่ระบุไว้ในคำตอบอื่น ๆ เรียกสิ่งนี้ในส่วน:

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();

5

หากคุณกำลังใช้ส่วนประกอบการนำทางใหม่นั้นง่ายเหมือน

findNavController().popBackStack()

มันจะทำ FragmentTransaction ทั้งหมดให้คุณ


4

ดูว่า DialogFragment ของคุณตรงตามความต้องการของคุณหรือไม่ DialogFragment มีวิธีการเลิกจ้าง () ทำความสะอาดมากในความคิดของฉัน


3

ฉันสร้างวิธีการง่าย ๆ สำหรับสิ่งนั้น

popBackStack(getSupportFragmentManager());

กว่าวางไว้ในชั้นเรียน ActivityUtils ของฉัน

public static void popBackStack(FragmentManager manager){
        FragmentManager.BackStackEntry first = manager.getBackStackEntryAt(0);
        manager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

มันใช้งานได้ดีสนุก!


2
ฉันไม่ได้รับวัตถุประสงค์ของวิธีการของคุณ popBackStack ดั้งเดิมดูเหมือนจะเพียงพอแล้ว
เหลือเชื่อ Jan

1

OnCreate:

//Add comment fragment
            container = FindViewById<FrameLayout>(Resource.Id.frmAttachPicture);
            mPictureFragment = new fmtAttachPicture();

            var trans = SupportFragmentManager.BeginTransaction();
            trans.Add(container.Id, mPictureFragment, "fmtPicture");
            trans.Show(mPictureFragment); trans.Commit();

นี่คือวิธีที่ฉันซ่อนส่วนในเหตุการณ์คลิก 1

//Close fragment
    var trans = SupportFragmentManager.BeginTransaction();
    trans.Hide(mPictureFragment);
    trans.AddToBackStack(null);
    trans.Commit();

จากนั้นแสดงให้เห็นว่ามันกลับมาเป็นเหตุการณ์ int 2

var trans = SupportFragmentManager.BeginTransaction();
            trans.Show(mPictureFragment); trans.Commit();

1

หากคุณต้องการป๊อปแบ็คจากส่วนที่สี่ในประวัติแบ็คสต็อกเป็นอันดับแรกให้ใช้แท็ก !!!

เมื่อคุณเพิ่มส่วนแรกคุณควรใช้สิ่งนี้:

getFragmentManager.beginTransaction.addToBackStack("A").add(R.id.container, FragmentA).commit() 

หรือ

getFragmentManager.beginTransaction.addToBackStack("A").replace(R.id.container, FragmentA).commit()

และเมื่อคุณต้องการแสดง Fragments B, C และ D คุณใช้สิ่งนี้:

getFragmentManager.beginTransaction.addToBackStack("B").replace(R.id.container, FragmentB, "B").commit()

และตัวอักษรอื่น ๆ ....

หากต้องการกลับไปที่FragmentA เพียงแค่เรียกpopBackStack(0, "A")ใช้ใช้แฟล็กที่คุณระบุเมื่อคุณเพิ่มและโปรดทราบว่ามันต้องเป็นแฟล็กเดียวกันในคำสั่งaddToBackStack()ไม่ใช่ที่ใช้ในการแทนที่หรือเพิ่มคำสั่ง

ไม่เป็นไร;)


ฉันได้ทดสอบ 'popBackStack (0, "A")' แล้วและแอปของฉันกลับมาเป็นแฟรกเมนต์ A แต่ฉันต้องการเพียงส่วนที่จะถูกลบออกจาก Back Stack ... ฉันจะลบแฟรกเมนต์ออกจากสแต็กได้อย่างไร
KryNaC


-9

ทำไมไม่เพียงแค่:

getActivity () จบ ().


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