DialogFragment에서 결과 받기


232

내가 사용하고 DialogFragments을 가지의 번호 : 목록에서 항목을 선택, 텍스트를 입력.

호출 활동 / 조각으로 값 (예 : 문자열 또는 목록의 항목)을 다시 반환하는 가장 좋은 방법은 무엇입니까?

현재 호출 활동을 구현 DismissListener하고 DialogFragment에 활동에 대한 참조를 제공하고 있습니다. 그런 다음 대화 상자 OnDimiss는 활동 에서 메소드 를 호출하고 활동은 DialogFragment 오브젝트의 결과를 가져옵니다. 매우 지저분하고 DialogFragment가 활동에 대한 참조를 잃기 때문에 구성 변경 (방향 변경)에서 작동하지 않습니다.

도움을 주셔서 감사합니다.


10
DialogFragments는 여전히 조각입니다. 실제로는 조각이 주요 활동과 대화하는 데 사용하는 것이 좋습니다. developer.android.com/guide/topics/fundamentals/…
코딩 사용자

1
고마워 나는 당신이 말한대로 매우 가깝습니다. 링크 된 문서가 도움이 된 비트는 onAttach ()를 사용하고 액티비티를 리스너에게 캐스팅하는 것이 었습니다.
James Cross

2
@codinguser는 @Styx는 - "를 DialogFragment에게 활동에 대한 참조를 제공"-이 상세하게하는 것은 조금 위험으로 모두 Activity와이 DialogFragment다시 될 수 있습니다. Activity전달 된 방법을 사용하는 onAttach(Activity activity)것이 적절하고 권장되는 방법입니다.
sstn

답변:


246

myDialogFragment.setTargetFragment(this, MY_REQUEST_CODE)대화 상자가 표시되는 곳에서 사용 하고 대화 상자가 끝나면 대화 상자에서을 호출 하고 포함하는 조각으로 getTargetFragment().onActivityResult(getTargetRequestCode(), ...)구현할 수 있습니다 onActivityResult().

onActivityResult()특히 활동을 포함하지 않기 때문에 의 악용으로 보입니다 . 그러나 나는 공식 구글 사람들이 추천하는 것을 보았고 아마도 API 데모에서도 볼 수있었습니다. 나는 그것이 g/setTargetFragment()추가 된 것 같아요 .


2
setTargetFragment는 요청 코드가 onActivityResult에서 사용된다고 언급 하므로이 접근법을 사용해도 괜찮습니다.
Giorgi

87
대상이 활동 인 경우 어떻게합니까?
페르난도 Gallego

9
대상이 활동이라면 "void onActivityResult2 (int requestCode, int resultCode, Intent data)"와 같은 메소드로 인터페이스를 선언하고 Activity로 구현합니다. DialogFragment에서 getActivity 만 사용 하고이 인터페이스를 확인하고 적절하게 호출하십시오.
Ruslan Yanchyshyn

4
좋은 해결책이 아닙니다. 대화 상자 조각 상태를 저장하고 복원 한 후에는 작동하지 않습니다. 이 경우 LocalBroadcastManager가 최상의 솔루션입니다.
Nik

4
@Nik 그것은 사실이 아닙니다. 최고의 솔루션입니다. 상태를 저장하고 복원 할 때 문제가 없습니다. 문제가 발생한 경우 잘못된 조각 관리자를 사용했습니다. 대상 조각 / 호출자는 getChildFragmentManager ()를 사용하여 대화 상자를 표시하지 않아도됩니다.
놀라운 1 월

140

여기에서 볼 수 있듯이 매우 간단한 방법이 있습니다.

귀하 DialogFragment의 인터페이스 리스너를 추가 하십시오 :

public interface EditNameDialogListener {
    void onFinishEditDialog(String inputText);
}

그런 다음 해당 리스너에 대한 참조를 추가하십시오.

private EditNameDialogListener listener;

리스너 메소드를 "활성화"하고 부모 활동 / 프래그먼트가이 인터페이스를 구현하는지 확인하는 데 사용됩니다 (아래 참조).

에서 Activity/ FragmentActivity/ Fragment"라고"그는 DialogFragment단순히이 인터페이스를 구현합니다.

당신이 DialogFragment모든 것을 닫고 DialogFragment그 결과를 반환 할 지점에 추가해야합니다 :

