Gọi lại cho một đoạn từ một DialogFragment


159

Câu hỏi: Làm thế nào để người ta tạo một cuộc gọi lại từ DialogFragment sang một đoạn khác. Trong trường hợp của tôi, Hoạt động liên quan nên hoàn toàn không biết về DialogFragment.

Hãy xem xét tôi có

public class MyFragment extends Fragment implements OnClickListener

Sau đó, tại một số điểm tôi có thể làm

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

MyDialogFragment trông như thế nào

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

Nhưng không có gì đảm bảo rằng người nghe sẽ ở xung quanh nếu DialogFragment tạm dừng và tiếp tục trong vòng đời của nó. Các đảm bảo duy nhất trong một mảnh là những thứ được truyền qua Bundle thông qua setArgument và getArgument.

Có một cách để tham chiếu hoạt động nếu nó phải là người nghe:

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

Nhưng tôi không muốn Activity lắng nghe các sự kiện, tôi cần một mảnh vỡ. Thực sự, nó có thể là bất kỳ đối tượng Java nào thực hiện OnClickListener.

Hãy xem xét ví dụ cụ thể về một Đoạn trình bày AlertDialog thông qua DialogFragment. Nó có các nút Có / Không. Làm thế nào tôi có thể gửi các nút bấm này trở lại Fragment đã tạo ra nó?


Bạn đã đề cập "Nhưng không có gì đảm bảo rằng người nghe sẽ ở xung quanh nếu DialogFragment tạm dừng và tiếp tục trong vòng đời của nó." Tôi nghĩ trạng thái Fragment bị phá hủy trong onDestroy ()? Bạn phải đúng, nhưng tôi chỉ hơi bối rối khi sử dụng trạng thái Fragment bây giờ. Làm thế nào để tôi tái tạo vấn đề bạn đề cập, người nghe không ở xung quanh?
Sean

Tôi không thấy lý do tại sao bạn không thể đơn giản sử dụng OnClickListener listener = (OnClickListener) getParentFragment();trong DialogFragment và Fragment chính của bạn triển khai giao diện như ban đầu.
kiruwka 6/2/2015

Dưới đây là câu trả lời cho một câu hỏi không liên quan nhưng nó cho bạn thấy cách thực hiện theo cách sạch sẽ stackoverflow.com/questions/28620026/
mẹo

Câu trả lời:


190

Hoạt động liên quan là hoàn toàn không biết về DialogFragment.

Lớp phân mảnh:

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

}

Lớp 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
Tôi nghĩ chìa khóa ở đây là setTargetFragmentgetTargetFragment. Việc sử dụng onActivityResultlà một chút không rõ ràng. Có lẽ sẽ tốt hơn nếu khai báo phương thức cụ thể của riêng bạn trong trình gọi Fragment và sử dụng phương thức đó, thay vì sử dụng lại trênActivityResult. Nhưng tất cả ngữ nghĩa của nó tại thời điểm đó.
everalmatt

2
biến cấp độ ngăn xếp không được sử dụng?
Tên hiển thị

6
điều này sẽ tồn tại một thay đổi cấu hình-xoay?
Maxrunner

3
Đã sử dụng cái này. Lưu ý: mức độ ngăn xếp là không cần thiết để tồn tại luân chuyển hoặc ngủ. Thay vì onActivityResult, đoạn của tôi thực hiện DialogResultHandler # handleDialogResult (một giao diện tôi đã tạo). @myCode, sẽ rất hữu ích để hiển thị một hộp thoại đã chọn giá trị được thêm vào Ý định, sau đó đọc bên trong onActivityResult của bạn. Ý định không rõ ràng cho người mới bắt đầu.
Chris Betti

7
@eternalmatt, sự phản đối của bạn là hoàn toàn hợp lý, nhưng tôi nghĩ giá trị của onActivityResult () là nó được đảm bảo tồn tại trên bất kỳ Fragment nào, vì vậy bất kỳ Fragment nào cũng có thể được sử dụng làm cha mẹ. Nếu bạn tạo giao diện của riêng mình và có phần cha mẹ thực hiện nó, thì đứa trẻ chỉ có thể được sử dụng với cha mẹ thực hiện giao diện đó. Kết nối trẻ với giao diện đó có thể quay lại ám ảnh bạn nếu bạn bắt đầu sử dụng trẻ rộng rãi hơn sau này. Sử dụng giao diện "tích hợp" onActivityResult () không yêu cầu thêm khớp nối, vì vậy nó cho phép bạn linh hoạt hơn một chút.
Dalbergia

