DialogFragment에서 조각으로 콜백


159

질문 : DialogFragment에서 다른 Fragment로 콜백을 만드는 방법은 무엇입니까? 필자의 경우 관련된 Activity는 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가 일시 중지되고 수명주기 동안 다시 시작되는 경우 리스너가있을 것이라는 보장은 없습니다. Fragment의 유일한 보증은 setArguments 및 getArguments를 통해 번들을 통과 한 것입니다.

리스너 여야하는 경우 활동을 참조하는 방법이 있습니다.

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

그러나 나는 Activity가 이벤트를 수신하기를 원하지 않습니다. Fragment가 필요합니다. 실제로 OnClickListener를 구현하는 Java 객체 일 수 있습니다.

DialogFragment를 통해 AlertDialog를 표시하는 Fragment의 구체적인 예를 고려하십시오. 예 / 아니요 버튼이 있습니다. 이 버튼 누름을 생성 한 조각으로 다시 보내려면 어떻게해야합니까?


"하지만 DialogFragment가 일시 중지되고 수명주기 동안 다시 시작되는 경우 리스너가있을 것이라는 보장은 없습니다." onDestroy () 중에 조각 상태가 파괴되었다고 생각 했습니까? 당신은 옳 아야하지만 지금은 조각 상태를 사용하는 방법이 약간 혼란 스럽습니다. 리스너가 주변에 있지 않은 문제를 어떻게 재현합니까?
Sean

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 호출자에서 자신의 특정 메소드를 선언하고 onActivityResult를 다시 사용하는 대신 해당 메소드를 사용하는 것이 좋습니다. 그러나 그 시점의 모든 의미론.
eternalmatt

2
스택 레벨 변수가 사용되지 않습니까?
표시 이름

6
이것이 구성 변경 회전에도 불구하고 있습니까?
Maxrunner

3
이것을 사용했습니다. 참고 : 회전 또는 절전 상태를 유지하기 위해 스택 수준이 필요하지 않았습니다. onActivityResult 대신 내 조각은 DialogResultHandler # handleDialogResult (내가 만든 인터페이스)를 구현합니다. @myCode는 Intent에 추가 된 대화 상자 선택 값을 표시 한 다음 onActivityResult 내부를 읽는 데 매우 유용합니다. 초보자에게는 의도가 불분명합니다.
Chris Betti

7
@eternalmatt, 귀하의 이의 제기는 전적으로 합리적이지만, onActivityResult ()의 가치는 그것이 모든 Fragment에 존재한다는 것을 보장하므로 모든 Fragment를 부모로 사용할 수 있다고 생각합니다. 자신 만의 인터페이스를 생성하고 부모 프래그먼트가 구현 한 경우 해당 인터페이스를 구현하는 부모 만 하위를 사용할 수 있습니다. 나중에 더 광범위하게 아이를 사용하기 시작하면 해당 인터페이스에 아이를 연결하면 귀찮게 될 수 있습니다. "내장"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
이것은 또한 api 23에 추가 된 onAttach (Context context)와 함께 작동합니다.
Santa Teclado

1
이 답변이 허용되어야합니다. 현재 허용되는 답변은 버그이며 조각은 이와 같이 사용되지 않습니다.
NecipAllef

3
@AhmadFadli 여기서 문제는 프래그먼트 간 통신에 적합한 컨텍스트 (부모)를 얻는 것입니다. 대화 조각을 활동의 자식으로 사용한다면 혼란이 없어야합니다. 콜백을 검색하기위한 Activity의 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 2016 년

또한 소스 및 대상 조각 모두에 대해 동일한 조각 관리자를 사용해야합니다. 그렇지 않으면 getTargetFragment가 작동하지 않습니다. 따라서 childFragmentManager를 사용하는 경우 소스 조각이 자식 조각 관리자에 의해 커밋되지 않으므로 작동하지 않습니다. 이 두 조각을 부모 / 자식 조각보다는 형제 조각으로 생각하는 것이 가장 좋습니다.
19시

