2012-06-05 32 views
180

我正在使用DialogFragments的许多事情:从列表中选择项目,输入文本。从DialogFragment接收结果

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

当前我正在调用活动执行DismissListener并给DialogFragment一个参考活动。然后Dialog在活动中调用OnDimiss方法,活动从DialogFragment对象获取结果。由于DialogFragment失去对活动的引用,所以非常混乱,并且它不适用于配置更改(方向更改)。

感谢您的任何帮助。

+9

DialogFragments仍然只是片段。实际上,您的方法是碎片用于与主要活动进行对话的推荐方式。 http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity – codinguser

+1

谢谢你。我非常接近(如你所说)。链接文档帮助我的那一点是使用onAttach()并将该活动投射到一个监听器。 –

+1

@codinguser,@Styx - “给DialogFragment一个对活动的引用” - 这个细节有点冒险,因为'Activity'和'DialogFragment'都可能被重新创建。使用传递给'onAttach(Activity activity)'的'Activity'是适当和推荐的方法。 – sstn

回答

208

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

这似乎是一个滥用onActivityResult(),尤其是因为它根本不涉及任何活动。但是我已经看到它由官方谷歌推荐,甚至可以在api演示中推荐。我认为这是增加了什么g/setTargetFragment()

+2

setTargetFragment提到请求代码在onActivityResult中使用,所以我想可以使用这种方法。 – Giorgi

+69

如果目标是活动,该怎么办? –

+8

如果目标是活动,我会声明接口的方法,如“void onActivityResult2(int requestCode,int resultCode,Intent data)”,并通过一个活动来实现它。在DialogFragment中只需getActivity并检查这个接口并适当地调用它。 –

113

正如你可以看到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"); 
    } 
} 

这种技术是非常灵活的,允许调用回来的结果,即使你不要;不想解雇该对话框尚未完成。

+12

这对于''''Activity''''和''''FragmentActivity'''很好,但是如果调用者是''''Fragment''''? –

+0

我不确定我完全理解你。但是,如果调用者是“片段”,它将起到相同的作用。 –

+1

但是,如何获得''''Fragment''''的实例?没有''''getFragment()'''函数。 –

42

从DialogFragment接收结果有一种更简单的方法。

首先,在你的活动,片段,或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发回告诉你当前的等待活动,片段,或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(); 

就是这样。注意, resultCode是de罚款作为我在这种情况下设置为resultCode = 1;int resultCode

就是这样,你现在可以把你的DialogFragment的结果返回给您的电话活动,片段,或FragmentActivity。

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

编辑06.24.2016 对于上述误导性代码,我表示歉意。但你肯定不能接受的结果返回给活动看到,因为该行:

dialogFrag.setTargetFragment(this, 1); 

设定目标Fragment而不是Activity。所以为了做到这一点,你需要使用工具InterfaceCommunicator

在你DialogFragment设置一个全局变量

public InterfaceCommunicator interfaceCommunicator; 

创建一个公共函数来处理它

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

然后,当你准备代码发送回ActivityDialogFragment是运行完,你只需在你面前添加行​​您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

+4

如果target为Activity,则此方法无效,因为由于受保护的访问级别,您无法调用onActivityResult(来自DialogFragment)。 –

+2

这根本不是真的。我在我的项目中使用了这个确切的代码。这是我从中拉出来的地方,它工作得很好。请记住,如果您有这种受保护的访问级别问题,您可以根据需要更改您受保护的私有或公共级别的方法和类的访问级别。 – Brandon

+0

如果您将Activity的'onActivityResult()'更改为'public',那么在DialogFragment中引用该Activity时,必须将其转换为该Activity以访问该方法,在这种情况下,您最好使用Assaf的解决方案。或者我在这里错过了什么? – kassim

6

我很惊讶地看到,没有人建议使用本地广播进行DialogFragmentActivity的沟通!我发现它比其他建议更简单,更清洁。实质上,您注册您的Activity以收听广播,并从DialogFragment实例发送本地广播。简单。有关如何完成设置的分步指南,请参见here

+2

我喜欢这个解决方案,这被认为是Android的一个很好的或最好的实践吗? –

+1

我真的很喜欢这个教程,感谢你发布它。我想补充一点,取决于你想要完成的任何一种方法可能比其他方法更有用。如果您有多个输入/结果从对话框发送回活动,我会建议本地广播路线。如果你的输出是非常基本的/简单的,我会推荐使用onActivityResult路线。所以,要回答最佳实践问题,这取决于你想要完成的事情! – Brandon

+0

Benjamin,最佳实践......我认为很明显,人们不清楚如何将数据从DialogFragment传递到Activity,这里缺少最佳实践: - /我曾经使用过过去的界面方法,但我尝试避免类型检查和显式类的这些天。 –

6

一个简单的方法,我发现了以下几点: 实现,这是你的dialogFragment,

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

然后在调用该对话框片段创建相应的功能,例如活动:

public void onUserSelectValue(String selectedValue) { 

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

吐司是为了表明它的工作原理。为我工作。

+0

我不确定这是否是一种正确的方式,但它确实有效:) – soshial

+0

这是一个很好的做法吗? – lcompare

+0

更好地使用'接口'而不是与混凝土类的硬耦合。 – waqaslam

10

那么它太晚可能会回答,但这里是我做了什么从DialogFragment得到结果。非常类似于@布兰登的回答。 在这里,我从片段调用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(); 

现在是时候回到调用片段并实现此方法。如果您希望resultCoderequestCode处于if条件,请检查数据有效性或结果是否成功。

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

在我的情况下,我需要传递参数到targetFragment。但我得到了“碎片已经激活”的例外。所以我在DialogFragment中声明了一个实现了parentFragment的接口。当parentFragment启动一个DialogFragment时,它将自己设置为TargetFragment。然后在DialogFragment我称为

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

不同的方法,以允许片段到通信到其活动

1)定义在该片段中一个公共接口,并创建用于可变它

public OnFragmentInteractionListener mCallback; 

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

2)铸活性TH在片段ë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

-2

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

bus.post(new AnswerAvailableEvent(42)); 

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

@Subscribe public void answerAvailable(AnswerAvailableEvent event) { 
    // TODO: React to the event somehow! 
}