我正在使用DialogFragment做很多事情:从列表中选择项目,输入文本。
将值(即字符串或列表中的项)返回给调用活动/片段的最佳方法是什么?
目前我正在调用活动实现DismissListener
并为DialogFragment提供对活动的引用。然后,Dialog在活动中调用OnDimiss
方法,活动从DialogFragment对象中获取结果。非常混乱,因为DialogFragment失去对活动的引用,它不能用于配置更改(方向更改)。
谢谢你的帮助。
在显示对话框的位置使用myDialogFragment.setTargetFragment(this, MY_REQUEST_CODE)
,然后在对话框完成后,可以从中调用getTargetFragment().onActivityResult(getTargetRequestCode(), ...)
,并在包含的片段中实现onActivityResult()
。
这似乎是对onActivityResult()
的滥用,特别是因为它根本不涉及活动。但我已经看到它被官方谷歌人推荐,甚至可能在api演示中。我认为这是加入g/setTargetFragment()
的原因。
在科特林
// 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()
}
}
我希望它有用,如果你可以改进请编辑它。我的英文不是很好
如果要发送参数并从第二个片段接收结果,可以使用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");
}
只是将它作为选项之一(因为没有人提到它) - 你可以使用像Otto这样的事件总线。所以在对话框中你做了:
bus.post(new AnswerAvailableEvent(42));
让你的调用者(Activity或Fragment)订阅它:
@Subscribe public void answerAvailable(AnswerAvailableEvent event) {
// TODO: React to the event somehow!
}
正如你可以看到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");
}
}
这种技术非常灵活,即使您还没有想要解除对话框,也可以回调结果。
从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(...),然后您将显示该对话框。就这么简单。
现在,在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
。
好吧,为时已晚可能会回答,但这是我从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条件下使用resultCode
和requestCode
,请检查数据有效性或结果是否成功。
@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();
}
不同的方法,允许Fragment与其活动进行通信:
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
我找到的一个简单方法如下:实现这是你的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表明它有效。为我工作。
我很惊讶地看到没有人建议使用DialogFragment
的本地广播来进行Activity
通信!我发现它比其他建议更简单,更清洁。基本上,你注册你的Activity
听取广播,你从你的DialogFragment
实例发送本地广播。简单。有关如何设置所有内容的分步指南,请参阅here。
在我的情况下,我需要将参数传递给targetFragment。但我得到了异常“片段已经活跃”。所以我在DialogFragment中声明了一个实现了parentFragment的接口。当parentFragment启动DialogFragment时,它将自己设置为TargetFragment。然后在DialogFragment中我打电话给
((Interface)getTargetFragment()).onSomething(selectedListPosition);
或者分享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