솔직히, 2 개의 형제 조각간에 통신 할 때는 대상 조각 패턴 만 사용하는 것이 좋습니다. 리스너가 없어서 fragment2에서 fragment1이 실수로 누출되는 것을 피할 수 있습니다. 대상 조각을 사용할 때는 리스너 / 콜백을 사용하지 마십시오. 만 사용 onActivityResult(request code, resultcode, intent)fragment1에 결과를 반환 할 수 있습니다. fragment1 setTargetFragment()및 fragment2에서을 사용하십시오 getTargetFragment(). 부모 / 자식 조각을 조각화하거나 활동을 조각화하는 경우 자식 조각에서 부모가 유출 될 위험이 없으므로 리스너 또는 콜백을 사용할 수 있습니다.
9

@ 누가 "누설"이라고 말하면 메모리 누수 또는 "누수 구현 세부 사항"을 의미합니까? Vijay의 대답이 onActivityResulty를 반환 값으로 사용하는 것보다 더 많은 메모리가 누출 될 것이라고 생각하지 않습니다. 두 패턴 모두 대상 조각에 대한 참조를 유지합니다. 구현 세부 정보가 유출된다는 것을 의미한다면 그의 패턴이 onActivityResult보다 낫습니다. 콜백 메소드는 명시 적입니다 (올바르게 이름 지정된 경우). 돌아온 것이 모두 OK이고 CANCELED이면 첫 번째 조각은 그 의미를 해석해야합니다.
tir38

34

어쩌면 조금 늦었지만 같은 질문을 가진 다른 사람들을 도울 수 있습니다.

표시하기 전에 setTargetFragmenton 을 사용할 수 있으며 Dialog대화 상자 getTargetFragment에서 참조를 얻기 위해 호출 할 수 있습니다 .


여기에 또 다른 질문에 대한 대답은뿐만 아니라 깨끗한 솔루션을 귀하의 질문에 적용하고 있습니다 : stackoverflow.com/questions/28620026/...
user2288580

나에게 IllegalStateException
크로스

19

다른 조각과통신 안내서에 따르면 조각은 관련 활동을 통해 통신 해야합니다 .

예를 들어 한 조각이 다른 조각과 통신하기를 원할 수 있습니다 (예 : 사용자 이벤트를 기반으로 내용 변경). 모든 Fragment-to-Fragment 통신은 연관된 활동을 통해 수행됩니다. 두 개의 조각은 직접 통신해서는 안됩니다.


1
내부 조각은 어떻습니까? 즉, 다른 조각 내의 조각은 어떻게 호스트 조각과 통신해야합니까
Ravi

@Ravi : 각 조각은 getActivity () 를 호출하여 모든 조각에 공통적 인 활동과 통신해야합니다 .
Edward Brey

1
@Chris : 프래그먼트에 지속적인 통신이 필요한 경우 구현할 각 적절한 프래그먼트에 대한 인터페이스를 정의하십시오. 그런 다음 활동의 작업은 해당 조각에 대한 인터페이스 포인터가있는 조각을 제공하는 것으로 제한됩니다. 그 후, 프래그먼트는 인터페이스를 통해 "직접"안전하게 통신 할 수 있습니다.
Edward Brey

3
조각의 사용이 확장됨에 따라 직접 조각 통신을 사용하지 않는다는 원래 아이디어가 무너 졌다고 생각합니다. 예를 들어 내비게이션 드로어에서 활동의 각 직계 하위 단편은 대략 활동으로 작동합니다. 따라서 대화 상자 조각과 같은 조각이 있으면 활동을 통해 통신하여 가독성 / 유연성 IMO가 손상됩니다. 실제로 대화 조각을 캡슐화하여 재사용 가능한 방식으로 활동과 조각 모두에서 작동 할 수있는 좋은 방법은없는 것 같습니다.
Sam

16
나는 이것이 오래되었다는 것을 알고 있지만 다른 누군가가 여기에 온다면 그 문서에서 이야기 한 사례가 하나의 조각이 DialogFragment의 생성 및 관리를 결정하는 데 사용되는 논리를 "소유"할 때 적용되지 않는다고 생각합니다. 활동이 왜 대화 상자가 생성되는지 또는 어떤 조건에서 해제되어야하는지 확실하지 않을 때 조각에서 활동으로 많은 연결을 만드는 것은 이상합니다. 그 외에도 DialogFragment는 매우 간단하며 사용자에게 알리고 응답을 얻을 수 있도록 존재합니다.
Chris