listener.onFinishEditDialog(mEditText.getText().toString());
this.dismiss();

어디로 mEditText.getText().toString()다시 부름을 전달할 것인가 Activity?

다른 것을 반환하려면 리스너가 취하는 인수를 변경하기 만하면됩니다.

마지막으로, 인터페이스가 실제로 부모 활동 / 조각에 의해 구현되었는지 확인해야합니다.

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    // Verify that the host activity implements the callback interface
    try {
        // Instantiate the EditNameDialogListener so we can send events to the host
        listener = (EditNameDialogListener) context;
    } catch (ClassCastException e) {
        // The activity doesn't implement the interface, throw exception
        throw new ClassCastException(context.toString()
                + " must implement EditNameDialogListener");
    }
}

이 기술은 매우 융통성이 있으며 아직 대화 상자를 닫지 않으려는 경우에도 결과를 다시 호출 할 수 있습니다.


13
이와 함께 좋은 작품 Activity의와 FragmentActivity의하지만 경우는 발신자 A는 Fragment?
Brais Gabin

1
나는 당신을 완전히 이해하지 못합니다. 그러나 발신자가이면 동일하게 작동합니다 Fragment.
Assaf Gamliel

2
호출자가 호출 한 경우 Fragment몇 가지 작업을 수행 할 수 있습니다. 1. 조각을 참조로 전달합니다 (메모리 누수가 발생할 수 있으므로 좋은 생각이 아님). 2. FragmentManager전화 및 findFragmentById또는 findFragmentByTag당신의 활동에 존재하는 조각을 얻을 것이다. 도움이 되었기를 바랍니다. 좋은 하루 되세요!
Assaf Gamliel

5
이 접근 방식의 문제점은 조각을 재생성해야하기 때문에 조각이 객체를 잘 유지하지 못한다는 것입니다. 예를 들어 방향을 변경하려고하면 OS가 조각을 재생성하지만 리스너의 인스턴스를 더 이상 사용할 수 없게됩니다.
Necronet

5
@LOG_TAG @Timmmm의 답변을보십시오. setTargetFragment()getTargetFragment()마법이다.
Brais Gabin

48

DialogFragment에서 결과를받는 훨씬 간단한 방법이 있습니다.

먼저 Activity, Fragment 또는 FragmentActivity에서 다음 정보를 추가해야합니다.

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Stuff to do, dependent on requestCode and resultCode
    if(requestCode == 1) { // 1 is an arbitrary number, can be any int
         // This is the return result of your DialogFragment
         if(resultCode == 1) { // 1 is an arbitrary number, can be any int
              // Now do what you need to do after the dialog dismisses.
         }
     }
}

이것은 requestCode기본적으로 호출 한 DialogFragment의 int 레이블입니다. 이것이 잠시 후에 어떻게 작동하는지 보여 드리겠습니다. resultCode는 DialogFragment에서 현재 대기중인 Activity, Fragment 또는 FragmentActivity에 발생한 상황을 알려주는 코드입니다.

다음 코드는 DialogFragment를 호출하는 것입니다. 예를 들면 다음과 같습니다.

DialogFragment dialogFrag = new MyDialogFragment();
// This is the requestCode that you are sending.
dialogFrag.setTargetFragment(this, 1);     
// This is the tag, "dialog" being sent.
dialogFrag.show(getFragmentManager(), "dialog");

이 세 줄을 사용하면 DialogFragment를 선언하고 대화 상자가 닫히면 requestCode (onActivityResult (...)를 호출 한 다음 대화 상자를 표시 함)를 설정합니다. 간단합니다.

이제 DialogFragment dismiss()에서 resultCode를 onActivityResult ()로 다시 보내 려면 바로 앞에 한 줄만 추가 하면됩니다.

getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, getActivity().getIntent());
dismiss();

그게 다야. 결과 코드 는이 경우 int resultCode설정 한 것으로 정의됩니다 resultCode = 1;.

이제 DialogFragment의 결과를 호출하는 Activity, Fragment 또는 FragmentActivity로 다시 보낼 수 있습니다.

또한이 정보가 이전에 게시 된 것처럼 보이지만 충분한 예가 없으므로 자세한 내용을 제공 할 것이라고 생각했습니다.

