โทรกลับไปยังส่วนจาก DialogFragment


159

คำถาม: จะสร้างการติดต่อกลับจาก DialogFragment ไปยัง Fragment อื่นได้อย่างไร ในกรณีของฉันกิจกรรมที่เกี่ยวข้องควรไม่ทราบถึง DialogFragment อย่างสมบูรณ์

พิจารณาฉันมี

public class MyFragment extends Fragment implements OnClickListener

เมื่อถึงจุดหนึ่งฉันก็ทำได้

DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
dialogFrag.show(getFragmentManager, null);

ที่ MyDialogFragment ดูเหมือนว่า

protected OnClickListener listener;
public static DialogFragment newInstance(OnClickListener listener) {
    DialogFragment fragment = new DialogFragment();
    fragment.listener = listener;
    return fragment;
}

แต่ไม่มีการรับประกันว่าผู้ฟังจะอยู่รอบ ๆ ถ้า DialogFragment หยุดชั่วคราวและดำเนินต่อผ่านวงจรชีวิตของมัน การรับประกันเฉพาะในแฟรกเมนต์คือการส่งผ่านผ่าน Bundle ผ่าน setArguments และ getArguments

มีวิธีอ้างอิงกิจกรรมหากควรเป็นผู้ฟัง:

public Dialog onCreateDialog(Bundle bundle) {
    OnClickListener listener = (OnClickListener) getActivity();
    ....
    return new AlertDialog.Builder(getActivity())
        ........
        .setAdapter(adapter, listener)
        .create();
}

แต่ฉันไม่ต้องการให้กิจกรรมเพื่อฟังกิจกรรมฉันต้องการแฟรกเมนต์ จริง ๆ แล้วมันอาจเป็นวัตถุ Java ใด ๆ ที่ใช้ OnClickListener

พิจารณาตัวอย่างที่เป็นรูปธรรมของ Fragment ที่นำเสนอ AlertDialog ผ่าน DialogFragment มันมีปุ่มใช่ / ไม่ใช่ ฉันจะส่งปุ่มเหล่านี้กลับไปที่ Fragment ที่สร้างขึ้นได้อย่างไร


คุณพูดถึง "แต่ไม่มีการรับประกันว่าผู้ฟังจะอยู่รอบ ๆ ถ้า DialogFragment หยุดชั่วคราวและดำเนินการต่อผ่านวงจรชีวิตของมัน" ฉันคิดว่าสถานะของชิ้นส่วนถูกทำลายระหว่าง onDestroy () หรือไม่ คุณต้องพูดถูก แต่ฉันสับสนนิดหน่อยว่าจะใช้สถานะ Fragment ได้อย่างไร ฉันจะทำซ้ำปัญหาที่คุณกล่าวถึงผู้ฟังไม่ได้รอบ?
ฌอน

ฉันไม่เห็นว่าทำไมคุณไม่สามารถใช้OnClickListener listener = (OnClickListener) getParentFragment();ใน DialogFragment แทนได้และแฟรกเมนต์หลักของคุณจะใช้อินเทอร์เฟซตามที่คุณทำ
kiruwka

นี่คือคำตอบสำหรับคำถามที่ไม่เกี่ยวข้อง แต่มันแสดงให้คุณเห็นว่าสิ่งนี้ทำในลักษณะที่สะอาด stackoverflow.com/questions/28620026/…
user2288580

คำตอบ:


190

กิจกรรมที่เกี่ยวข้องนั้นไม่ได้รับรู้ถึง DialogFragment อย่างสมบูรณ์

ชั้นส่วน:

public class MyFragment extends Fragment {
int mStackLevel = 0;
public static final int DIALOG_FRAGMENT = 1;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        mStackLevel = savedInstanceState.getInt("level");
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("level", mStackLevel);
}

void showDialog(int type) {

    mStackLevel++;

    FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
    Fragment prev = getActivity().getFragmentManager().findFragmentByTag("dialog");
    if (prev != null) {
        ft.remove(prev);
    }
    ft.addToBackStack(null);

    switch (type) {

        case DIALOG_FRAGMENT:

            DialogFragment dialogFrag = MyDialogFragment.newInstance(123);
            dialogFrag.setTargetFragment(this, DIALOG_FRAGMENT);
            dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");

            break;
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode) {
            case DIALOG_FRAGMENT:

                if (resultCode == Activity.RESULT_OK) {
                    // After Ok code.
                } else if (resultCode == Activity.RESULT_CANCELED){
                    // After Cancel code.
                }

                break;
        }
    }
}

}

