0

我一生都无法弄清楚如何在不使用 configChanges 来指定您要手动处理方向更改的情况下管理对话框。所以假设你有这个 AndroidManifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testandroid"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>    
</manifest>

拿这个 MainActivity.java:

package com.example.testandroid;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;

public class MainActivity extends Activity {
    private final static String TAG = "MainActivity";
    Dialog mDialog = null;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
        setContentView(R.layout.activity_main);
    }    
    public void doShowDialog(View b) {
        Log.d(TAG, "doShowDialog");
        showDialog(1);
    }

    private void tryDismiss() {
        Log.d(TAG, "tryDismiss");
        try {           
            dismissDialog(1);
            removeDialog(1);
            mDialog.dismiss();
        } catch(IllegalArgumentException ex) {
            Log.e(TAG, ex.getMessage());
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    @Override
    protected void onPause() {
        tryDismiss();
        super.onPause();
        Log.d(TAG, "onPause");

    }
    @Override
    protected Dialog onCreateDialog(int dialog) {
        Builder b = new AlertDialog.Builder(this);
        b.setTitle("Hello").setMessage("Waiting..");
        mDialog =  b.create();
        return mDialog;

    }    
}

和这个布局(main.xml)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
 >
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Open Dialog"
        android:onClick="doShowDialog"
        />

</LinearLayout>

如果您从 onDestroy 或 onPause 调用似乎并不重要,对话框会在方向切换后重新显示。但为什么?我告诉它走开。如果调用 removeDialog/dismissDialog 在方向改变之前调用它什么也不做。我终其一生都无法弄清楚为什么会这样。我所知道的摆脱这种情况的唯一方法是自己处理方向变化,方法是使用

android:configChanges="keyboardHidden|orientation"

我知道新的工作方式是使用我尚未升级到的 FragmentDialog 的东西,并且还没有准备好为此重写我的整个应用程序。这不起作用似乎很奇怪。

这只是我在我的应用程序中遇到的一个真实世界问题的一个示例,用户可以请求从远程服务器中提取一些数据(以更新微调器的数据),如果他们切换方向,加载对话框将永远不会消失除了使用 android:configChanges 选项处理方向更改之外,似乎没有解决此问题的方法。我可以做到,但对我来说不得不这样做似乎很荒谬。

-- 更新 -- 删除了关闭对话框的按钮,因为它不是必需的,而且由于对话框位于顶部,因此无论如何您都无法单击它。

要重现只需启动应用程序,单击打开对话框的按钮,然后旋转手机。

4

4 回答 4

2

您的对话框保存在 中onSaveInstanceState,因此您可以尝试在它启动之前将其关闭:

@Override
protected void onSaveInstanceState(Bundle state)
{
  tryDismiss();
  super.onSaveInstanceState(state);
}

另外我不太明白你为什么要使用Activity的onCreateDialog来管理对话框。设计它的原因是自动处理方向变化。如果你想手动处理,为什么不直接使用对话框的功能呢?而不是使用showDialog(id)onCreateDialog(id)直接启动它,它不会在旋转屏幕后重新出现。

    Builder b = new AlertDialog.Builder(this);
    b.setTitle("Hello").setMessage("Waiting..");
    Dialog mDialog =  b.create();
    mDialog.show(); // <-----
于 2012-07-09T10:20:17.457 回答
1

塞巴斯蒂安明白了,但我想多解释一下这种情况和我在对话中的发现,因为它可能会让人很困惑:

  1. 正如塞巴斯蒂安所说, 如果您希望对话框在旋转之前消失,则必须从onSaveInstanceState调用dismissDialog/removeDialog。
  2. 虽然您可以从 onCreate 创建一个对话框,但如果您在方向更改之前不关闭它,您将无法在活动重新启动时在 onCreate 方法中关闭它。您必须从onPostCreate调用dismissDialog
  3. 在方向改变后调用dismissDialog 也不能在onRestoreInstanceState中工作。我在调用 super.onRestoreInstanceState 之前和之后都尝试过,但都没有工作(认为它会因为在 onSaveInstanceState 中发生消除)

比这更重要的是,我了解到如果您正在执行一些异步任务,例如 HTTP 调用,并且您有一个包含回调函数的内部类,该回调函数将在任务完成时运行,您需要知道该内部如果屏幕旋转,类方法将包含对外部 Activity 类的原始实例的引用。这对我来说一点也不明显,因为我实际上并没有使用 AsyncTask,因为许多其他人都遇到了问题(我使用的是异步 http 库)。

举一个类似于我真实代码的小例子:

     public class MyActivity extends Activity {
   private static MyActivity sThis;

   @Override
   public void onCreate(Bundle state) {
      super.onCreate(state);
      sThis = this;
      doAsyncWork();
   }

   @Override
   public void onDestroy() {
      super.onDestroy();
      sThis = null;
   }
   private void doAsyncWork() {
      showDialog(LOADING_DIALOG); 
      ExampleAsyncWorker.work(new AsyncWorkerCallback() {   
           @Override
           public void onWorkComplete() {
             dismissDialog(LOADING_DIALOG); //doesn't work if orientation change happened.
           }        
      });
   }
}

上述代码通过 CategoryManager 连接到外部服务器,下载类别列表,完成后调用 onCategoriesObtained 和 onFetchComplete(为简洁起见,还删除了一些错误处理回调函数)。如果在 fetchCategories 调用和 onFetchComplete 之间发生方向变化,那么 onFetchComplete 中对dismissDialog 的调用将永远不会起作用。原因是这个内部类隐式引用了在方向改变之前创建的 Activity 类的原始实例。因此,当您调用dismissDialog 时,您是在原始实例而不是新实例上调用它,这将导致dismissDialog 失败并显示以下消息:“没有通过Activity#showDialog 显示ID 为1 的对话框”。我确实想出了解决这个问题的方法,但它有点破解:

在您的 Activity 类中,包含对this引用的静态引用并将其设置在 onCreate 中,并将其设置为 onDestroy,并使用内部类中的该引用,如下所示:

public class MyActivity extends Activity {
   private static MyActivity sThis;

   @Override
   public void onCreate(Bundle state) {
      super.onCreate(state);
      sThis = this;
      doAsyncWork();
   }

   @Override
   public void onDestroy() {
      super.onDestroy();
      sThis = null;
   }
   private void doAsyncWork() {
      showDialog(LOADING_DIALOG); 
      ExampleAsyncWorker.work(new AsyncWorkerCallback() {   
           @Override
           public void onWorkComplete() {
              sThis.dismissDialog(LOADING_DIALOG);
           }        
      });
   }
}

请注意,我不确定这是一个很好的做法,但它对我有用。我知道在引用外部类的活动中内部类可能存在问题(泄漏上下文),因此可能有更好的方法来解决这个问题。

于 2012-07-10T17:22:10.163 回答
0

AFAIK,这个泄露的窗口可以通过两种方式处理。

@Override
public void onDestroy(){
    super.onDestroy();
    if ( Dialog!=null && Dialog.isShowing() ){
        Dialog.dismiss();
    }
}

或者

if(getActivity()!= null && !getActivity().isFinishing()){
            Dialog.show();
} 

这里 Dailog 是你的进度 /alert dailog

于 2016-11-08T09:59:56.233 回答
-1

要在没有保存实例的情况下创建应用程序,您可以使用 super.onCreate(null);

于 2012-11-16T14:41:04.207 回答