从 DialogFragment 回调片段

Posted

技术标签:

【中文标题】从 DialogFragment 回调片段【英文标题】:Callback to a Fragment from a DialogFragment 【发布时间】:2012-11-23 20:57:38 【问题描述】:

问题:如何创建从 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 中的唯一保证是通过 Bundle 通过 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 的具体示例。它有是/否按钮。如何将这些按钮按下发送回创建它的 Fragment?

【问题讨论】:

您提到“但如果 DialogFragment 在其生命周期中暂停和恢复,则无法保证侦听器会在附近。”我认为片段状态在 onDestroy() 期间被破坏?您一定是对的,但是我现在对如何使用 Fragment 状态有点困惑。如何重现你提到的问题,听者不在? 我不明白为什么你不能简单地在 DialogFragment 中使用 OnClickListener listener = (OnClickListener) getParentFragment();,而你的主 Fragment 像你原来那样实现接口。 这是一个无关问题的答案,但它确实向您展示了这是如何以一种干净的方式完成的 ***.com/questions/28620026/… 【参考方案1】:

所涉及的活动完全不知道 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();


【讨论】:

我认为这里的关键是setTargetFragmentgetTargetFragmentonActivityResult 的使用有点不清楚。在 Fragment 调用者中声明您自己的特定方法并使用它可能会更好,而不是重新利用 onActivityResult。但那时它的所有语义。 栈级变量没有使用? 这会在配置更改轮换后继续存在吗? 用过这个。注意:堆栈级别不是在旋转或睡眠中生存的必要条件。我的片段实现了 DialogResultHandler#handleDialogResult(我创建的接口),而不是 onActivityResult。 @myCode,对于显示添加到 Intent 的对话框选择值非常有帮助,然后在您的 onActivityResult 中读取。初学者不清楚意图。 @eternalmatt,你的反对是完全有道理的,但我认为onActivityResult()的价值在于它保证存在于任何Fragment上,因此任何Fragment都可以作为父级。如果您创建自己的接口并让父 Fragment 实现它,则子级只能与实现该接口的父级一起使用。如果您以后开始更广泛地使用孩子,那么将孩子耦合到该界面可能会再次困扰您。使用“内置” onActivityResult() 接口不需要额外的耦合,因此它可以让您更加灵活。【参考方案2】:

TargetFragment 解决方案似乎不是对话框片段的最佳选择,因为它可能会在应用程序被销毁并重新创建后创建IllegalStateException。在这种情况下,FragmentManager 找不到目标片段,您将收到带有如下消息的 IllegalStateException

“关键 android:target_state: index 1 的片段不再存在”

似乎Fragment#setTargetFragment() 不是用于子片段和父片段之间的通信,而是用于兄弟片段之间的通信。

因此,另一种方法是使用父片段的ChildFragmentManager 创建这样的对话框片段,而不是使用活动FragmentManager

dialogFragment.show(ParentFragment.this.getChildFragmentManager(), "dialog_fragment");

并且通过使用接口,在DialogFragmentonCreate 方法中,您可以获得父片段:

@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

【讨论】:

这也适用于 api 23 中引入的 onAttach(Context context)。 这应该是公认的答案。当前接受的答案是错误的,片段不应该像这样使用。 @AhmadFadli 这里的问题是为片段之间的通信获取正确的上下文(父级)。如果您要将对话框片段用作活动的子项,则不应有任何混淆。 Activity 的 FragmentManager 和 getActivity() 检索回调就足够了。 这应该是公认的答案。它解释清楚,详细,不仅仅是扔给人们的代码。 @lukecross ParentFragment 是创建 DialogFragment 的片段(调用 show() 的片段)但看起来 childFragmentManager 无法在重新配置/屏幕旋转中幸存...【参考方案3】:

我按照这个简单的步骤来做这些事情。

    使用callBackMethod(Object data) 之类的方法创建DialogFragmentCallbackInterface 之类的接口。您会调用它来传递数据。 现在您可以在片段中实现DialogFragmentCallbackInterface 接口,例如MyFragment implements DialogFragmentCallbackInterface

    在创建DialogFragment 时,将您的调用片段MyFragment 设置为创建DialogFragment 的目标片段,使用myDialogFragment.setTargetFragment(this, 0) 检查setTargetFragment (Fragment fragment, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
    

    通过调用getTargetFragment() 将您的目标片段对象放入您的DialogFragment 并将其转换为DialogFragmentCallbackInterface。现在您可以使用此接口将数据发送到您的片段。

    DialogFragmentCallbackInterface callback = 
               (DialogFragmentCallbackInterface) getTargetFragment();
    callback.callBackMethod(Object data);
    

    这一切都完成了!只要确保你已经在你的片段中实现了这个接口。

【讨论】:

这应该是最佳答案。很好的答案。 还要确保对源片段和目标片段使用相同的片段管理器,否则 getTargetFragment 将不起作用。因此,如果您使用 childFragmentManager,它将无法工作,因为源片段不是由子片段管理器提交的。最好将这 2 个片段视为兄弟片段而不是父/子片段。 坦率地说,在 2 个兄弟片段之间进行通信时,最好只使用目标片段模式。通过没有监听器,您可以避免在 fragment2 中意外泄漏 fragment1。使用目标片段时,不要使用监听器/回调。仅使用onActivityResult(request code, resultcode, intent) 将结果返回给fragment1。从fragment1,setTargetFragment() 和从fragment2,使用getTargetFragment()。当使用父/子片段到片段或活动到片段时,您可以使用侦听器或回调,因为在子片段中没有任何泄漏父级的危险。 @Thupten 当您说“泄漏”时,您是指内存泄漏还是“泄漏实现细节”?我认为 Vijay 的答案不会比使用 onActivityResulty 作为返回值更容易泄漏内存。两种模式都将保持对目标片段的引用。如果您的意思是泄漏实现细节,那么我认为他的模式甚至比 onActivityResult 更好。回调方法是显式的(如果命名正确)。如果您返回的所有内容都正常且已取消,则第一个片段必须解释这些内容的含义。 喜欢这个。在设置回调之前从未听说过setTargetFragment。像魅力一样工作!【参考方案4】:

可能有点晚了,但可能会像我一样帮助其他有同样问题的人。

您可以在显示之前在Dialog 上使用setTargetFragment,并在对话框中调用getTargetFragment 以获取参考。

【讨论】:

这是另一个问题的答案,但它也适用于您的问题并且是一个干净的解决方案:***.com/questions/28620026/… IllegalStateException 对我来说【参考方案5】:

Communicating with Other Fragments 指南说 Fragments 应该通过相关的 Activity 进行通信

通常你会希望一个 Fragment 与另一个 Fragment 进行通信,因为 基于用户事件更改内容的示例。全部 片段到片段的通信是通过关联的 活动。两个 Fragment 永远不应该直接通信。

【讨论】:

内部片段怎么样,即另一个片段中的片段应该如何与宿主片段通信 @Ravi:每个片段都应该通过调用getActivity()与所有片段共有的活动进行通信。 @Chris:如果片段需要持续通信,请为每个要实现的适当片段定义一个接口。然后,活动的工作仅限于为片段提供指向其对应片段的接口指针。之后,片段可以通过接口安全地“直接”通信。 我认为随着片段的使用范围的扩大,不使用直接片段通信的最初想法被打破了。例如。在导航抽屉中,活动的每个直接子片段大致充当活动。因此,让诸如对话片段之类的片段通过活动进行通信会损害 IMO 的可读性/灵活性。事实上,似乎没有什么好的方法可以封装 dialogfragment 以使其以可重用的方式与活动和片段一起工作。 我知道这已经过时了,但万一其他人来到这里,我觉得当一个片段“拥有”用于确定创建和DialogFragment 的管理。当活动甚至不确定为什么要创建对话框或在什么条件下应该关闭它时,创建一堆从片段到活动的连接有点奇怪。除此之外,DialogFragment 非常简单,它的存在只是为了通知用户并可能得到响应。【参考方案6】:

您应该在片段类中定义一个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)

    
    ...

【讨论】:

我认为您浏览文档的速度太快了。这两个代码段都是FragmentA,他假设一个活动是OnArticleSelectedListener,而不是启动他的片段。 我会考虑你试图做的不好的做法。 Android 指南建议所有片段到片段的通信都通过活动进行(根据developer.android.com/training/basics/fragments/…)。如果你真的希望这一切都在MyFragment 中处理,你可能想要切换到使用常规的AlertDialog 我认为让片段直接相互通信的问题在于,在某些布局中,并非所有片段都可能被加载,并且如示例中所示,可能需要切换片段。在谈论从片段启动对话框片段时,我认为这种担忧是不正确的。 我已经为我的活动实现了这个。问题:这个解决方案可以扩展成片段可以实例化这个对话框吗? 从架构的角度来看,这是一个很好的做法,因此应该是公认的答案。使用 onActivityResult 会导致意大利面条式架构【参考方案7】:

根据官方文档:

Fragment#setTargetFragment

此片段的可选目标。例如,如果此片段正在由另一个片段启动,并且完成后希望将结果返回给第一个片段,则可以使用此方法。此处设置的目标通过 FragmentManager#putFragment 跨实例保留。