คลาส DialogFragment:

public class MyDialogFragment extends DialogFragment {

public static MyDialogFragment newInstance(int num){

    MyDialogFragment dialogFragment = new MyDialogFragment();
    Bundle bundle = new Bundle();
    bundle.putInt("num", num);
    dialogFragment.setArguments(bundle);

    return dialogFragment;

}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.ERROR)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setPositiveButton(R.string.ok_button,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, getActivity().getIntent());
                        }
                    }
            )
            .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, getActivity().getIntent());
                }
            })
            .create();
}
}

100
ผมคิดว่ากุญแจสำคัญที่นี่เป็นและsetTargetFragment getTargetFragmentการใช้งานonActivityResultไม่ชัดเจนนัก มันจะเป็นการดีกว่าถ้าคุณจะประกาศวิธีการเฉพาะของคุณใน Fragment Caller และใช้วิธีนั้นแทนการใช้งานซ้ำใน ActivityResult แต่ความหมายทั้งหมดของมัน ณ จุดนั้น
รันดร์

2
ไม่ใช้ตัวแปรระดับสแต็ก
ชื่อที่ปรากฏ

6
สิ่งนี้จะมีผลต่อการเปลี่ยนแปลงการกำหนดค่าหรือไม่
Maxrunner

3
ใช้สิ่งนี้ หมายเหตุ: ไม่จำเป็นต้องมีระดับสแต็กเพื่อความอยู่รอดในการหมุนหรือการนอนหลับ แทนที่จะเป็น onActivityResult แฟรกเมนต์ของฉันใช้ DialogResultHandler # handleDialogResult (อินเทอร์เฟซที่ฉันสร้าง) @myCode จะเป็นประโยชน์อย่างยิ่งในการแสดงค่าที่เลือกในกล่องโต้ตอบที่ถูกเพิ่มเข้าใน Intent จากนั้นอ่านภายในของคุณ onActivityResult เจตนาไม่ชัดเจนสำหรับผู้เริ่มต้น
Chris Betti

7
@ ชั่วนิรันดร์การคัดค้านของคุณมีเหตุผลทั้งหมด แต่ฉันคิดว่าคุณค่าของ onActivityResult () คือการรับประกันว่าจะมีอยู่ในส่วนใด ๆ ดังนั้นชิ้นส่วนใด ๆ ที่สามารถใช้เป็นผู้ปกครอง หากคุณสร้างอินเทอร์เฟซของคุณเองและให้พาเรนต์ส่วนย่อยนำไปใช้งานส่วนนั้นลูกสามารถใช้กับผู้ปกครองที่ใช้อินเทอร์เฟซนั้นเท่านั้น การเชื่อมต่อลูกเข้ากับอินเทอร์เฟซนั้นอาจกลับมาหลอกหลอนคุณได้ถ้าคุณเริ่มใช้ลูกอย่างกว้างขวางยิ่งขึ้นในภายหลัง การใช้อินเทอร์เฟซ "ในตัว" onActivityResult () ไม่ต้องการการเชื่อมต่อเพิ่มเติมดังนั้นจึงช่วยให้คุณมีความยืดหยุ่นเพิ่มขึ้นเล็กน้อย
Dalbergia

78

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

"ไม่มีแฟรกเมนต์สำหรับ android ที่สำคัญอีกต่อไป: target_state: ดัชนี 1"

ดูเหมือนFragment#setTargetFragment()ว่าไม่ได้มีไว้สำหรับการสื่อสารระหว่างเด็กและผู้ปกครองส่วน แต่สำหรับการสื่อสารระหว่างพี่น้อง - ชิ้นส่วน

ดังนั้นทางเลือกคือการสร้างส่วนย่อยของไดอะล็อกเช่นนี้โดยใช้ChildFragmentManagerชิ้นส่วนพาเรนต์แทนที่จะใช้กิจกรรมFragmentManager:

