从DialogFragment接收结果

时间:2012-06-05 21:35:09

标签: android dialog android-fragments fragment dismiss

我正在使用 DialogFragments 来做很多事情:从列表中选择项目,输入文字。

将值(即字符串或列表中的项)返回给调用活动/片段的最佳方法是什么?

目前,我正在调用活动实现DismissListener,并为DialogFragment提供对活动的引用。然后,Dialog调用活动中的OnDimiss方法,活动将从DialogFragment对象中获取结果。非常混乱,因为DialogFragment失去对活动的引用,它不能用于配置更改(方向更改)。

感谢您的帮助。

14 个答案:

答案 0 :(得分:237)

在显示对话框的位置使用myDialogFragment.setTargetFragment(this, MY_REQUEST_CODE),然后在对话框完成后,您可以从中调用getTargetFragment().onActivityResult(getTargetRequestCode(), ...),并在包含的片段中实现onActivityResult()。< / p>

这似乎是对onActivityResult()的滥用,特别是因为它根本不涉及活动。但我已经看到它被官方谷歌人推荐,甚至可能在api演示中。我认为这是为g/setTargetFragment()添加的内容。

答案 1 :(得分:131)

正如您所见,here有一种非常简单的方法。

DialogFragment中添加一个界面侦听器,如:

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

然后,添加对该侦听器的引用:

private EditNameDialogListener listener;

这将用于“激活”侦听器方法,并检查父Activity / Fragment是否实现此接口(见下文)。

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

这项技术非常灵活,即使您不想解开对话框,也可以回复结果。

答案 2 :(得分:46)

从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(一旦Dialog被关闭就会调用onActivityResult(...),然后你就会显示对话框了。就这么简单。< / p>

现在,在DialogFragment中,您需要在dismiss()之前直接添加一行,以便将resultCode发送回onActivityResult()。

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

那就是它。注意,resultCode定义为int resultCode,在这种情况下我已设置为resultCode = 1;

即便如此,您现在可以将DialogFragment的结果发送回您的调用Activity,Fragment或FragmentActivity。

此外,看起来这些信息是先前发布的,但没有给出足够的例子,所以我想我会提供更多细节。

编辑06.24.2016 我为上面的误导性代码道歉。但是您肯定无法将结果返回到活动中,如下所示:

dialogFrag.setTargetFragment(this, 1);

设置目标Fragment而不是Activity。因此,为了做到这一点,您需要使用实现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()方法一样。除了接口方法用于DialogFragments,另一个用于Fragments

答案 3 :(得分:19)

好吧,为时已晚,可能会回答,但这就是我从DialogFragment获得结果的方法。与@ brandon的答案非常相似。 在这里,我从片段中调用DialogFragment,只需将此代码放在您调用对话框的位置。

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

其中categoryDialog是我想要调用的DialogFragment,在此dialogfragment的实现之后,将此代码置于意图设置数据的位置。 resultCode的值为1,您可以设置它或使用系统定义。

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

现在是时候回到调用片段并实现这个方法了。如果您想在if条件中使用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();
    }

答案 4 :(得分:9)

允许片段活动进行通信的不同方法:

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

答案 5 :(得分:7)

我发现的一个简单方法如下: 实现这是你的dialogFragment,

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

Toast表明它有效。为我工作。

答案 6 :(得分:6)

我很惊讶地发现没有人建议使用本地广播进行DialogFragmentActivity通信!我发现它比其他建议更简单,更清洁。基本上,您注册了Activity以收听广播,并从DialogFragment个实例发送本地广播。简单。有关如何设置全部内容的分步指南,请参阅here

答案 7 :(得分:2)

对于仍在阅读此内容的任何人:setTargetFragment()已被弃用。现在建议像这样使用FragmentResultListener API:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setFragmentResultListener("requestKey") { key, bundle ->
        val result = bundle.getString("resultKey")
        // Do something with the result...
    }

    ...

    // Somewhere show your dialog
    MyDialogFragment.newInstance().show(parentFragmentManager, "tag")
}

然后在您的MyDialogFragment中设置结果:

button.setOnClickListener{
    val result = "some string"
    setFragmentResult("requestKey", bundleOf("resultKey" to result))
    dismiss()
}

答案 8 :(得分:1)

在我的情况下,我需要将参数传递给targetFragment。但是我得到了例外情况&#34; Fragment已经活跃了#34;。所以我在DialogFragment中声明了一个实现了parentFragment的接口。当parentFragment启动DialogFragment时,它将自己设置为TargetFragment。然后在DialogFragment中我调用了

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

答案 9 :(得分:1)

或共享此处显示的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

答案 10 :(得分:0)

在科特林

// My DialogFragment

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

    }
}

}

//我的活动

MyActivity类:AppCompatActivity(),FiltroDialogFragment.InterfaceCommunicator {

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

}

我希望它能起作用,如果您可以改进,请对其进行编辑。  我的英语不太好

答案 11 :(得分: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");
}

答案 12 :(得分:0)

在对话框片段上

class AbcDialogFragment(private val ondata: (data: String) -> Unit) :  DialogFragment() {}

从片段/活动显示对话框的代码

val abcDialogFragment = AbcDialogFragment(ondata = {data->  })
                
abcDialogFragment.show(requireActivity().supportFragmentManager, "TAG")

并且在对话框片段中,您可以在对话框片段关闭或任何点击侦听器时调用 onData。

答案 13 :(得分:-3)

只是将它作为选项之一(因为还没有人提到它) - 你可以使用像Otto这样的事件总线。 所以在对话框中你可以:

select * from 
table_a as a inner join table_b as b
on a.A = b.B
inner join table_a as a 
on a.A = a.B
inner join table_b as b 
on a.A = b.A;
-- SO ON joining till n number of tables...

让你的来电者(活动或片段)订阅它:

bus.post(new AnswerAvailableEvent(42));