方向更改时的 DialogFragment 回调
Posted
技术标签:
【中文标题】方向更改时的 DialogFragment 回调【英文标题】:DialogFragment callback on orientation change 【发布时间】:2012-10-01 02:18:03 【问题描述】:我正在迁移我目前使用Activity.showDialog(DIALOG_ID);
的对话框,以使用android reference 中讨论的DialogFragment
系统。
在我的开发过程中使用回调将某些事件传递回打开对话框的活动/片段时出现了一个问题:
这是一个简单对话框的示例代码:
public class DialogTest extends DialogFragment
public interface DialogTestListener
public void onDialogPositiveClick(DialogFragment dialog);
// Use this instance of the interface to deliver action events
static DialogTestListener mListener;
public static DialogTest newInstance(Activity activity, int titleId, int messageId)
udateListener(activity);
DialogTest frag = new DialogTest();
Bundle args = new Bundle();
args.putInt("titleId", titleId);
args.putInt("messageId", messageId);
frag.setArguments(args);
return frag;
public static void udateListener(Activity activity)
try
// Instantiate the NoticeDialogListener so we can send events with it
mListener = (DialogTestListener) activity;
catch (ClassCastException e)
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString() + " must implement DialogTestListener");
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
int titleId = getArguments().getInt("titleId");
int messageId = getArguments().getInt("messageId");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// dialog title
builder.setTitle(titleId);
// dialog message
builder.setMessage(messageId);
// dialog negative button
builder.setNegativeButton("No", new OnClickListener()
public void onClick(DialogInterface dialog, int id) );
// dialog positive button
builder.setPositiveButton("Yes", new OnClickListener()
public void onClick(DialogInterface dialog, int id)
mListener.onDialogPositiveClick(DialogTest.this);
);
// create the Dialog object and return it
return builder.create();
下面是一些调用它的活动代码:
public class SomeActivity extends FragmentActivity implements DialogTestListener
private EditText mUserName;
@Override
public void onCreate(Bundle savedInstanceState)
// setup ui
super.onCreate(savedInstanceState);
setContentView(R.layout.ui_user_edit);
// name input
mUserName = (EditText) findViewById(R.id.userEdit_editTextName);
@Override
public void onDialogPositiveClick(DialogFragment dialog)
Log.d(TAG, this.toString());
mUserName.setText(mUserName.getText() + "1");
private void showDialog()
DialogTest test = DialogTest.newInstance(SomeActivity.this, R.string.someTitleText, R.string.someMessageText);
test.show(getSupportFragmentManager(), "testDialog");
代码几乎就是您所看到的参考。问题是,一旦您进行方向更改,当显示一个对话框时,它就会停止按预期工作-->由于活动生命周期,活动和对话框都被重建,对话框现在没有正确的引用新的重建活动。
我在我的活动 onResume 方法中添加了以下代码:
@Override
protected void onResume()
super.onResume();
DialogTest.udateListener(this);
这样做,我得到了预期的行为,当方向改变发生时,对话框将事件发送回新的重建活动。
我的问题是: 处理在方向更改期间由 FragmentActivity 打开的 DialogFragment 之间的回调的“最佳做法”是什么?
最好的问候
【问题讨论】:
听起来你已经陷入了许多 Fragment 陷阱。我遇到了和你一样的问题,我在阅读这篇优秀的文章时能够解决它:code.hootsuite.com/orientation-changes-on-android。 【参考方案1】:有更好的解决方案,而不是使用静态方法和变量,因为它只适用于对话框的一个实例。最好将回调存储为非静态成员
private DialogTestListener mListener;
public void setListener (DialogTestListener listener)
mListener = listener;
那么你应该使用像这样mDialogFragment.show(getSupportFragmentManager(), DIALOG_TAG);
这样的标签来显示你的对话框
然后在你的活动的onResume
方法中你可以重置你的监听器
protected void onResume()
super.onResume();
mDialogFragment = (CMFilterDialogFrg) getSupportFragmentManager().findFragmentByTag(DIALOG_TAG);
if(mDialogFragment != null)
mDialogFragment.setListener(yourListener)
【讨论】:
谢谢 - 这个方法对我有用!我第一次使用带有回调的对话框片段。我为此挣扎了一段时间-谢天谢地,我看到了这篇文章。我自己就快到了,但这篇文章帮助我们找到了解决方案 +1! Jason Long 如果您从 Activity 调用对话框,则提供最佳解决方案,如果您从 Fragment 调用,则 cuasodayleo 提供最佳解决方案,如果您需要面对这两种情况,这是最佳解决方案。 非常感谢..这很容易实现和直接 这不会在方向更改后继续存在。getSupportFragmentManager().findFragmentByTag(DIALOG_TAG);
将为空【参考方案2】:
是的,这是我自己一直陷入的常见陷阱。首先让我说您在onResume()
中调用DialogTest.udateListener()
的解决方案似乎完全适合我。
另一种方法是使用ResultReceiver
,它可以序列化为Parcelable
:
public class DialogTest extends DialogFragment
public static DialogTest newInstance(ResultReceiver receiver, int titleId, int messageId)
DialogTest frag = new DialogTest();
Bundle args = new Bundle();
args.putParcelable("receiver", receiver);
args.putInt("titleId", titleId);
args.putInt("messageId", messageId);
frag.setArguments(args);
return frag;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
int titleId = getArguments().getInt("titleId");
int messageId = getArguments().getInt("messageId");
ResultReceiver receiver = getArguments().getParcelable("receiver");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// dialog title
builder.setTitle(titleId);
// dialog message
builder.setMessage(messageId);
// dialog negative button
builder.setNegativeButton("No", new OnClickListener()
public void onClick(DialogInterface dialog, int id)
receiver.sendResult(Activity.RESULT_CANCEL, null);
);
// dialog positive button
builder.setPositiveButton("Yes", new OnClickListener()
public void onClick(DialogInterface dialog, int id)
receiver.sendResult(Activity.RESULT_OK, null);
);
// create the Dialog object and return it
return builder.create();
然后您可以像这样处理 Receiver 中的所有内容:
protected void onReceiveResult(int resultCode, Bundle resultData)
if (getActivity() != null)
// Handle result
查看ResultReceiver doesn't survire to screen rotation 了解更多详情。所以最后你可能仍然需要用你的Activity
重新连接ResultReceiver
。唯一的区别是您将Activity
与DialogFragment
解耦。
【讨论】:
感谢您的意见!现在我将使用onResume()
解决方案,一旦我需要一些 ActivityDialogFragment 解耦,我将看看 ResultReceiver。
查看 Fragment#setRetainInstance(boolean) 并通读文档。这实际上是我们真正在寻找的解决方案。虽然 Activity 仍在被销毁和重新创建,但 Fragment 正在被保留和重用。因此,回调 DialogTestListener 仍然指向正确的对象,您不必在配置更改后重新连接 Fragments。
此解决方案的问题是,如果您激活“不保留活动”打开对话框,然后按 HOME 并从主页启动器打开应用程序,将重新创建对话框,您将获得BadParcelableException,我正在为此苦苦挣扎。
非常干净的解决方案;以前没有听说过 ResultReceiver。【参考方案3】:
虽然 André 的解决方案有效,但更好的解决方案是在您的 Fragment
中获取 onAttach()
期间的更新活动。
private DialogTestListener mListener;
@Override
public void onAttach(Activity activity)
super.onAttach(activity);
mListener = (DialogTestListener) activity;
使用此解决方案,您将不再需要在 newInstance()
中传递 Activity
。您只需要确保拥有您的Fragment
的Activity
是DialogTestListener
。您也不需要像ResultReceiver
解决方案那样保存状态。
【讨论】:
【参考方案4】:首先,从FragmentParent
调用setTargetFragment
以启动dialogFragment
。在dialogFragment
中使用getTargetFragment
回调片段并返回数据。所有数据结果将在onactivityresult
的FragmentParent
中执行
点击此链接: Receive result from DialogFragment
【讨论】:
虽然这是正确的,但它对问题没有任何帮助。因为数据传输和轮换后恢复回调是不同的事情。例如,在回调中我可以打开另一个活动或发出请求。【参考方案5】:另一种方法是您可以停止重新创建活动。您必须告诉 Android 您将自己处理方向更改,并且 android 不会重新创建您的活动。您需要将此活动添加到清单文件中:
android:configChanges="keyboardHidden|orientation"
如果不是这样,那么您可以按照 Google 的建议使用标准 onSaveInstanceState()
来保存您的状态并使用 savedInstanceState
进行恢复。
这是 Google 的官方指南: http://developer.android.com/guide/components/activities.html#Lifecycle
如果您还没有,请仔细阅读。它会真正帮助您进行 android 开发。
【讨论】:
我不想停止在方向更改期间销毁和重新创建的活动,它在这方面按预期工作。我只想知道在方向更改期间处理由 FragmentActivity 打开的 DialogFragment 之间的回调的最佳实践。以上是关于方向更改时的 DialogFragment 回调的主要内容,如果未能解决你的问题,请参考以下文章
重叠DialogFragment,在方向更改时以错误的顺序重新创建
DialogFragment 方向更改崩溃与 getActivity()