dialogFragment.show(ParentFragment.this.getChildFragmentManager(), "dialog_fragment");

และโดยใช้อินเทอร์เฟซในonCreateวิธีการDialogFragmentคุณจะได้รับส่วนผู้ปกครอง:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        callback = (Callback) getParentFragment();
    } catch (ClassCastException e) {
        throw new ClassCastException("Calling fragment must implement Callback interface");
    }
}

สิ่งเดียวที่เหลือคือการโทรวิธีการโทรกลับของคุณหลังจากขั้นตอนเหล่านี้

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับปัญหาคุณสามารถตรวจสอบลิงค์: https://code.google.com/p/android/issues/detail?id=54520


2
นอกจากนี้ยังทำงานร่วมกับ onAttach (บริบทบริบท) ที่นำมาเพิ่มใน api 23.
Santa Teclado

1
นี่ควรเป็นคำตอบที่ยอมรับได้ คำตอบที่ยอมรับในปัจจุบันคือ buggy และชิ้นส่วนไม่ได้มีไว้เพื่อใช้เช่นนี้
NecipAllef

3
@AhmadFadli ปัญหาที่นี่คือการรับบริบทที่เหมาะสม (แม่) สำหรับการสื่อสารในระหว่างชิ้นส่วน หากคุณจะใช้ส่วนย่อยของการโต้ตอบเป็นลูกของกิจกรรมก็ไม่ควรมีความสับสน FragmentManager ของกิจกรรมและ getActivity () เพื่อดึงข้อมูลการโทรกลับก็เพียงพอแล้ว
Oguz Ozcan

1
นี่ควรเป็นคำตอบที่ยอมรับได้ มันอธิบายอย่างชัดเจนและมีรายละเอียดไม่ใช่แค่โยนรหัสไปที่ผู้คน
Vince

1
@ lukecross ParentFragment เป็นแฟรกเมนต์ที่สร้าง DialogFragment (อันที่เรียก show ()) แต่ดูเหมือนว่า childFragmentManager จะไม่รอดการกำหนดค่าใหม่ / การหมุนหน้าจอ ...
iwat0qs

34