78

Giải pháp TargetFragment dường như không phải là lựa chọn tốt nhất cho các đoạn hộp thoại bởi vì nó có thể tạo IllegalStateExceptionsau khi ứng dụng bị phá hủy và được tạo lại. Trong trường hợp FragmentManagernày, không thể tìm thấy đoạn đích và bạn sẽ nhận được IllegalStateExceptionmột tin nhắn như thế này:

"Đoạn không còn tồn tại cho khóa android: target_state: index 1"

Có vẻ như Fragment#setTargetFragment()nó không có nghĩa là để giao tiếp giữa một đứa trẻ và cha mẹ Fragment, mà là để giao tiếp giữa anh chị em-Fragment.

Vì vậy, cách khác là tạo các đoạn hộp thoại như thế này bằng cách sử dụng ChildFragmentManagerđoạn cha mẹ, thay vì sử dụng các hoạt động FragmentManager:

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

Và bằng cách sử dụng Giao diện, theo onCreatephương thức, DialogFragmentbạn có thể nhận được đoạn gốc:

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

Điều duy nhất còn lại là gọi phương thức gọi lại của bạn sau các bước này.

Để biết thêm thông tin về vấn đề này, bạn có thể xem liên kết: https://code.google.com.vn/p/android/issues/detail?id=54520


2
Điều này cũng hoạt động với onAttach (Bối cảnh bối cảnh) được giới thiệu thêm vào api 23.
Santa Teclado

1
Đây phải là câu trả lời được chấp nhận. Câu trả lời được chấp nhận hiện tại là lỗi và các đoạn không có nghĩa là được sử dụng như thế này.
NecipAllef

3
@AhmadFadli Vấn đề ở đây là có được bối cảnh (phụ huynh) phù hợp để liên lạc giữa các đoạn. Nếu bạn sẽ sử dụng đoạn hội thoại như một đứa trẻ của hoạt động, thì không nên có bất kỳ sự nhầm lẫn nào. FragmentManager của Activity và getActivity () để truy xuất gọi lại là đủ.
Oguz Ozcan

1
Đây phải là câu trả lời được chấp nhận. Nó được giải thích rõ ràng và chi tiết, nó không chỉ là mã ném vào người.
Vince

1
@lukecross ParentFragment là đoạn tạo ra các DialogFragment (một trong đó cuộc gọi hiển thị ()) Nhưng có vẻ rằng childFragmentManager không tồn tại phép quay cấu hình lại / màn hình ...
iwat0qs

34