편집 06.24.2016 위의 잘못된 코드에 대해 사과드립니다. 그러나 가장 확실한 것은 결과를 라인으로 보는 활동으로 되돌릴 수 없다는 것입니다.

dialogFrag.setTargetFragment(this, 1);

Fragment아닌 대상을 설정합니다 Activity. 따라서 이렇게하려면 implement a을 사용해야합니다 InterfaceCommunicator.

당신의 DialogFragment세트에서 전역 변수

public InterfaceCommunicator interfaceCommunicator;

그것을 다루는 공개 함수를 만듭니다

public interface InterfaceCommunicator {
    void sendRequestCode(int code);
}

당신이에 코드를 다시 보낼 준비가되면 Activity이 때 DialogFragment실행 완료, 당신은 단순히 전에 줄을 추가 dismiss();하여 DialogFragment:

interfaceCommunicator.sendRequestCode(1); // the parameter is any int code you choose.

활동에서 이제 두 가지를 수행해야합니다. 첫 번째는 더 이상 적용 할 수없는 한 줄의 코드를 제거하는 것입니다.

dialogFrag.setTargetFragment(this, 1);  

그런 다음 인터페이스를 구현하면 모든 작업이 완료됩니다. implements클래스 맨 위에 있는 절에 다음 줄을 추가하면 됩니다.

public class MyClass Activity implements MyDialogFragment.InterfaceCommunicator

그리고 @Override활동의 기능은

@Override
public void sendRequestCode(int code) {
    // your code here
}

메소드와 마찬가지로이 인터페이스 메소드를 사용합니다 onActivityResult(). 인터페이스 방법은 for DialogFragments이고 다른 하나는 for Fragments입니다.


4
보호 된 액세스 수준으로 인해 DialogFragment에서 onActivityResult를 호출 할 수 없으므로 대상이 Activity 인 경우이 방법이 작동하지 않습니다.
Ruslan Yanchyshyn

2
그것은 사실이 아닙니다. 내 프로젝트 에서이 정확한 코드를 사용합니다. 그것이 내가 뽑은 곳이며 제대로 작동합니다. 이 보호 된 액세스 수준 문제가있는 경우 필요한 경우 모든 방법 및 클래스의 액세스 수준을 보호 된 수준에서 비공개 또는 공개 수준으로 변경할 수 있습니다.
Brandon

6
안녕하세요, dialogFrag.setTargetFragment(this, 1)Activity에서 호출 할 수 있다고 말 했지만이 메소드는 Fragment를 첫 번째 인수로 받으 므로 캐스팅 할 수 없습니다. 내가 맞아?
MondKin

1
활동 내용을 설명하기 위해 몇 가지 답변을 게시하겠습니다.
Brandon

1
@Swift @lcompare DialogFragment에서 onAttach (Context context)를 재정의해야 할 수도 있습니다. 같은 :@Override public void onAttach(Context context) { super.onAttach(context); yourInterface = (YourInterface) context; }
lidkxx

20

글쎄 너무 늦게 대답 할 수 있지만 여기에서 내가 결과를 다시 얻기 위해 한 것 DialogFragment입니다. @brandon의 답변과 매우 유사합니다. 여기 DialogFragment에서 조각에서 전화를 걸고 대화 상자를 호출하는 위치 에이 코드를 배치하십시오.

FragmentManager fragmentManager = getFragmentManager();
            categoryDialog.setTargetFragment(this,1);
            categoryDialog.show(fragmentManager, "dialog");

어디에 categoryDialogDialogFragment내가 전화를 걸이 이후의 구현에 dialogfragment장소 당신이 의도에 데이터를 설정하는 코드. 값 resultCode은 1이며 설정하거나 시스템 정의를 사용할 수 있습니다.

            Intent intent = new Intent();
            intent.putExtra("listdata", stringData);
            getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, intent);
            getDialog().dismiss();

이제 호출 조각으로 돌아가서이 메소드를 구현할 차례입니다. 데이터 유효성 검사 또는 당신이 원하는 경우 성공 결과 resultCoderequestCode상태 경우에.

 @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);        
        //do what ever you want here, and get the result from intent like below
        String myData = data.getStringExtra("listdata");
Toast.makeText(getActivity(),data.getStringExtra("listdata"),Toast.LENGTH_SHORT).show();
    }

10