ฉันทำตามขั้นตอนง่าย ๆ นี้เพื่อทำสิ่งนี้

  1. สร้างอินเตอร์เฟซที่ชอบด้วยวิธีการบางอย่างเช่นDialogFragmentCallbackInterface callBackMethod(Object data)ซึ่งคุณจะโทรติดต่อเพื่อส่งข้อมูล
  2. ตอนนี้คุณสามารถใช้DialogFragmentCallbackInterfaceส่วนต่อประสานในส่วนของคุณเช่นMyFragment implements DialogFragmentCallbackInterface
  3. เมื่อมีDialogFragmentการสร้างให้ตั้งค่าแฟรกเมนต์ที่คุณเรียกใช้MyFragmentเป็นแฟรกเมนต์เป้าหมายที่สร้างการDialogFragmentใช้งานmyDialogFragment.setTargetFragment(this, 0)ตรวจสอบsetTargetFragment (แฟรกเมนต์แฟรกเมนต์, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. รับวัตถุชิ้นส่วนเป้าหมายของคุณเป็นของคุณDialogFragmentโดยการโทรgetTargetFragment()และส่งไปยังDialogFragmentCallbackInterfaceตอนนี้คุณสามารถใช้อินเทอร์เฟซนี้เพื่อส่งข้อมูลไปยังส่วนของคุณ

    DialogFragmentCallbackInterface callback = 
               (DialogFragmentCallbackInterface) getTargetFragment();
    callback.callBackMethod(Object data);

    เสร็จแล้ว! เพียงให้แน่ใจว่าคุณใช้อินเทอร์เฟซนี้ในส่วนของคุณ


4
นี่ควรเป็นคำตอบที่ดีที่สุด คำตอบที่ดี
Md. Sajedul Karim

ตรวจสอบให้แน่ใจว่าใช้ Fragment manager เดียวกันสำหรับทั้งซอร์สและปลายทางมิฉะนั้น getTargetFragment จะไม่ทำงาน ดังนั้นหากคุณใช้ childFragmentManager มันจะไม่ทำงานเนื่องจากชิ้นส่วนต้นทางไม่ได้ถูกกำหนดโดยตัวจัดการส่วนย่อยของเด็ก เป็นการดีที่สุดที่จะคิดว่า 2 ชิ้นส่วนเหล่านี้เป็นชิ้นส่วนพี่น้องแทนที่จะเป็นชิ้นส่วนแม่ / ลูก
Thupten

ตรงไปตรงมามันจะดีกว่าที่จะใช้รูปแบบชิ้นส่วนเป้าหมายเท่านั้นเมื่อการสื่อสารระหว่าง 2 ส่วนพี่น้อง เมื่อไม่มีผู้ฟังคุณจะหลีกเลี่ยงการรั่วไหลของส่วนที่ 1 ในส่วนที่ 2 โดยไม่ได้ตั้งใจ เมื่อใช้ชิ้นส่วนเป้าหมายอย่าใช้ผู้ฟัง / ติดต่อกลับ ใช้onActivityResult(request code, resultcode, intent)เพื่อส่งคืนผลลัพธ์ไปที่แฟรกเมนต์ 1 เท่านั้น จาก fragment1, setTargetFragment()และจาก fragment2 getTargetFragment()ใช้ เมื่อใช้แฟรกเมนต์ parent / child เป็นแฟรกเมนต์หรือกิจกรรมเป็นแฟรกเมนต์คุณสามารถใช้ listener หรือ callback เนื่องจากไม่มีอันตรายจากการรั่วไหลของพาเรนต์ใน fragment child
Thupten

@ ขอบคุณเมื่อคุณพูดว่า "การรั่วไหล" คุณหมายถึงการรั่วไหลของหน่วยความจำหรือ "รายละเอียดการดำเนินการรั่วไหล"? ฉันไม่คิดว่าคำตอบของ Vijay จะทำให้หน่วยความจำรั่วไหลเกินกว่าที่จะใช้ onActivityResulty เพื่อรับค่าตอบแทน ทั้งสองรูปแบบจะอ้างอิงถึงชิ้นส่วนเป้าหมาย หากคุณหมายถึงการรั่วไหลของรายละเอียดการดำเนินการแล้วฉันคิดว่ารูปแบบของเขาจะดีกว่า onActivityResult วิธีการโทรกลับมีความชัดเจน (หากตั้งชื่อถูกต้อง) หากสิ่งที่คุณได้รับกลับมาเป็นปกติและถูกยกเลิกชิ้นส่วนแรกจะต้องตีความความหมายเหล่านั้น
tir38

34

อาจจะช้าไปหน่อย แต่อาจช่วยคนอื่นด้วยคำถามเดียวกันกับที่ฉันทำ

คุณสามารถใช้setTargetFragmentในDialogก่อนที่จะแสดงและในกล่องโต้ตอบที่คุณสามารถเรียกgetTargetFragmentได้รับการอ้างอิง


นี่คือคำตอบสำหรับคำถามอื่น แต่ยังใช้กับคำถามของคุณและเป็นวิธีแก้ปัญหาที่สะอาด: stackoverflow.com/questions/28620026/…
2288580

IllegalStateException to me
คข้าม

19

สื่อสารกับเศษอื่น ๆคู่มือบอกว่าเศษควรสื่อสารผ่านกิจกรรมที่เกี่ยวข้อง

บ่อยครั้งที่คุณต้องการให้ชิ้นส่วนหนึ่งสื่อสารกับอีกชิ้นหนึ่งตัวอย่างเช่นเปลี่ยนเนื้อหาตามกิจกรรมของผู้ใช้ การสื่อสาร Fragment-to-Fragment ทั้งหมดดำเนินการผ่านกิจกรรมที่เกี่ยวข้อง สองแฟรกเมนต์ไม่ควรสื่อสารโดยตรง


1
สิ่งที่เกี่ยวกับชิ้นส่วนภายในเช่นวิธีการที่ควรส่วนภายในส่วนอื่นสื่อสารกับส่วนโฮสต์
ราวี

@Ravi: แต่ละส่วนควรสื่อสารกับกิจกรรมที่เป็นเรื่องธรรมดาที่ทุกชิ้นส่วนโดยการเรียกgetActivity ()
Edward Brey

1
@Chris: หากแฟรกเมนต์ต้องการการสื่อสารอย่างต่อเนื่องให้กำหนดอินเตอร์เฟสสำหรับแต่ละแฟรกเมนต์ที่เหมาะสมที่จะนำไปใช้ จากนั้นงานของกิจกรรมนั้น จำกัด อยู่ที่การให้แฟรกเมนต์ที่มีตัวชี้ส่วนต่อประสานกับแฟรกเมนต์คู่กัน หลังจากนั้นแฟรกเมนต์สามารถสื่อสาร "โดยตรง" ได้อย่างปลอดภัยผ่านอินเตอร์เฟส
Edward Brey

3
ฉันคิดว่าเมื่อมีการใช้งานแฟรกเมนต์เพิ่มขึ้นความคิดดั้งเดิมที่ไม่ได้ใช้การแยกส่วนการสื่อสารโดยตรงแตกหัก เช่นในลิ้นชักการนำทางแต่ละส่วนย่อยของเด็กที่เกิดขึ้นทันทีของกิจกรรมจะทำหน้าที่เป็นกิจกรรมอย่างคร่าวๆ ดังนั้นการมีแฟรกเมนต์เช่นการสื่อสารระหว่างข้อความโต้ตอบผ่านกิจกรรมเป็นอันตรายต่อ IMO ที่สามารถอ่านได้ / ความยืดหยุ่น ในความเป็นจริงดูเหมือนจะไม่มีวิธีที่ดีในการห่อหุ้มกล่องโต้ตอบเพื่อให้มันทำงานกับทั้งกิจกรรมและชิ้นส่วนในวิธีที่นำมาใช้ซ้ำได้
Sam

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

12

คุณควรกำหนดinterfaceในคลาสแฟรกเมนต์ของคุณและใช้อินเตอร์เฟสนั้นในกิจกรรมพาเรนต์ โดยมีรายละเอียดแสดงไว้ที่นี่http://developer.android.com/guide/components/fragments.html#EventCallbacks รหัสจะมีลักษณะคล้ายกับ:

Fragment:

public static class FragmentA extends DialogFragment {

    OnArticleSelectedListener mListener;

    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
}

กิจกรรม:

public class MyActivity extends Activity implements OnArticleSelectedListener{

    ...
    @Override
    public void onArticleSelected(Uri articleUri){

    }
    ...
}

1
ฉันคิดว่าคุณขาดไขมันเอกสารเร็วเกินไป ทั้งสองส่วนของรหัสเหล่านี้เป็นFragmentAและเขากำลังสมมติว่ากิจกรรมนั้นOnArticleSelectedListenerไม่ใช่ส่วนที่เริ่มต้นเขา
eternalmatt

2
ฉันจะพิจารณาสิ่งที่คุณพยายามทำในทางที่ผิด แนวทางของ Android แนะนำให้ใช้การสื่อสารแบบแฟรกเมนต์ต่อชิ้นส่วนทั้งหมดที่เกิดขึ้นผ่านกิจกรรม (ต่อdeveloper.android.com/training/basics/fragments/ ...... ) หากคุณต้องการให้ทุกอย่างได้รับการจัดการภายในMyFragmentคุณอาจต้องการเปลี่ยนไปใช้แบบปกติAlertDialog
James McCracken

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

ฉันนำสิ่งนี้ไปใช้สำหรับกิจกรรมของฉัน คำถาม: สามารถขยายวิธีแก้ปัญหานี้ได้อย่างไรเพื่อให้ชิ้นส่วนสามารถโต้ตอบกับกล่องโต้ตอบ
บิลโมทย์

1
นี่เป็นแนวปฏิบัติที่ดีจากมุมมองด้านสถาปัตยกรรมและควรเป็นคำตอบที่ยอมรับได้ การใช้ onActivityResult นำไปสู่สถาปัตยกรรมสปาเก็ตตี้
Bruno Carrier

4

วิธีที่ถูกต้องของการตั้งค่าฟังให้ส่วนคือการตั้งค่ามันเมื่อมันถูกแนบมา ปัญหาที่ฉันมีคือ onAttachFragment () ไม่เคยถูกเรียก หลังจากการตรวจสอบฉันรู้ว่าฉันใช้getFragmentManagerแทนgetChildFragmentManager

นี่คือวิธีที่ฉันทำ:

MyDialogFragment dialogFragment = MyDialogFragment.newInstance("title", "body");
dialogFragment.show(getChildFragmentManager(), "SOME_DIALOG");

แนบใน onAttachFragment:

@Override
public void onAttachFragment(Fragment childFragment) {
    super.onAttachFragment(childFragment);

    if (childFragment instanceof MyDialogFragment) {
        MyDialogFragment dialog = (MyDialogFragment) childFragment;
        dialog.setListener(new MyDialogFragment.Listener() {
            @Override
            public void buttonClicked() {

            }
        });
    }
}

3

ตามเอกสารอย่างเป็นทางการ:

Fragment # setTargetFragment

เป้าหมายเพิ่มเติมสำหรับส่วนนี้ สิ่งนี้อาจถูกใช้ตัวอย่างเช่นถ้าแฟรกเมนต์นี้เริ่มต้นขึ้นโดยผู้อื่นและเมื่อเสร็จแล้วต้องการให้ผลลัพธ์กลับไปเป็นอันดับแรก ชุดเป้าหมายที่นี่จะถูกเก็บไว้ในอินสแตนซ์ผ่าน FragmentManager # putFragment

Fragment # getTargetFragment

ส่งคืนชิ้นส่วนเป้าหมายที่กำหนดโดย setTargetFragment (Fragment, int)

ดังนั้นคุณสามารถทำสิ่งนี้:

// In your fragment

public class MyFragment extends Fragment implements OnClickListener {
    private void showDialog() {
        DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
        // Add this
        dialogFrag.setTargetFragment(this, 0);
        dialogFrag.show(getFragmentManager, null);
    }
    ...
}

// then

public class MyialogFragment extends DialogFragment {
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Then get it
        Fragment fragment = getTargetFragment();
        if (fragment instanceof OnClickListener) {
            listener = (OnClickListener) fragment;
        } else {
            throw new RuntimeException("you must implement OnClickListener");
        }
    }
    ...
}

