ChatGPT解决这个技术问题 Extra ChatGPT

从 DialogFragment 回调片段

问题:如何创建从 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 的具体示例。它有是/否按钮。如何将这些按钮按下发送回创建它的片段?

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

P
Piotr Ślesarew

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

}

对话片段类:

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() 接口不需要额外的耦合,因此它可以让您更加灵活。
V
Vasily Kabunov

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 无法在重新配置/屏幕旋转中幸存下来......
V
Vijay Vankhede

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

使用诸如 callBackMethod(Object data) 之类的方法创建诸如 DialogFragmentCallbackInterface 之类的接口。您会调用它来传递数据。现在您可以在片段中实现 DialogFragmentCallbackInterface 接口,例如 MyFragment 实现 DialogFragmentCallbackInterface 在创建 DialogFragment 时将调用片段 MyFragment 设置为创建 DialogFragment 的目标片段使用 myDialogFragment.setTargetFragment(this, 0) check setTargetFragment (Fragment fragment, int requestCode) MyDialogFragment dialogFrag =新的 MyDialogFragment(); dialogFrag.setTargetFragment(this, 1);通过调用 getTargetFragment() 将目标片段对象放入 DialogFragment 并将其转换为 DialogFragmentCallbackInterface。现在您可以使用此接口将数据发送到片段。 DialogFragmentCallbackInterface 回调 = (DialogFragmentCallbackInterface) getTargetFragment(); callback.callBackMethod(对象数据);这一切都完成了!只要确保你已经在你的片段中实现了这个接口。


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

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

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


这是另一个问题的答案,但它也适用于您的问题并且是一个干净的解决方案:stackoverflow.com/questions/28620026/…
对我来说 IllegalStateException
E
Edward Brey

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

通常,您会希望一个 Fragment 与另一个 Fragment 进行通信,例如根据用户事件更改内容。所有 Fragment 到 Fragment 的通信都是通过关联的 Activity 完成的。两个 Fragment 永远不应该直接通信。


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

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

通过使用它,您无需覆盖 onAttach(context) 或 setTargetFragment(),后者现已弃用。

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

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

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

}

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

button.setOnClickListener {
    val result = "resultSample"

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

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

希望能帮助到你!


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

您应该在片段类中定义一个 interface 并在其父 Activity 中实现该接口。此处概述了详细信息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 指南建议所有片段到片段的通信都通过 Activity 进行(每个 developer.android.com/training/basics/fragments/…)。如果您真的希望在 MyFragment 内处理所有内容,您可能需要切换到使用常规 AlertDialog
我认为让片段直接相互对话的问题在于,在某些布局中,并非所有片段都可能被加载,并且如示例中所示,可能需要切换片段。在谈论从片段启动对话片段时,我认为这种担忧是不正确的。
我已经为我的活动实施了这个。问题:这个解决方案可以扩展成片段可以实例化这个对话框吗?
从架构的角度来看,这是一个很好的做法,因此应该是公认的答案。使用 onActivityResult 会导致意大利面条式架构
S
SUPERYAO

根据官方文档:

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。
不推荐使用 setTargetFragment
E
ElOjcar

将侦听器设置为片段的正确方法是在附加时设置它。我遇到的问题是从未调用过 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() {

            }
        });
    }
}

G
Gilbert

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

Kotlin 伙计们,我们开始吧!

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

笔记:

FragmentA 是 MainActivity 的子 Fragment。为了管理在 FragmentA 中创建的片段,我们将使用 childFragmentManager 来执行此操作! FragmentA 是 FragmentB 的父 Fragment,要从 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 感谢您的评论。我做了一些研究,似乎没有必要关闭听众。 stackoverflow.com/a/37031951/10030693
S
Shailesh Mani Pandey

我面临着类似的问题。我发现的解决方案是:

就像 James McCracken 上面解释的那样,在 DialogFragment 中声明一个接口。在您的活动中实现接口(不是片段!这不是一个好习惯)。从您活动中的回调方法中,调用片段中所需的公共函数,该函数执行您想要执行的工作。

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


Z
Zar E Ahmer

我从 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 {
                    }
                }
            }
        }
    }

A
Arst

更新:

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

https://github.com/zeroarst/callbackfragment

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

老答案:

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

然后你只需要扩展它并在你的片段中声明你的接口并给它注解,剩下的基本片段将完成。该注解还有一个参数 mandatory 供您确定是否要强制片段实现回调。

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

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

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


ح
حسن ستاری
    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")
    }

N
NickUnuchek

如何使用 setFragmentResultListener 的完整示例:

父片段 MainFragment.kt

import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResult
import androidx.fragment.app.setFragmentResultListener

class MainFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val showDialogButton = view.findViewById<Button>(R.id.dialog_button)
        showDialogButton.setOnClickListener {
            showMyDialog()
        }
    }

    private fun showMyDialog() {
        MyDialogFragment.showOn(this) { bundle ->
            /*here handle bundle result*/
        }
    }
}

你的对话:

import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResult
import androidx.fragment.app.setFragmentResultListener

class MyDialogFragment : DialogFragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val submitButton = view.findViewById<Button>(R.id.submitButton)
        submitButton.setOnClickListener {
            parentFragment?.setFragmentResult(KEY_CALLBACK_BUNDLE, buildResultBundle())
        }
    }

    private fun buildResultBundle(): Bundle {
        val bundle = Bundle()
        /*here build your result bundle for parent fragment*/
        return bundle
    }

    companion object {

        const val TAG: String = "MyDialogFragment"
        private const val KEY_CALLBACK_BUNDLE: String = "KEY_CALLBACK_BUNDLE"

        fun showOn(fragment: Fragment, callback: (Bundle) -> Unit) {
            val dialog = MyDialogFragment()
            fragment.setFragmentResultListener(KEY_CALLBACK_BUNDLE) { requestKey: String, bundle: Bundle ->
                if (requestKey == KEY_CALLBACK_BUNDLE) {
                    callback(bundle)
                }
            }
            dialog.show(fragment.childFragmentManager, TAG)
        }
    }
}

G
Gerardo Suarez

我用 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 的有趣之处在于有一种叫做生命周期的东西。基本片段或对话片段需要能够在生命周期事件中保留状态(及其连接)。回调或观察者不能被序列化,因此这里有同样的问题。
o
olajide

更改进的方法是只使用 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");
    }
}
}

享受!


a
alierdogan7

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