Tôi đã làm theo các bước đơn giản để làm công cụ này.

  1. Tạo giao diện như DialogFragmentCallbackInterfacevới một số phương pháp như callBackMethod(Object data). Mà bạn sẽ gọi để truyền dữ liệu.
  2. Bây giờ bạn có thể thực hiện DialogFragmentCallbackInterfacegiao diện trong đoạn của bạn nhưMyFragment implements DialogFragmentCallbackInterface
  3. Tại thời điểm DialogFragmenttạo, đặt đoạn gọi của bạn MyFragmentlà đoạn đích đã tạo, DialogFragmentsử dụng myDialogFragment.setTargetFragment(this, 0)kiểm tra setTargetFragment (Đoạn phân đoạn, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
    
  4. Đưa đối tượng phân đoạn mục tiêu của bạn vào DialogFragmentbằng cách gọi getTargetFragment()và chuyển nó tới DialogFragmentCallbackInterface. Bây giờ bạn có thể sử dụng giao diện này để gửi dữ liệu đến đoạn của mình.

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

    Thế là xong! chỉ cần chắc chắn rằng bạn đã thực hiện giao diện này trong đoạn của bạn.


4
Điều này nên được trả lời tốt nhất. Câu trả lời chính xác.
Md. Sajedul Karim

Ngoài ra, hãy đảm bảo sử dụng cùng một trình quản lý Fragment cho cả các đoạn nguồn và đoạn đích, nếu không getTargetFragment sẽ không hoạt động. Vì vậy, nếu bạn đang sử dụng childFragmentManager, nó sẽ không hoạt động do đoạn mã nguồn không được trình quản lý phân đoạn con cam kết. Tốt nhất nên nghĩ về 2 mảnh này là những mảnh anh chị em chứ không phải là mảnh vỡ cha mẹ / con cái.
Thupten

Thành thật mà nói, tốt hơn là chỉ sử dụng mô hình mảnh mục tiêu khi giao tiếp giữa 2 mảnh anh chị em. Bằng cách không có người nghe, bạn tránh vô tình làm rò rỉ đoạn1 trong đoạn2. Khi sử dụng đoạn đích, không sử dụng trình nghe / gọi lại. Chỉ sử dụng onActivityResult(request code, resultcode, intent)để trả về kết quả cho đoạn1. Từ đoạn1 setTargetFragment()và từ đoạn2, sử dụng getTargetFragment(). Khi sử dụng phân đoạn cha / con để phân đoạn hoặc hoạt động thành phân đoạn, bạn có thể sử dụng trình nghe hoặc gọi lại vì không có nguy cơ rò rỉ cha mẹ trong đoạn con.
Thupten

@Thupten khi bạn nói "rò rỉ", bạn có nghĩa là rò rỉ bộ nhớ hoặc "chi tiết thực hiện rò rỉ"? Tôi không nghĩ câu trả lời của Vijay sẽ làm rò rỉ bộ nhớ nhiều hơn là sử dụng onActivityResemony cho giá trị trả về. Cả hai mẫu sẽ giữ tham chiếu đến đoạn đích. Nếu bạn có nghĩa là rò rỉ chi tiết triển khai, thì tôi nghĩ rằng mô hình của anh ấy thậm chí còn tốt hơn onActivityResult. Phương thức gọi lại là rõ ràng (nếu được đặt tên chính xác). Nếu tất cả những gì bạn nhận lại là OK và bị hủy bỏ, đoạn đầu tiên phải diễn giải ý nghĩa của chúng.
tir38

34

Có thể hơi muộn, nhưng có thể giúp những người khác có cùng câu hỏi như tôi đã làm.

Bạn có thể sử dụng setTargetFragmenttrên Dialogtrước khi hiển thị, và trong hộp thoại, bạn có thể gọi getTargetFragmentđể có được những tài liệu tham khảo.


Đây là một câu trả lời cho một câu hỏi khác nhưng nó cũng áp dụng cho câu hỏi của bạn và là một giải pháp rõ ràng: stackoverflow.com/questions/28620026/ chủ
user2288580

IllegalStateException với tôi
luke chéo

19

Các giao tiếp với mảnh vỡ khác hướng dẫn cho biết mảnh vỡ nên giao tiếp thông qua các hoạt động liên quan .

Thường thì bạn sẽ muốn một Fragment giao tiếp với người khác, ví dụ để thay đổi nội dung dựa trên sự kiện của người dùng. Tất cả các giao tiếp Fragment-to-Fragment được thực hiện thông qua Hoạt động liên quan. Hai mảnh không bao giờ nên giao tiếp trực tiếp.


1
những gì về các mảnh bên trong, tức là một mảnh trong một mảnh khác sẽ giao tiếp với mảnh vỡ như thế nào
Ravi

@Ravi: Mỗi phân đoạn nên giao tiếp với hoạt động chung cho tất cả các phân đoạn bằng cách gọi getActivity () .
Edward Brey

1
@Chris: Nếu các đoạn cần liên lạc liên tục, hãy xác định giao diện cho từng đoạn thích hợp để thực hiện. Công việc của hoạt động sau đó được giới hạn trong việc cung cấp các đoạn có con trỏ giao diện cho các đoạn đối tác của chúng. Sau đó, các mảnh có thể giao tiếp "trực tiếp" một cách an toàn thông qua các giao diện.
Edward Brey

3
Tôi nghĩ rằng khi việc sử dụng các mảnh đã được mở rộng, ý tưởng ban đầu về việc không sử dụng giao tiếp mảnh trực tiếp bị phá vỡ. Ví dụ, trong ngăn kéo điều hướng, mỗi phần con ngay lập tức của hoạt động gần như đóng vai trò là một hoạt động. Vì vậy, việc có một đoạn như hộp thoại giao tiếp thông qua hoạt động gây hại cho khả năng đọc / linh hoạt IMO. Trong thực tế, dường như không có cách nào hay để đóng gói hộp thoại để cho phép nó hoạt động với cả hoạt động và các đoạn theo cách có thể sử dụng lại.
Sam

16
Tôi biết điều này đã cũ, nhưng trong trường hợp có ai khác đến đây tôi cảm thấy như trường hợp được nói đến trong tài liệu đó không áp dụng khi một đoạn "sở hữu" logic được sử dụng để xác định việc tạo và quản lý DialogFragment. Thật kỳ lạ khi tạo ra một loạt các kết nối từ phân đoạn đến hoạt động khi hoạt động thậm chí không chắc chắn tại sao Hộp thoại được tạo hoặc trong những điều kiện nào nên loại bỏ. Bên cạnh đó, DialogFragment cực kỳ đơn giản và chỉ tồn tại để thông báo cho người dùng và có khả năng nhận được phản hồi.
Chris

12

Bạn nên định nghĩa một interfacelớp trong đoạn của bạn và thực hiện giao diện đó trong hoạt động chính của nó. Các chi tiết được nêu ở đây http://developer.android.com/guide/components/fragments.html#EventCallbacks . Mã này sẽ trông giống như:

Miếng:

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

Hoạt động:

public class MyActivity extends Activity implements OnArticleSelectedListener{

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

    }
    ...
}