คุณอธิบายได้ไหม
Yilmaz

ในกรณีนี้เราต้องผ่านการอ้างอิง "MyFragment" กับ "MyialogFragment" และ "Fragment" ให้วิธีการที่จะทำ ฉันเพิ่มคำอธิบายของเอกสารอย่างเป็นทางการแล้วมันควรพูดให้ชัดเจนกว่าที่ฉันทำ
SUPERYAO

2

ฉันกำลังเผชิญกับปัญหาที่คล้ายกัน ทางออกที่ฉันค้นพบคือ:

  1. ประกาศอินเทอร์เฟซใน DialogFragment เช่นเดียวกับ James McCracken ที่อธิบายไว้ข้างต้น

  2. ใช้อินเทอร์เฟซในกิจกรรมของคุณ (ไม่ใช่ส่วน! นั่นไม่ใช่วิธีปฏิบัติที่ดี)

  3. จากเมธอด callback ในกิจกรรมของคุณให้เรียกฟังก์ชันพับลิกที่ต้องการในแฟรกเมนต์ของคุณซึ่งทำงานที่คุณต้องการทำ

ดังนั้นมันจะกลายเป็นกระบวนการสองขั้นตอน: DialogFragment -> Activity และ Activity -> Fragment


1

ฉันได้รับผลลัพธ์จาก Fragment DashboardLiveWall (call fragment) จาก Fragment LiveWallFilterFragment (กำลังรับแฟรกเมนต์) เช่นนี้ ...

 LiveWallFilterFragment filterFragment = LiveWallFilterFragment.newInstance(DashboardLiveWall.this ,"");

 getActivity().getSupportFragmentManager().beginTransaction(). 
 add(R.id.frame_container, filterFragment).addToBackStack("").commit();

