从对话框片段回调片段

问题: 如何创建从 Dialog片段到另一个片段的回调。在我的例子中,所涉及的活动应该完全不知道 Dialog片段。

我考虑过了

public class MyFragment extends Fragment implements OnClickListener

然后在某个时候,我 可以

DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
dialogFrag.show(getFragmentManager, null);

我的对话框片段是什么样子的

protected OnClickListener listener;
public static DialogFragment newInstance(OnClickListener listener) {
DialogFragment fragment = new DialogFragment();
fragment.listener = listener;
return fragment;
}

但是,如果 DialogFragment 在其生命周期中暂停并继续运行,并不能保证侦听器就在附近。片段中唯一的保证是那些通过 setArguments 和 getArguments 通过 Bundle 传递的保证。

如果活动应该是侦听器,那么有一种方法可以引用该活动:

public Dialog onCreateDialog(Bundle bundle) {
OnClickListener listener = (OnClickListener) getActivity();
....
return new AlertDialog.Builder(getActivity())
........
.setAdapter(adapter, listener)
.create();
}

但是我不希望活动监听事件,我需要一个片段。实际上,它可以是任何实现 OnClickListener 的 Java 对象。

考虑一个片段的具体示例,该片段通过 DialogFragment 呈现 AlertDialog。它有是/否按钮。我怎样才能将这些按钮发送回创建它的碎片?

172899 次浏览

所涉及的活动完全没有意识到对话框片段。

碎片类:

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

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


}
...
}

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

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

与其他碎片交流指南说碎片应该是 通过相关的活动进行交流

通常你会希望一个片段与另一个片段交流,因为 示例根据用户事件更改内容 片段到片段的通信是通过关联的 两个片段不应该直接交流。

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

  1. 创建类似于 DialogFragmentCallbackInterface的接口,使用类似于 callBackMethod(Object data)的方法。
  2. 现在您可以在片段中实现 DialogFragmentCallbackInterface接口,如 MyFragment implements DialogFragmentCallbackInterface
  3. 在创建 DialogFragment时,将您的调用片段 MyFragment设置为创建 DialogFragment的目标片段,使用 myDialogFragment.setTargetFragment(this, 0)检查 < a href = “ http://developer.android.com/reference/android/app/Fragment.html # setTargetFragment (android.app.Fragment,% 20int)”rel = “ noReferrer”> setTargetFragment (片段,int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1);
    
  4. Get your target fragment object into your DialogFragment by calling getTargetFragment() and cast it to DialogFragmentCallbackInterface.Now you can use this interface to send data to your fragment.

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

    完成了! 只需要确保在片段中实现了这个接口。

我也遇到过类似的问题,我找到的解决办法是:

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

  2. 在您的活动中实现接口(而不是片段! 这不是一个好的实践)。

  3. 从您的活动中的回调方法,调用片段中执行您想要执行的工作的必需的公共函数。

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

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

”关键 android: target _ state: index 1的片段不再存在

看起来 Fragment#setTargetFragment()并不是用于子片段和父片段之间的通信,而是用于兄弟姐妹片段之间的通信。

所以另一种方法是通过使用父片段的 ChildFragmentManager而不是使用活动 FragmentManager来创建这样的对话框片段:

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

通过使用 Interface,在 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

我得到的结果到片段 DashboardLiveWall (调用片段)从片段 LiveWallFilterFragment (接收片段)像这样..。

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

我用 RxAndroid 以一种优雅的方式解决了这个问题。在 DialogFragment 的构造函数中接收一个观察器,并在调用回调时订阅可观察值并推送该值。然后,在片段中创建观察者的内部类,创建一个实例并将其传递到 DialogFragment 的构造函数中。我在观察器中使用 WeakReference 来避免内存泄漏。密码如下:

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

日期 PickerFragment.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());
}
}
}

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

将侦听器设置为片段的正确方法是将其设置为 附件。我遇到的问题是 onAttachFragment ()从未被调用过。经过一些调查,我意识到我一直在使用 GetFragmentManager而不是 获取儿童碎片管理器

我是这么做的:

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


}
});
}
}

根据官方文件:

片段 # setTargetFragment

这个片段的可选目标。例如,如果这个片段是由另一个片段启动的,并且当这个片段启动后想要将结果返回给第一个片段时,就可以使用这个方法。这里的目标集通过 FragmentManager # putFragment 跨实例保留。

片段 # 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");
}
}
...
}

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

科特林的伙计们,我们走!

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

注:

  1. FragmentAMainActivity的一个子片段。为了管理在 FragmentA中创建的片段,我们将使用 childFragmentManager
  2. 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)
}

就是这样。

    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,因此将其设置为 display 方法

    **in dialog fragment**


lateinit var callBackResult: CallbackDialogFragment




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

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

你的对话片段来了

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

好好享受吧!

Android 官方文档中建议使用 onAttach。因此,我们可以利用这种方法。

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

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

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

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

在扩展 DialogFragment实例的对话框类中添加以下方法: (注意,我们正在通过实现定制侦听器接口 OnPopupButtonClickListenergetParentFragment()检索父片段

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

建议的方法是使用新的 片段结果 API

通过使用它,无论是 您不需要重写 onAttach (上下文)还是 SetTargetFragment ()现在已经废弃了


1-在 家长片段的 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

希望能有帮助!

完整的例子如何使用 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)
}
}
}