当自定义alertdialog具有打开的微调器时,处理方向更改的正确方法是什么?

在我的应用程序中,我有一个自定义AlertDialog(由系统使用showDialog()处理),其中包含一个带有2个选项卡的tabhost。 其中一个标签是一个旋转器。 只要微调器未打开(显示微调器对话框),我就可以毫无问题地旋转屏幕。 如果在旋转过程中旋转器是打开的,我会得到:

FATAL EXCEPTION: main java.lang.IllegalArgumentException: View not attached to window manager at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:355) at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:200) at android.view.Window$LocalWindowManager.removeView(Window.java:432) at android.app.Dialog.dismissDialog(Dialog.java:278) at android.app.Dialog.access$000(Dialog.java:71) at android.app.Dialog$1.run(Dialog.java:111) at android.app.Dialog.dismiss(Dialog.java:268) at android.widget.Spinner.onDetachedFromWindow(Spinner.java:89) at android.view.View.dispatchDetachedFromWindow(View.java:6173) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1164) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1162) at android.view.ViewRoot.dispatchDetachedFromWindow(ViewRoot.java:1746) at android.view.ViewRoot.doDie(ViewRoot.java:2757) at android.view.ViewRoot.handleMessage(ViewRoot.java:1995) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:130) at android.app.ActivityThread.main(ActivityThread.java:3683) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:507) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) at dalvik.system.NativeStart.main(Native Method) 

所以…

1 – 是否可以在onPause()期间以编程方式关闭微调器?

2 – 我应该使用不同的方法吗?