ที่ไหน

public static LiveWallFilterFragment newInstance(Fragment targetFragment,String anyDummyData) {
        LiveWallFilterFragment fragment = new LiveWallFilterFragment();
        Bundle args = new Bundle();
        args.putString("dummyKey",anyDummyData);
        fragment.setArguments(args);

        if(targetFragment != null)
            fragment.setTargetFragment(targetFragment, KeyConst.LIVE_WALL_FILTER_RESULT);
        return fragment;
    }

setResult กลับสู่การเรียกแฟรกเมนต์ที่ต้องการ

private void setResult(boolean flag) {
        if (getTargetFragment() != null) {
            Bundle bundle = new Bundle();
            bundle.putBoolean("isWorkDone", flag);
            Intent mIntent = new Intent();
            mIntent.putExtras(bundle);
            getTargetFragment().onActivityResult(getTargetRequestCode(),
                    Activity.RESULT_OK, mIntent);
        }
    }

onActivityResult

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == KeyConst.LIVE_WALL_FILTER_RESULT) {

                Bundle bundle = data.getExtras();
                if (bundle != null) {

                    boolean isReset = bundle.getBoolean("isWorkDone");
                    if (isReset) {

                    } else {
                    }
                }
            }
        }
    }

1

Updated:

ฉันทำห้องสมุดตามรหัสสรุปสาระสำคัญของฉันที่สร้างหล่อเหล่านั้นสำหรับคุณโดยใช้และ@CallbackFragment@Callback