프래그먼트액티비티 와 통신 할 수 있도록하는 다른 접근 방식 :

1) 프래그먼트에서 공개 인터페이스를 정의하고 변수를 만듭니다.

public OnFragmentInteractionListener mCallback;

public interface OnFragmentInteractionListener {
    void onFragmentInteraction(int id);
}

2) 활동을 조각의 mCallback 변수에 캐스트하십시오.

try {
    mCallback = (OnFragmentInteractionListener) getActivity();
} catch (Exception e) {
    Log.d(TAG, e.getMessage());
}

3) 활동에서 청취자를 구현하십시오.

public class MainActivity extends AppCompatActivity implements DFragment.OnFragmentInteractionListener  {
     //your code here
}

4) 활동에서 OnFragmentInteraction을 대체하십시오.

@Override
public void onFragmentInteraction(int id) {
    Log.d(TAG, "received from fragment: " + id);
}

그것에 대한 자세한 정보 : https://developer.android.com/training/basics/fragments/communicating.html


잘 요약 해 주셔서 감사합니다. 다른 사람들에게 한 가지 참고할 사항은 Android Devs 튜토리얼 public void onAttach에서 프래그먼트 조각 을 재정의하고 여기에서 액티비티 캐스팅을 수행하는 것입니다.
Big_Chair

8

내가 찾은 한 가지 쉬운 방법은 다음과 같습니다.이 대화 상자를 구현하십시오.

  CallingActivity callingActivity = (CallingActivity) getActivity();
  callingActivity.onUserSelectValue("insert selected value here");
  dismiss();

그런 다음 Dialog Fragment라는 활동에서 다음과 같이 적절한 기능을 작성하십시오.

 public void onUserSelectValue(String selectedValue) {

        // TODO add your implementation.
      Toast.makeText(getBaseContext(), ""+ selectedValue, Toast.LENGTH_LONG).show();
    }

토스트는 그것이 작동한다는 것을 보여주는 것입니다. 나를 위해 일했다.


그것이 올바른 방법인지 확실하지 않지만 확실히 작동합니다 :)
soshial

Interface콘크리트 클래스와의 하드 커플 링보다는 사용하는 것이 좋습니다.
waqaslam

6

아무도 로컬 통신 을 사용 DialogFragment하여 Activity통신 을 제안한 사람이 없다는 것에 매우 놀랐습니다 ! 다른 제안보다 훨씬 간단하고 깨끗합니다. 기본적으로 Activity방송을 청취하도록 등록하고 DialogFragment인스턴스 에서 로컬 방송을 보냅니다 . 단순한. 전체 설정 방법에 대한 단계별 지침은 여기를 참조 하십시오 .


2
나는 그 해결책을 좋아한다. 이것은 안드로이드에서 우수하거나 모범 사례로 간주됩니까?
Benjamin Scharbau

1
튜토리얼이 정말 마음에 들었습니다. 게시 해 주셔서 감사합니다. 나는 당신이 시도하는 것에 따라 어느 방법이 다른 것보다 더 유용 할 수 있다고 덧붙이고 싶습니다. 대화 상자에서 여러 입력 / 결과가 활동으로 다시 전송되는 경우 로컬 브로드 캐스트 경로를 제안합니다. 출력이 매우 기본 / 간단한 경우 onActivityResult 경로를 사용하는 것이 좋습니다. 따라서 모범 사례 질문에 답하려면 달성하려는 목표에 따라 다릅니다.
Brandon

1
@AdilHussain 당신이 맞아요. 사람들이 활동 내에서 조각을 사용하고 있다고 가정했습니다. setTargetFragment 옵션은 Fragment 및 DialogFragment와 통신하는 경우 유용합니다. 그러나 DialogFragment를 호출하는 활동 인 경우 Broadcast 메소드를 사용해야합니다.
Brandon

3
Foo의 사랑을 위해 방송을 사용하지 마십시오! 보안 문제 때문에 응용 프로그램을 엽니 다. 또한 학대 방송에서 작동 해야하는 최악의 안드로이드 응용 프로그램을 발견했습니다. 코드를 완전히 사용할 수 없게 만드는 더 좋은 방법을 생각할 수 있습니까? 이제 CLEAR 코드 줄 대신 브로드 캐스트 수신기를 근절해야합니까? 명확하게하기 위해 브로드 코스트에는 용도가 있지만이 상황에서는 그렇지 않습니다! 이 맥락에서 절대로! 그냥 부주의하다. 로컬 여부 콜백 만 있으면됩니다.
StarWind0