Fragment#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");
        
    
    ...

【讨论】:

你能解释一下吗? 在这种情况下,我们需要将“MyFragment”引用传递给“MyialogFragment”,而“Fragment”提供了执行此操作的方法。我添加了官方文档的描述,应该比我说的更清楚。 你已经使用过newInstance,不需要再次setTargetFragment。【参考方案8】:

推荐的方法是使用新的Fragment Result API。

通过使用它,您不需要覆盖 onAttach(context)setTargetFragment()现在已弃用。 p>


1 - 在 parent Fragment 的onCreate 上添加结果侦听器:

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)

    childFragmentManager.setFragmentResultListener("requestKey", this)  key, bundle ->
        val result = bundle.getString("bundleKey")
    


2- 在 child 片段上,设置结果(例如在按钮单击侦听器上):

button.setOnClickListener 
    val result = "resultSample"

    setFragmentResult("requestKey", bundleOf("bundleKey" to result))

有关文档的更多信息:https://developer.android.com/guide/fragments/communicate#fragment-result

希望对你有帮助!

【讨论】:

哇,多么棒的答案!我想知道这个答案需要多长时间才能提高选票,我是一名活跃的首席 Android 工程师,不知何故没有听到这个 api 的风声,我不得不说这可能是现在 - 在 2022 年 -正确的方法,应该是这个问题的公认答案!干杯杰拉尔多:-) 我很高兴它有帮助!如果没有这个新的 API,做这么简单的任务会很痛苦!干杯 Vin。 很好的答案,谢谢。【参考方案9】:

为片段设置监听器的正确方法是在附加时设置它。我遇到的问题是从未调用过 onAttachFragment() 。经过一番调查,我意识到我一直在使用 getFragmentManager 而不是 getChildFragmentManager

这是我的做法:

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

            
        );
    

【讨论】:

【参考方案10】:

更新:请注意,如果有人感兴趣,我可以分享使用视图模型的更简单方法。

Kotlin 伙计们,我们开始吧!

所以我们遇到的问题是我们创建了一个活动MainActivity,在该活动上我们创建了一个片段FragmentA,现在我们想在FragmentA之上创建一个对话框片段,将其称为FragmentB .我们如何在不经过MainActivity 的情况下将结果从FragmentB 返回到FragmentA

注意:

    FragmentAMainActivity 的子片段。要管理在 FragmentA 中创建的片段,我们将使用 childFragmentManager 来实现这一点! FragmentAFragmentB 的父片段,要从FragmentB 内部访问FragmentA,我们将使用parenFragment

话虽如此,在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)

就是这样。

【讨论】:

我关注了这个文档:developer.android.com/guide/topics/ui/dialogs,但它没有用。太感谢了。我希望这个 parentfragment 事情每次都能像预期的那样工作:) 不要忘记在 onDetach 中将 listener 设置为 null :) @BekaBot 感谢您的评论。我做了一些研究,似乎没有必要关闭听众。 ***.com/a/37031951/10030693【参考方案11】:

我遇到了类似的问题。我发现的解决方案是:

    在您的 DialogFragment 中声明一个接口,就像 James McCracken 上面解释的那样。

    在您的活动中实现接口(不是片段!这不是一个好习惯)。

    从您的活动中的回调方法,调用片段中所需的公共函数,该函数执行您想做的工作。

因此,它变成了一个两步过程:DialogFragment -> Activity,然后是 Activity -> Fragment

【讨论】:

【参考方案12】:

我从 Fragment LiveWallFilterFragment(receiving fragment) 得到结果到 Fragment DashboardLiveWall(calling 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 
                    
                
            
        
    

【讨论】:

【参考方案13】:

更新:

我根据我的 gist 代码创建了一个库,它使用 @CallbackFragment@Callback 为您生成这些转换。

https://github.com/zeroarst/callbackfragment.

该示例为您提供了将回调从片段发送到另一个片段的示例。

旧答案:

我做了一个BaseCallbackFragment 和注释@FragmentCallback。它目前扩展了Fragment,您可以将其更改为DialogFragment 并且可以使用。它按以下顺序检查实现:getTargetFragment() > getParentFragment() > context (activity)。

然后你只需要扩展它并在你的片段中声明你的接口并给它注解,然后基础片段将完成其余的工作。注解还有一个参数mandatory,供你判断是否要强制fragment实现回调。

public class EchoFragment extends BaseCallbackFragment 

    private FragmentInteractionListener mListener;

    @FragmentCallback
    public interface FragmentInteractionListener 
        void onEcho(EchoFragment fragment, String echo);
    

https://gist.github.com/zeroarst/3b3f32092d58698a4568cdb0919c9a93

【讨论】:

【参考方案14】:
    this is work for me
    i think you can set callback in display method in your fragment,
    

    **in my fragment**

    val myDialogFragment=MyDialogFragment()
    myDialogFragment.display(fragmentManager!!,this)

//我的片段实现了CallbackDialogFragment所以设置这个显示方法

    **in dialog fragment**

    lateinit var callBackResult: CallbackDialogFragment


    fun display(fragmentManager: FragmentManager, callback: CallbackDialogFragment) 
        callBackResult = callback
        show(fragmentManager,"dialogTag")
    

【讨论】:

【参考方案15】:

我使用 RxAndroid 以一种优雅的方式解决了这个问题。在 DialogFragment 的构造函数中接收一个观察者,并在调用回调时订阅 observable 并推送该值。然后,在您的 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

【讨论】:

Android 的有趣之处在于有一种叫做生命周期的东西。基本片段或对话片段需要能够在生命周期事件中保留状态(及其连接)。回调或观察者不能被序列化,因此这里有同样的问题。【参考方案16】:

更多改进的方法是只使用newInstanceinterface

这是一个需要DailogFragment的片段

public class Fragment extends Fragment implements returnPinInterface 
....
....
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup 
container,Bundle savedInstanceState) 

// A simple call to show DialogFragment

   btnProceed.setOnClickListener(v -> 
        fragment = DailogFragment.newInstance(this);
        fragment.show(getChildFragmentManager(),null );
        fragment.setCancelable(false);
    );


   //Grab whatever user clicked/selected/ or typed
   @Override
   public void onPinReturn(String s) 
      Log.d("ReturnedPin", s);
   

你的 DialogFragment 来了

public class PinDialogFragment extends DialogFragment 

 //Create a static variable to help you receive instance of fragment you 
 //passed
public static Fragment fragm;


  // Create new Instance and grab the object passed in Fragment up 
  //there
  public static PinDialogFragment newInstance(Fragment frag) 
    PinDialogFragment fragment = new PinDialogFragment();
    fragm = frag;
    return fragment;
  

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) 
   View v = inflater.inflate(R.layout.fragment_pin, container, false);

  //
   btn.setOnClickListener(btn ->                         
   listener.onReturnPin("1234"));
   
   return v;



//Use the Fragm to instantiate your Interface

@Override
public void onAttach(Context context) 
    super.onAttach(context);

    if (fragm instanceof ReturnPinInterface) 
        listener = (ReturnPinInterface) fragm;
     else 
        throw new RuntimeException("you must implement ReturnPinInterface");
    


享受吧!

【讨论】:

【参考方案17】:

在official Android documentation 中建议使用onAttach。所以,我们可以利用那个方法。

确保您的父片段实现了一个监听器,例如OnPopupButtonClickListener:

public interface OnPopupButtonClickListener 
   void onPositiveButtonClicked();
   void onNegativeButtonClicked();

在您的父片段中使用getChildFragmentManager() 显示您的DialogFragment 实例:

PopupDialogFragment dialogFragment = new PopupDialogFragment();
dialogFragment.show(getChildFragmentManager(), "PopupDialogFragment");

在扩展DialogFragment 实例的对话框类中添加此方法: (请注意,我们正在通过 getParentFragment() 检索我们的父片段,它实现了我们的自定义侦听器接口 OnPopupButtonClickListener

@Override
public void onAttach(@NonNull @NotNull Context context) 
   super.onAttach(context);
   // Verify that the host activity implements the callback interface
   try 
      listener = (OnPopupButtonClickListener) getParentFragment();
    catch (ClassCastException e) 
      // The activity doesn't implement the interface, throw exception
      throw new ClassCastException(getActivity().toString()
                    + " must implement OnPopupButtonClickListener");
   

在您的对话框中,您可以在 DialogFragment 中需要时使用您的侦听器,例如:

Button positiveButton = view.findViewById(R.id.positiveButton);
positiveButton.setOnClickListener(v -> 
    if (listener != null) 
       listener.onPositiveButtonClicked();
       getDialog().dismiss();
    
);

【讨论】:

以上是关于从 DialogFragment 回调片段的主要内容,如果未能解决你的问题,请参考以下文章

如何从Android中没有活动和片段的函数调用DialogFragment?

来自另一个片段的 Snackbar 回调

从 dialogfragment 调用 onBackPressed 完成活动

android DialogFragment 回调到 Fragment

关闭 dialogFragment 时键盘未关闭

DialogFragment:创建片段时仅显示一次动画