1
Tôi nghĩ rằng bạn đã lướt qua các tài liệu quá nhanh. Cả hai phân đoạn mã này đều được FragmentAvà anh ta giả định rằng một hoạt động là một OnArticleSelectedListener, không phải là Đoạn đã bắt đầu anh ta.
everalmatt

2
Tôi sẽ xem xét những gì bạn đang cố gắng thực hành xấu. Các nguyên tắc của Android khuyến nghị rằng tất cả các giao tiếp từ mảnh đến mảnh đều diễn ra thông qua hoạt động (theo nhà phát triển.android.com / training / basics / fragments / Lỗi ). Nếu bạn thực sự muốn tất cả được xử lý trong phạm vi MyFragmentbạn có thể muốn chuyển sang sử dụng thường xuyênAlertDialog
James McCracken

1
Tôi nghĩ rằng mối quan tâm với việc các đoạn nói chuyện trực tiếp với nhau là trong một số bố cục, không phải tất cả các mảnh có thể được tải và như trong ví dụ, có thể cần phải chuyển sang đoạn. Tôi không nghĩ rằng mối quan tâm đó là hợp lệ khi nói về việc khởi chạy một đoạn hội thoại từ một đoạn.

Tôi đã thực hiện điều này cho các hoạt động của tôi. Câu hỏi: giải pháp này có thể được mở rộng sao cho một đoạn có thể khởi tạo hộp thoại này không?
Bill Mote

1
Đây là một thực tiễn tốt từ góc độ kiến ​​trúc, và như vậy nên là câu trả lời được chấp nhận. Sử dụng onActivityResult dẫn đến kiến ​​trúc spaghetti
Bruno Carrier

4

Cách chính xác để đặt người nghe thành một đoạn là bằng cách đặt nó khi nó được đính kèm . Vấn đề tôi gặp phải là onAttachFragment () không bao giờ được gọi. Sau một số điều tra, tôi nhận ra rằng tôi đã sử dụng getFragmentManager thay vì getChildFragmentManager

Đây là cách tôi làm điều đó:

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

Đính kèm nó trong 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

Theo tài liệu chính thức:

Đoạn # setTargetFragment

Mục tiêu tùy chọn cho đoạn này. Điều này có thể được sử dụng, ví dụ, nếu đoạn này đang được bắt đầu bởi một đoạn khác và khi hoàn thành muốn đưa kết quả trở lại đầu tiên. Mục tiêu được đặt ở đây được giữ lại qua các phiên bản thông qua FragmentManager # putFragment.

Đoạn # getTargetFragment

Trả về đoạn đích được thiết lập bởi setTargetFragment (Fragment, int).

Vì vậy, bạn có thể làm điều này:

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

bạn có thể giải thích của bạn?
Yilmaz

Trong trường hợp này, chúng ta cần chuyển tham chiếu "MyFragment" sang "MyialogFragment" và "Fragment" cung cấp phương thức để thực hiện. Tôi đã thêm mô tả của tài liệu chính thức, nó sẽ nói rõ hơn tôi làm.
SUPERYAO

2

Tôi đã phải đối mặt với một vấn đề tương tự. Giải pháp mà tôi tìm ra là:

  1. Khai báo một giao diện trong DialogFragment của bạn giống như James McCracken đã giải thích ở trên.

  2. Thực hiện giao diện trong hoạt động của bạn (không phải là đoạn! Đó không phải là một thực hành tốt).

  3. Từ phương thức gọi lại trong hoạt động của bạn, hãy gọi một chức năng công cộng bắt buộc trong đoạn của bạn, công việc mà bạn muốn làm.