https://github.com/zeroarst/callbackfragment

และตัวอย่างให้ตัวอย่างที่ส่งการเรียกกลับจากแฟรกเมนต์ไปยังแฟรกเมนต์อื่น

คำตอบเก่า:

ฉันทำและคำอธิบายประกอบBaseCallbackFragment @FragmentCallbackขณะนี้มันขยายFragmentคุณสามารถเปลี่ยนเป็นDialogFragmentและจะทำงาน ตรวจสอบการใช้งานตามลำดับต่อไปนี้: getTargetFragment ()> getParentFragment ()> บริบท (กิจกรรม)

จากนั้นคุณเพียงแค่ขยายและประกาศอินเทอร์เฟซของคุณในแฟรกเมนต์ของคุณและให้คำอธิบายประกอบและส่วนฐานจะทำส่วนที่เหลือ คำอธิบายประกอบยังมีพารามิเตอร์mandatoryให้คุณพิจารณาว่าคุณต้องการบังคับให้แฟรกเมนต์นำการโทรกลับไปใช้หรือไม่

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

    @FragmentCallback
    public interface FragmentInteractionListener {
        void onEcho(EchoFragment fragment, String echo);
    }
}

https://gist.github.com/zeroarst/3b3f32092d58698a4568cdb0919c9a93


1

พวก Kotlin พวกเราไปกันแล้ว!

ดังนั้นปัญหาที่เรามีคือการที่เราได้สร้างกิจกรรมMainActivityในกิจกรรมที่เราสร้างขึ้นส่วนหนึ่งFragmentAและตอนนี้เราต้องการที่จะสร้างส่วนโต้ตอบด้านบนของเรียกมันว่าFragmentA FragmentBเราจะได้ผลลัพธ์จากFragmentBกลับไปFragmentAโดยไม่ผ่านได้MainActivityอย่างไร

บันทึก:

  1. FragmentAMainActivityเป็นชิ้นส่วนของเด็ก ในการจัดการเศษที่สร้างในFragmentAเราจะใช้childFragmentManagerซึ่งทำเช่นนั้น!
  2. FragmentAเป็นส่วนแม่ของFragmentBเพื่อการเข้าถึงFragmentAจากภายในเราจะใช้FragmentBparenFragment

ต้องบอกว่าภายในFragmentA,

class FragmentA : Fragment(), UpdateNameListener {
    override fun onSave(name: String) {
        toast("Running save with $name")
    }

    // call this function somewhere in a clickListener perhaps
    private fun startUpdateNameDialog() {
        FragmentB().show(childFragmentManager, "started name dialog")
    }
}

FragmentBนี่คือส่วนโต้ตอบ

class FragmentB : DialogFragment() {

    private lateinit var listener: UpdateNameListener