12

interface프래그먼트 클래스에서를 정의하고 해당 부모 인터페이스에서 해당 인터페이스를 구현해야합니다. 자세한 내용은 http://developer.android.com/guide/components/fragments.html#EventCallbacks 에 나와 있습니다 . 코드는 다음과 유사합니다.

파편:

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
조각이 서로 직접 대화하는 것에 대한 우려는 일부 레이아웃에서는 모든 조각이로드되지 않을 수 있으며 예제에 표시된 것처럼 조각을 전환해야 할 수도 있다는 것입니다. 조각에서 대화 상자 조각을 시작하는 것에 대해 이야기 할 때 우려가 유효하다고 생각하지 않습니다.

나는 내 활동을 위해 이것을 구현했다. 질문 : 프래그먼트가이 대화 상자를 인스턴스화 할 수 있도록이 솔루션을 확장 할 수 있습니까?
Bill Mote

1
이것은 아키텍처 관점에서 좋은 방법이므로 허용되는 답변이어야합니다. 스파게티 아키텍처로 onActivityResult를 리드를 사용
브루노 캐리어

4

리스너를 프래그먼트로 설정하는 올바른 방법은 리스너를 첨부 할 때 설정하는 것입니다 . 내가 가진 문제는 onAttachFragment ()가 호출되지 않았다는 것입니다. 조사 후 getChildFragmentManager 대신 getFragmentManager 를 사용하고 있음을 깨달았습니다.

내가하는 방법은 다음과 같습니다.

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

공식 문서에 따르면 :

조각 #setTargetFragment

이 프래그먼트의 선택적 대상입니다. 예를 들어,이 프래그먼트가 다른 프래그먼트에 의해 시작되고 완료되면 결과를 처음으로 되돌리려 고 할 때 사용될 수 있습니다. 여기에 설정된 대상은 FragmentManager # putFragment를 통해 인스턴스 전체에 유지됩니다.

프래그먼트 #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. James McCracken이 위에서 설명한 것처럼 DialogFragment에서 인터페이스를 선언하십시오.

  2. 활동에서 인터페이스를 구현하십시오 (조각화가 아닙니다! 좋은 습관은 아닙니다).

  3. 활동의 콜백 메소드에서 수행하려는 작업을 수행하는 프래그먼트에서 필수 공용 함수를 호출하십시오.

따라서 DialogFragment-> Activity 및 Activity-> Fragment의 두 단계 프로세스가됩니다.


1

Fragment LiveWallFilterFragment (fragment 수신)에서 Fragment DashboardLiveWall (call fragment) 결과가 표시됩니다.

 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

업데이트 :

@CallbackFragmentand 를 사용하여 캐스팅을 생성하는 내 요지 코드를 기반으로 라이브러리를 만들었습니다 @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. FragmentA의 하위 조각입니다 MainActivity. 작성된 조각을 관리하기 위해 FragmentA우리는 그것을 사용 childFragmentManager합니다!
  2. FragmentA우리가 사용할 내부 FragmentB에서 액세스 할 수 있는 부모 조각입니다 .FragmentAFragmentBparenFragment

내부 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에서 리스너를 null로 설정하는 것을 잊지 마십시오 :)
BekaBot

@BekaBot 댓글 주셔서 감사합니다. 나는 약간의 연구를했으며 청취자를 닫을 필요가없는 것으로 보입니다. stackoverflow.com/a/37031951/10030693
Ssenyonjo

0

RxAndroid를 사용하여 이것을 우아한 방식으로 해결했습니다. DialogFragment의 생성자에서 옵저버를 수신하고 콜백이 호출 될 때 옵저버 블을 선언하고 값을 푸시하십시오. 그런 다음 Fragment에서 Observer의 내부 클래스를 만들고 인스턴스를 만들고 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
안드로이드에 대한 재미있는 점은 수명주기라는 것이 있다는 것입니다. 기본 조각 또는 대화 조각은 수명주기 이벤트 동안 상태 및 연결을 유지할 수 있어야합니다. 콜백 또는 옵저버는 직렬화 할 수 없으므로 여기서 동일한 문제가 발생합니다.
GDanger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.