Do đó, nó trở thành một quy trình gồm hai bước: DialogFragment -> Activity và sau đó là Activity -> Fragment


1

Tôi đang nhận được kết quả với Bảng điều khiển mảnh vỡLiveWall (gọi đoạn) từ Fragment LiveWallFilterFragment (nhận đoạn) Như thế này ...

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

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

Ở đâu

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 quay lại gọi đoạn như

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

Cập nhật:

Tôi đã tạo một thư viện dựa trên mã chính của mình để tạo ra các bản đúc cho bạn bằng cách sử dụng @CallbackFragment@Callback.

https://github.com/zeroarst/callbackfragment .

Và ví dụ cho bạn ví dụ gửi một cuộc gọi lại từ một mảnh này sang một mảnh khác.

Câu trả lời cũ:

Tôi đã thực hiện một BaseCallbackFragmentvà chú thích @FragmentCallback. Nó hiện đang mở rộng Fragment, bạn có thể thay đổi nó DialogFragmentvà sẽ hoạt động. Nó kiểm tra việc triển khai theo thứ tự sau: getTargetFragment ()> getParentFragment ()> bối cảnh (hoạt động).

Sau đó, bạn chỉ cần mở rộng nó và khai báo các giao diện của bạn trong đoạn của bạn và đưa ra chú thích, và đoạn cơ sở sẽ làm phần còn lại. Chú thích cũng có một tham số mandatoryđể bạn xác định xem bạn có muốn buộc đoạn đó thực hiện cuộc gọi lại hay không.

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

Chúng tôi đi đây!

Vì vậy, vấn đề chúng ta gặp phải là chúng ta đã tạo ra một hoạt động, MainActivitytrên hoạt động đó chúng ta đã tạo ra một đoạn FragmentAvà bây giờ chúng ta muốn tạo một đoạn hộp thoại trên đầu trang của FragmentAFragmentB. Làm thế nào để chúng ta có được kết quả từ FragmentBtrở lại FragmentAmà không đi qua MainActivity?

Ghi chú:

  1. FragmentAlà một mảnh con của MainActivity. Để quản lý các mảnh được tạo trong FragmentAchúng tôi sẽ sử dụng childFragmentManagercái nào!
  2. FragmentAlà một phần cha mẹ của FragmentB, để truy cập FragmentAtừ bên trong FragmentBchúng ta sẽ sử dụng parenFragment.

Có nói rằng, bên trong 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")
    }
}

Đây là đoạn hội thoại 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")
    }
}

Đây là giao diện liên kết hai.

interface UpdateNameListener {
    fun onSave(name: String)
}

Đó là nó.


1
Tôi đã theo tài liệu này: developer.android.com/guide/topics/ui/dialogs và nó không hoạt động. Cảm ơn bạn rất nhiều. Tôi hy vọng điều cha mẹ này hoạt động như mong đợi mọi lúc :)
UmutTekin

1
đừng quên đặt người nghe thành null bên trong onDetach :)
BekaBot

@BekaBot Cảm ơn bạn đã bình luận. Tôi đã thực hiện một số nghiên cứu và có vẻ như không cần thiết phải đóng người nghe. stackoverflow.com/a/37031951/10030693
Ssenyonjo

0

Tôi đã giải quyết điều này một cách tao nhã với RxAndroid. Nhận một người quan sát trong hàm tạo của DialogFragment và có thể quan sát và đẩy giá trị khi gọi lại được gọi. Sau đó, trong Fragment của bạn tạo một lớp bên trong của Observer, tạo một thể hiện và truyền nó vào hàm tạo của DialogFragment. Tôi đã sử dụng WeakReference trong bộ quan sát để tránh rò rỉ bộ nhớ. Đây là mã:

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() {

    }
}

Bạn có thể xem mã nguồn tại đây: https://github.com/andresuarezz26/carpoolingapp


1
Điều thú vị về Android là có một thứ gọi là vòng đời. Đoạn cơ sở hoặc đoạn hộp thoại cần có khả năng duy trì trạng thái (và kết nối của chúng) trong các sự kiện vòng đời. Các cuộc gọi lại hoặc quan sát viên không thể được nối tiếp và do đó có cùng một vấn đề ở đây.
GDanger
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.