3 – 如果没有合适的解决方案,我该如何捕获这个特殊的exception? (我知道不好的做法,但是需要停止崩溃,并且由于活动在被破坏后正确地重建自己,无论如何都无关紧要。

…请为所有圣洁的爱,请不要建议我在我的活动声明中添加android:configChanges="orientation" 。 令我感到惊讶的是,它经常被接受作为定向问题的官方解决方案。

编辑其他信息

这是我的对话框创建代码供参考:

 static final int ID_DIALOG_CHOOSER = 1; int pref_location; private Dialog dialog; ... protected Dialog onCreateDialog(int id) { switch(id) { case ID_DIALOG_CHOOSER: dialog = show(ID_DIALOG_CHOOSER); break; } return dialog; } ... showDialog(DialogView.ID_DIALOG_CHOOSER); ... Dialog show(final int dialogType) { if (dialogType == ID_DIALOG_CHOOSER) { AlertDialog.Builder builder = new AlertDialog.Builder(this); // inflate tabhost layout View tabHostLayout = (View) inflater.inflate(R.layout.tabhost_layout, null); FrameLayout tabContent = (FrameLayout) tabHostLayout.findViewById(android.R.id.tabcontent); // inflate tab content layouts and add to tabhost layout LinearLayout tab1 = (LinearLayout) inflater.inflate(R.layout.dialog_tab1, null); tabContent.addView(tab1); LinearLayout tab2 = (LinearLayout) inflater.inflate(R.layout.dialog_tab2, null); tabContent.addView(tab2); // tab setup TabHost tabHost = (TabHost) tabHostLayout.findViewById(R.id.TabHost_Dialog_Tabs); tabHost.setup(); TabHost.TabSpec tab_1 = tabHost.newTabSpec("Category 1"); tab_1.setContent(R.id.LinearLayout_Dialog_Tab1_Content); tab_1.setIndicator(this.getResources().getString(R.string.dialog_tab1), this.getResources().getDrawable(R.drawable.tabhost_icon_selector)); tabHost.addTab(tab_1); TabHost.TabSpec tab_2 = tabHost.newTabSpec("Category 2"); tab_2.setContent(R.id.LinearLayout_Dialog_Tab2_Content); tab_2.setIndicator(this.getResources().getString(R.string.dialog_tab2), this.getResources().getDrawable(R.drawable.tabhost_icon_selector)); tabHost.addTab(tab_2); // spinner setup final Spinner spinner_location = (Spinner) tab1.findViewById(R.id.Spinner_Dialog_Location); String[] locationArrayVals = null; ArrayAdapter adapter_location = null; locationArrayVals = this.getResources().getStringArray(R.array.location_array_vals); adapter_location = ArrayAdapter.createFromResource(this, R.array.location_array_listdisplay, android.R.layout.simple_spinner_item); adapter_location.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner_location.setAdapter(adapter_location); // ok button Button button_ok = (Button) tab1.findViewById(R.id.Button_Dialog_OK); button_ok.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { pref_location = spinner_location.getSelectedItemPosition(); dialog.dismiss(); } }); // create dialog builder.setTitle("Location") .setView(tabHost) .setCancelable(true); dialog = builder.create(); } return dialog; } 

使用临时解决方法进行编辑

对于任何感兴趣的人,我设法通过子类化微调器并覆盖onDetachedFromWindow()来至少停止崩溃:

 public static class CatchingSpinner extends Spinner { public CatchingSpinner(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDetachedFromWindow() { try { super.onDetachedFromWindow(); } catch (IllegalArgumentException e) { // do whatever } } } 

就像我说的,一个解决方法。 仍在努力解决方案。 :/

我找到了更好的解决方案。 用一个看起来和行为像Spinner的按钮替换Spinner实际上并不是那么难(幸运的是崩溃除外)。

  

 public void showSpinner() { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setTitle(_spinnerPrompt); builder.setSingleChoiceItems(_spinnerOptions, _spinnerSelection, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { _spinnerSelection = item; _pseudoSpinner.setText(_spinnerOptions[item]); _restoreSpinnerOnRestart = false; dialog.dismiss(); } }); builder.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { _restoreSpinnerOnRestart = false; } }); AlertDialog alert = builder.create(); _restoreSpinnerOnRestart = true; alert.show(); } @Override public Bundle onSaveInstanceState() { Bundle state = super.onSaveInstanceState(); state.putBoolean(STATE_SPINNER_RESTORE, _restoreSpinnerOnRestart); state.putInt(STATE_SPINNER_SELECTION, _spinnerSelection); return state; }; @Override public void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); _spinnerSelection = savedInstanceState.getInt(STATE_SPINNER_SELECTION, -1); _restoreSpinnerOnRestart = savedInstanceState.getBoolean(STATE_SPINNER_RESTORE); _pseudoSpinner.setText(_spinnerOptions[_spinnerSelection]); if (_restoreSpinnerOnRestart) { showSpinner(); } }; 

好的,我想我找到了你的问题: dialog.dismiss();
你做的确很奇怪 – 你正在通过活动创建对话,但是说要解雇它而不是放弃它的活动 – 这是两种不同的方法,你不能混合。 您应该选择一种方法:dialog.show和dialog.dismiss或activity.showDialog()和activity.dismissDialog()< - 这是更好的,因为处理方向更改。
你应该做的只是从类变量中删除对话框,到处使用activity.showDialog。 可能你的问题是在onClick中解雇对话框。 只需使用YourActivityName.this.dismissDialog(DIALOG_NUMBER)即可。
阅读更多关于开发者网站的对话框 – 我也有这样的问题,并花了很长时间来了解它是如何工作的,但毕竟 – 对话框并不那么复杂;)

我有类似的崩溃。 我在显示对话框时禁用了方向更改。

 @Override public void onDismiss(DialogInterface dialog) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } @Override public void onShow(DialogInterface dialog) { int loadedOrientation = getResources().getConfiguration().orientation; int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; if (loadedOrientation == Configuration.ORIENTATION_LANDSCAPE) { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; } else if (loadedOrientation == Configuration.ORIENTATION_PORTRAIT) { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; } setRequestedOrientation(requestedOrientation); } 

注意:我实际上发现没有可靠的方法来锁定屏幕方向 。