在Android中防止屏幕旋转时取消对话框
我试图防止在活动重新启动时关闭使用Alert builder生成的对话框 如果我重载onConfigurationChanged方法,我可以成功地执行此操作并将布局重置为正确的方向,但我会丢失edittext的粘滞文本功能。因此,在解决对话问题时,我创建了这个edittext问题 如果我保存edittext中的字符串并在OnConfiguration更改中重新分配它们,它们似乎仍然默认为初始值,而不是旋转前输入的值。即使我强制执行一个invalidate,它似乎也会更新它们 我真的需要解决对话问题或编辑文本问题在Android中防止屏幕旋转时取消对话框,android,dialog,android-edittext,onconfigurationchanged,Android,Dialog,Android Edittext,Onconfigurationchanged,我试图防止在活动重新启动时关闭使用Alert builder生成的对话框 如果我重载onConfigurationChanged方法,我可以成功地执行此操作并将布局重置为正确的方向,但我会丢失edittext的粘滞文本功能。因此,在解决对话问题时,我创建了这个edittext问题 如果我保存edittext中的字符串并在OnConfiguration更改中重新分配它们,它们似乎仍然默认为初始值,而不是旋转前输入的值。即使我强制执行一个invalidate,它似乎也会更新它们 我真的需要解决对话问
感谢您的帮助。如果您在更改方向时更改布局,我不会将
android:configChanges=“orientation”
放在您的清单中,因为您仍在重新创建视图
使用以下方法保存活动的当前状态(如输入的文本、显示的对话框、显示的数据等):
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
这样,活动将再次执行onCreate,然后调用onRestoreInstanceState方法,您可以在其中再次设置EditText值
如果要存储更复杂的对象,可以使用
@Override
public Object onRetainNonConfigurationInstance() {
}
在这里可以存储任何对象,在onCreate中只需调用
getLastNonConfigurationInstance()
获取对象。一个非常简单的方法是从方法onCreateDialog()
创建对话框(请参见下面的注释)。您可以通过showDialog()
显示它们。通过这种方式,Android为您处理旋转,您不必在onPause()
中调用dismise()
,以避免窗口泄漏,然后您也不必恢复对话框。从文档中:
显示由此活动管理的对话框。对于给定id首次调用onCreateDialog(int,Bundle)时,将使用相同的id进行调用。此后,对话框将自动保存和恢复
有关更多信息,请参阅。希望它能帮助别人
注意:如果使用AlertDialog.Builder,不要从
onCreateDialog()
调用show()
,而是调用create()
。如果使用ProgressDialog,只需创建对象,设置所需参数并返回即可。总之,show()
内部的onCreateDialog()
会导致问题,只需创建de Dialog实例并返回它即可。这应该管用!(我在使用onCreate()中的showDialog()时遇到过问题-实际上没有显示对话框-但是如果在onResume()或侦听器回调中使用它,它会工作得很好)。现在避免此问题的最佳方法是使用一个
创建一个扩展的新类。覆盖并返回旧的或新的
然后你可以用它来展示
下面是一个示例,其中包含:
在你所称的活动中:
new MyDialogFragment().show(getSupportFragmentManager(), "tag"); // or getFragmentManager() in API 11+
此答案有助于解释其他三个问题(及其答案):
您可以将对话框的onSave/onRestore方法与活动的onSave/onRestore方法相结合,以保持对话框的状态 注意:此方法适用于那些“简单”对话框,例如显示警报消息。它不会复制嵌入在对话框中的WebView的内容。如果您真的想防止在旋转过程中删除复杂的对话框,请尝试Chung IW的方法
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
myDialog.onRestoreInstanceState(savedInstanceState.getBundle("DIALOG"));
// Put your codes to retrieve the EditText contents and
// assign them to the EditText here.
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Put your codes to save the EditText contents and put them
// to the outState Bundle here.
outState.putBundle("DIALOG", myDialog.onSaveInstanceState());
}
这个问题很久以前就得到了回答 然而,这是我自己使用的非黑客的和简单的解决方案 我是为自己做的,所以你也可以在你的应用程序中使用它 用途是:
PersistentDialogFragment.newInstance(
getBaseContext(),
RC_REQUEST_CODE,
R.string.message_text,
R.string.positive_btn_text,
R.string.negative_btn_text)
.show(getSupportFragmentManager(), PersistentDialogFragment.TAG);
或
只需在活动中添加安卓:configChanges=“orientation” 元素,该元素位于AndroidManifest.xml中 例如:
<activity
android:name=".YourActivity"
android:configChanges="orientation"
android:label="@string/app_name"></activity>
只需使用
ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize
应用程序将知道如何处理旋转和屏幕大小。当然,最好的方法是使用DialogFragment 下面是我的包装类解决方案,它有助于防止不同的对话框在一个片段(或具有小重构的活动)中被消除。此外,如果由于某些原因,代码中分散了大量的
AlertDialogs
,但在操作、外观或其他方面存在细微差异,那么这有助于避免大规模代码重构
public class DialogWrapper extends DialogFragment {
private static final String ARG_DIALOG_ID = "ARG_DIALOG_ID";
private int mDialogId;
/**
* Display dialog fragment.
* @param invoker The fragment which will serve as {@link AlertDialog} alert dialog provider
* @param dialogId The ID of dialog that should be shown
*/
public static <T extends Fragment & DialogProvider> void show(T invoker, int dialogId) {
Bundle args = new Bundle();
args.putInt(ARG_DIALOG_ID, dialogId);
DialogWrapper dialogWrapper = new DialogWrapper();
dialogWrapper.setArguments(args);
dialogWrapper.setTargetFragment(invoker, 0);
dialogWrapper.show(invoker.getActivity().getSupportFragmentManager(), null);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDialogId = getArguments().getInt(ARG_DIALOG_ID);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return getDialogProvider().getDialog(mDialogId);
}
private DialogProvider getDialogProvider() {
return (DialogProvider) getTargetFragment();
}
public interface DialogProvider {
Dialog getDialog(int dialogId);
}
}
你可以在我的博客上阅读完整的文章,然后玩。我遇到了一个类似的问题:当屏幕方向改变时,对话框的
onDismiss
侦听器被调用,即使用户没有关闭对话框。我可以通过使用onCancel
侦听器来解决这个问题,该侦听器在用户按下后退按钮和用户触摸对话框外部时都会触发。这似乎仍然是一个问题,即使是在“一切正常”和使用DialogFragment
等时
有一个线程在其上声明这是由于消息队列中保留了一个旧的disclose消息。提供的解决方法非常简单:
@Override
public void onDestroyView() {
/* Bugfix: https://issuetracker.google.com/issues/36929400 */
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
令人难以置信的是,在该问题首次报告7年后,仍然需要此功能。如果没有任何帮助,并且您需要一个有效的解决方案,您可以从安全的角度出发,每次打开对话框时,将其基本信息保存到activity ViewModel(并在关闭对话框时将其从该列表中删除)。此基本信息可以是对话框类型和一些id(打开此对话框所需的信息)。此ViewModel在活动生命周期更改期间不会被销毁。假设用户打开一个对话框,留下对餐厅的引用。因此,对话框类型将是LeaveReferenceDialog,id将是餐厅id。打开此对话框时,将此信息保存在一个可以调用DialogInfo的对象中,并将此对象添加到活动的ViewModel中。此信息将允许您在恢复活动时重新打开对话框(
<activity
android:name=".YourActivity"
android:configChanges="orientation"
android:label="@string/app_name"></activity>
ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize
public class DialogWrapper extends DialogFragment {
private static final String ARG_DIALOG_ID = "ARG_DIALOG_ID";
private int mDialogId;
/**
* Display dialog fragment.
* @param invoker The fragment which will serve as {@link AlertDialog} alert dialog provider
* @param dialogId The ID of dialog that should be shown
*/
public static <T extends Fragment & DialogProvider> void show(T invoker, int dialogId) {
Bundle args = new Bundle();
args.putInt(ARG_DIALOG_ID, dialogId);
DialogWrapper dialogWrapper = new DialogWrapper();
dialogWrapper.setArguments(args);
dialogWrapper.setTargetFragment(invoker, 0);
dialogWrapper.show(invoker.getActivity().getSupportFragmentManager(), null);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDialogId = getArguments().getInt(ARG_DIALOG_ID);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return getDialogProvider().getDialog(mDialogId);
}
private DialogProvider getDialogProvider() {
return (DialogProvider) getTargetFragment();
}
public interface DialogProvider {
Dialog getDialog(int dialogId);
}
}
public class MainFragment extends Fragment implements DialogWrapper.DialogProvider {
private static final int ID_CONFIRMATION_DIALOG = 0;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
Button btnHello = (Button) view.findViewById(R.id.btnConfirm);
btnHello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DialogWrapper.show(MainFragment.this, ID_CONFIRMATION_DIALOG);
}
});
}
@Override
public Dialog getDialog(int dialogId) {
switch (dialogId) {
case ID_CONFIRMATION_DIALOG:
return createConfirmationDialog(); //Your AlertDialog
default:
throw new IllegalArgumentException("Unknown dialog id: " + dialogId);
}
}
}
@Override
public void onDestroyView() {
/* Bugfix: https://issuetracker.google.com/issues/36929400 */
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
// On resume in Activity
override fun onResume() {
super.onResume()
// Restore dialogs that were open before activity went to background
restoreDialogs()
}
fun restoreDialogs() {
mainActivityViewModel.setIsRestoringDialogs(true) // lock list in view model
for (dialogInfo in mainActivityViewModel.openDialogs)
openDialog(dialogInfo)
mainActivityViewModel.setIsRestoringDialogs(false) // open lock
}
// Create new dialog
override fun openLeaveReferenceDialog(restaurantId: String) {
var dialog = LeaveReferenceDialog()
// Add id to dialog in bundle
val bundle = Bundle()
bundle.putString(Constants.RESTAURANT_ID, restaurantId)
dialog.arguments = bundle
dialog.show(supportFragmentManager, "")
// Add dialog info to list of open dialogs
addOpenDialogInfo(DialogInfo(LEAVE_REFERENCE_DIALOG, restaurantId))
}
// Dismiss dialog
override fun dismissLeaveReferenceDialog(Dialog dialog, id: String) {
if (dialog?.isAdded()){
dialog.dismiss()
mainActivityViewModel.removeOpenDialog(LEAVE_REFERENCE_DIALOG, id)
}
}
fun addOpenDialogInfo(dialogInfo: DialogInfo){
if (!isRestoringDialogs){
val dialogWasInList = removeOpenDialog(dialogInfo.type, dialogInfo.id)
openDialogs.add(dialogInfo)
}
}
fun removeOpenDialog(type: Int, id: String) {
if (!isRestoringDialogs)
for (dialogInfo in openDialogs)
if (dialogInfo.type == type && dialogInfo.id == id)
openDialogs.remove(dialogInfo)
}
public class AlertRetryDialog extends DialogFragment {
public interface Listener{
void onRetry();
}
Listener listener;
public void setListener(Listener listener)
{
this.listener=listener;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());
builder.setMessage("Please Check Your Network Connection").setPositiveButton("Retry", new
DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//Screen rotation will cause the listener to be null
//Always do a null check of your interface listener before calling its method
if(listener!=null&&listener instanceof HomeFragment)
listener.onRetry();
}
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return builder.create();
}
}
AlertRetryDialog alertRetryDialog = new AlertRetryDialog();
alertRetryDialog.setListener(HomeFragment.this);
alertRetryDialog.show(getFragmentManager(), "tag");
public class YourActivity or YourFragment implements AlertRetryDialog.Listener{
//here's my listener interface's method
@Override
public void onRetry()
{
//your code for action
}
}