1
Guava EventBus뿐만 아니라 GreenRobot EventBus도 있습니다. 나는 Guava EventBus를 사용하지 않았지만 GreenRobot EventBus를 사용했으며 그것에 대한 좋은 경험을 가지고 있습니다. 친절하고 사용하기 쉽습니다. GreenRobot EventBus를 사용하도록 Android 애플리케이션을 아키텍처하는 방법에 대한 작은 예는 여기를 참조 하십시오 .
Adil Hussain 12

3

또는 다음과 같이 ViewModel을 공유하십시오.

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}


public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

https://developer.android.com/topic/libraries/architecture/viewmodel#sharing_data_between_fragments


2

필자의 경우 targetFragment에 인수를 전달해야했습니다. 그러나 "Fragment already active"예외가 있습니다. 그래서 parentFragment가 구현 한 DialogFragment에 인터페이스를 선언했습니다. parentFragment가 DialogFragment를 시작하면 자체적으로 TargetFragment로 설정됩니다. 그런 다음 DialogFragment에서 전화했습니다.

 ((Interface)getTargetFragment()).onSomething(selectedListPosition);

2

코 틀린에서

    // My DialogFragment
class FiltroDialogFragment : DialogFragment(), View.OnClickListener {
    
    var listener: InterfaceCommunicator? = null

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        listener = context as InterfaceCommunicator
    }

    interface InterfaceCommunicator {
        fun sendRequest(value: String)
    }   

    override fun onClick(v: View) {
        when (v.id) {
            R.id.buttonOk -> {    
        //You can change value             
                listener?.sendRequest('send data')
                dismiss()
            }
            
        }
    }
}

// 내 활동

class MyActivity: AppCompatActivity(),FiltroDialogFragment.InterfaceCommunicator {

    override fun sendRequest(value: String) {
    // :)
    Toast.makeText(this, value, Toast.LENGTH_LONG).show()
    }
}
 

나는 그것이 향상되기를 희망한다면 그것을 개선하십시오. 내 영어 실력이 좋지 않다


내 경우에는 대화 상자가 활동이 아닌 조각으로 만들어져이 솔루션이 작동하지 않습니다. 하지만 난 당신이 넣어 웃는 :)
Ssenyonjo

0

인수를 보내고 두 번째 조각에서 결과를 받으려면 Fragment.setArguments를 사용하여이 작업을 수행 할 수 있습니다.

static class FirstFragment extends Fragment {
    final Handler mUIHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 101: // receive the result from SecondFragment
                Object result = msg.obj;
                // do something according to the result
                break;
            }
        };
    };

    void onStartSecondFragments() {
        Message msg = Message.obtain(mUIHandler, 101, 102, 103, new Object()); // replace Object with a Parcelable if you want to across Save/Restore
                                                                               // instance
        putParcelable(new SecondFragment(), msg).show(getFragmentManager().beginTransaction(), null);
    }
}

static class SecondFragment extends DialogFragment {
    Message mMsg; // arguments from the caller/FirstFragment

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);
        mMsg = getParcelable(this);
    }

    void onClickOK() {
        mMsg.obj = new Object(); // send the result to the caller/FirstFragment
        mMsg.sendToTarget();
    }
}

static <T extends Fragment> T putParcelable(T f, Parcelable arg) {
    if (f.getArguments() == null) {
        f.setArguments(new Bundle());
    }
    f.getArguments().putParcelable("extra_args", arg);
    return f;
}
static <T extends Parcelable> T getParcelable(Fragment f) {
    return f.getArguments().getParcelable("extra_args");
}

-3

옵션 중 하나로 사용하려면 (아직 언급하지 않았으므로) 오토와 같은 이벤트 버스를 사용할 수 있습니다. 따라서 대화 상자에서 다음을 수행하십시오.

bus.post(new AnswerAvailableEvent(42));

그리고 발신자 (활동 또는 조각)에게 구독을 요청하십시오.

@Subscribe public void answerAvailable(AnswerAvailableEvent event) {
   // TODO: React to the event somehow!
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.