    override fun onAttach(context: Context) {
        super.onAttach(context)
        try {
            listener = parentFragment as UpdateNameListener
        } catch (e: ClassCastException) {
            throw ClassCastException("$context must implement UpdateNameListener")
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            val binding = UpdateNameDialogFragmentBinding.inflate(LayoutInflater.from(context))
            binding.btnSave.setOnClickListener {
                val name = binding.name.text.toString()
                listener.onSave(name)
                dismiss()
            }
            builder.setView(binding.root)
            return builder.create()
        } ?: throw IllegalStateException("Activity can not be null")
    }
}

นี่คืออินเตอร์เฟสที่เชื่อมโยงทั้งสอง

interface UpdateNameListener {
    fun onSave(name: String)
}

แค่นั้นแหละ.


1
ฉันติดตามเอกสารนี้: developer.android.com/guide/topics/ui/dialogsและมันไม่ทำงาน ขอบคุณมาก. ฉันหวังว่าสิ่งที่พ่อแม่ได้ผลอย่างที่คาดไว้ทุกครั้ง :)
UmutTekin

1
อย่าลืมตั้งค่าฟังให้เป็นโมฆะภายใน onDetach :)
BekaBot

@BekaBot ขอบคุณสำหรับความคิดเห็น ฉันได้ทำการวิจัยแล้วและดูเหมือนว่าไม่จำเป็นต้องปิดผู้ฟัง stackoverflow.com/a/37031951/10030693
Ssenyonjo

0

ฉันแก้ไขมันด้วยวิธีที่สวยงามด้วย RxAndroid รับผู้สังเกตการณ์ใน Constructor ของ DialogFragment และเลือกที่จะสังเกตได้และผลักดันค่าเมื่อมีการโทรกลับ จากนั้นใน Fragment ของคุณสร้างคลาสภายในของ Observer สร้างอินสแตนซ์และส่งผ่านมันใน Constructor ของ DialogFragment ฉันใช้ WeakReference ในผู้สังเกตการณ์เพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ นี่คือรหัส:

BaseDialogFragment.java

import java.lang.ref.WeakReference;

import io.reactivex.Observer;

public class BaseDialogFragment<O> extends DialogFragment {

    protected WeakReference<Observer<O>> observerRef;

    protected BaseDialogFragment(Observer<O> observer) {
        this.observerRef = new WeakReference<>(observer);
   }

    protected Observer<O> getObserver() {
    return observerRef.get();
    }
}

DatePickerFragment.java

public class DatePickerFragment extends BaseDialogFragment<Integer>
    implements DatePickerDialog.OnDateSetListener {


public DatePickerFragment(Observer<Integer> observer) {
    super(observer);
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    // Use the current date as the default date in the picker
    final Calendar c = Calendar.getInstance();
    int year = c.get(Calendar.YEAR);
    int month = c.get(Calendar.MONTH);
    int day = c.get(Calendar.DAY_OF_MONTH);

    // Create a new instance of DatePickerDialog and return it
    return new DatePickerDialog(getActivity(), this, year, month, day);
}

@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
        if (getObserver() != null) {
            Observable.just(month).subscribe(getObserver());
        }
    }
}

MyFragment.java

//Show the dialog fragment when the button is clicked
@OnClick(R.id.btn_date)
void onDateClick() {
    DialogFragment newFragment = new DatePickerFragment(new OnDateSelectedObserver());
    newFragment.show(getFragmentManager(), "datePicker");
}
 //Observer inner class
 private class OnDateSelectedObserver implements Observer<Integer> {

    @Override
    public void onSubscribe(Disposable d) {

    }

    @Override
    public void onNext(Integer integer) {
       //Here you invoke the logic

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onComplete() {

    }
}

คุณสามารถดูซอร์สโค้ดได้ที่นี่: https://github.com/andresuarezz26/carpoolingapp


1
สิ่งที่ตลกเกี่ยวกับ Android คือมีสิ่งที่เรียกว่าวงจรชีวิต ส่วนฐานหรือส่วนโต้ตอบจำเป็นต้องสามารถรักษาสถานะ (และการเชื่อมต่อ) ผ่านเหตุการณ์วงจรชีวิต การโทรกลับหรือผู้สังเกตการณ์ไม่สามารถต่อเนื่องกันได้ดังนั้นจึงมีปัญหาเดียวกันที่นี่
GDanger
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.