android.view.WindowManager$BadTokenException 创建对话框时出错

android.view.WindowManager$BadTokenException error while creating dialog box

我正在尝试从非 activity Class 创建对话框。

这是我的代码

  public static void ShowDialogBox(final Context con, final Listener list) {
        AlertDialog.Builder dlgAlert = new AlertDialog.Builder(con);
        dlgAlert.setMessage("TEXT");
        dlgAlert.setTitle("TEXT");
        dlgAlert.setPositiveButton("TEXT"),
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {

                    }
                });
        dlgAlert.setCancelable(false);
        dlgAlert.create().show(); // THIS LINE GIVES ME AN ERROR
    }

这是我遇到的错误

android.view.WindowManager$BadTokenException: at android.view.ViewRootImpl.setView (ViewRootImpl.java:574) at android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:282) at android.view.WindowManagerImpl.addView (WindowManagerImpl.java:85)
at android.app.Dialog.show (Dialog.java:298) at PACKAGE NAME AND CLASS PACKAGE NAME AND CLASS at PACKAGE NAME AND CLASS PACKAGE NAME AND CLASS at PACKAGE NAME AND CLASS.onBackPressed (Class.java:95) at android.app.Activity.onKeyUp (Activity.java:2465) at android.view.KeyEvent.dispatch (KeyEvent.java:2646) at android.app.Activity.dispatchKeyEvent (Activity.java:2716) at android.support.v7.internal.view.WindowCallbackWrapper.dispatchKeyEvent (WindowCallbackWrapper.java:50) at android.support.v7.app.AppCompatDelegateImplBase$AppCompatWindowCallbackBase.dispatchKeyEvent (AppCompatDelegateImplBase.java:224) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent (PhoneWindow.java:2280) at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent (ViewRootImpl.java:4038) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess (ViewRootImpl.java:4000) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3562) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3615) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3581) at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:3698) at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3589)
at android.view.ViewRootImpl$AsyncInputStage.apply (ViewRootImpl.java:3755) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3562) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3615) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3581) at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3589) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3562) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3615) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3581) at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:3731) at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent (ViewRootImpl.java:3892) at android.view.inputmethod.InputMethodManager$PendingEvent.run (InputMethodManager.java:2208) at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback (InputMethodManager.java:1849) at android.view.inputmethod.InputMethodManager.finishedInputEvent (InputMethodManager.java:1840) at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished (InputMethodManager.java:2185) at android.view.InputEventSender.dispatchInputEventFinished (InputEventSender.java:141) at android.os.MessageQueue.nativePollOnce (Native Method) at android.os.MessageQueue.next (MessageQueue.java:143) at android.os.Looper.loop (Looper.java:122) at android.app.ActivityThread.main (ActivityThread.java:5254) at java.lang.reflect.Method.invoke (Native Method) at java.lang.reflect.Method.invoke (Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:902) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:697)

这里是用户的场景

Activity A --> 打开 Activity B--> 用户在 Activity 中按下后退按钮 B--> 在按下后退按钮时,监听器是发送到 Activity A --> 然后调用显示的对话框。

我通常更喜欢使用 DialogFragment 而不是您尝试的方法,以减少重复。这是一个带有自定义布局的 DialogFragment 示例,我称之为 R.layout.fragment_alert_dialog:

public class AlertDialogFragment extends DialogFragment {

    private static final String ARG_TITLE = "title";
    private static final String ARG_MESSAGE = "message";

    private String title;
    private String message;
    boolean endSuccess = false;

    private AlertFinishedDialogListener mListener;

    public AlertDialogFragment() {
    }

    public static AlertDialogFragment newInstance(String title, String message) {
        AlertDialogFragment fragment = new AlertDialogFragment();
        Bundle args = new Bundle();
        args.putString(ARG_TITLE, title);
        args.putString(ARG_MESSAGE, message);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            title = getArguments().getString(ARG_TITLE);
            message = getArguments().getString(ARG_MESSAGE);
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle saveIntsanceState){

        final Context context = getActivity();

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

        LayoutInflater inflater = getActivity().getLayoutInflater();

        View rootView = inflater.inflate(R.layout.fragment_alert_dialog, null, false);
        final TextView titleView = (TextView)rootView.findViewById(R.id.tvTitle);
        final TextView messView = (TextView)rootView.findViewById(R.id.tvMessage);

        titleView.setText(title);
        messView.setText(message);

        builder.setView(rootView)
//                .setTitle(title)
                .setPositiveButton(R.string.ok_button_dialog_title, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                        endSuccess = true;
                        if(mListener == null) mListener = (AlertFinishedDialogListener) context;
                        mListener.onAlertFinishedDialog();
                    }
                });
        return builder.create();
    }


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            if(mListener == null) mListener = (AlertFinishedDialogListener) context;
        }
        catch (Exception ex){
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    public interface AlertFinishedDialogListener {
        void onAlertFinishedDialog();
    }
}

它包含一个 Listener 以防万一您需要在 DialogFragment 完成时得到通知。

首先你需要实现回调: implements AlertDialogFragment.AlertFinishedDialogListener{

为了调用 AlertDialogFragment,您可以从 Activity 执行此操作(如有必要,也可以 Fragment)。

private void startAlertDialogFragment(String title, String mess){
    AlertDialogFragment alert = AlertDialogFragment.newInstance(title, mess);
    alert.show(getFragmentManager(), "alertDialogFragment132");
}

@Override
public void onAlertFinishedDialog() {
    Log.e(TAG, "onAlertFinishedDialog");
}

Problem

您只能显示来自 activity 上下文的对话框。 除了 TYPE_SYSTEM_ALERT or TYPE_APPLICATION_OVERLAY,如果您的应用不向用户显示紧急通知,则不推荐这样做。

Solution

如果您有 activity 上下文可用,那么您可以显示来自任何 class 的对话,例如 servicebroadcast receiver,甚至您想象的任何 class .

这是我的解决方法,可以像我说的那样显示来自任何 class 的对话框。

Here is a snippet what i do to show dialog from any class. (Could it be more simpler!)

import android.app.Dialog;
import android.content.DialogInterface;

public class SampleClass {
    void anyMethod() {
        Dialog dialog = ApplicationContext.getInstance().showDialog("title", "yourMessage", "Cancel", "Ok", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO: handle button 1 clicked 
            }
        }, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO: handle button 2 clicked
            }
        });
    }
}

现在您将执行此操作。

1.创建您将在 android 中注册的应用程序 class manifest application tag

  <application
    android:name=".ApplicationContext"
    ...
    >
      ...
  </application>

2。在此应用程序 class 中,您将持有实时 activity 对象。这对于显示对话框更有用。

ApplicationContext.java

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Application;
import android.content.DialogInterface;


public class ApplicationContext extends Application {
    private static ApplicationContext mInstance;
    private Activity liveActivity;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        mInstance = null;
    }

    public static synchronized ApplicationContext getInstance() {
        return mInstance;
    }

    public Activity getLiveActivity() {
        return liveActivity;
    }

    public void setLiveActivity(Activity liveActivity) {
        this.liveActivity = liveActivity;
    }

    /*
     * Show Dialog with Title, Message, Button1, Button2 with Button1 and Button2 Listener
     */
    public AlertDialog showDialog(String title, String msg,
                                  String btn1, String btn2,
                                  DialogInterface.OnClickListener listener1,
                                  DialogInterface.OnClickListener listener2) {
        if (liveActivity == null) return null;
        AlertDialog.Builder builder = new AlertDialog.Builder(liveActivity)
                .setTitle(title)
                .setMessage(msg)
                .setCancelable(false)
                .setPositiveButton(btn1, listener1);
        if (btn2 != null)
            builder.setNegativeButton(btn2, listener2);

        AlertDialog alert = builder.create();
        alert.show();
        return alert;
    }
}

再走一步

3。您将通过此基数 activity class 扩展您的所有 activity(如果您已有基数,则可以编辑基数 activity。)

import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onResume() {
        super.onResume();
        ApplicationContext.getInstance().setLiveActivity(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        ApplicationContext.getInstance().setLiveActivity(null);
    }
}

给你!!!

您尝试在单独的 class 中构建 AlertDialog 时遇到的问题是您正在传递 AlertDialog Context [=15] =].您收到错误是因为 AlertDialog 需要来自具有布局的 ActivityWindowManager,而不是 Context。这是因为 Activit 扩展了 Context... 而不是相反。

为了使您的代码正常工作,您需要提供 AlertDialog.BuilderActivity 的访问权限。所以把你的代码改成这样:

public class TestDialog {

    private static final String TAG = TestDialog.class.getSimpleName();

    Activity mActivity;

    public TestDialog(Activity activity){
        mActivity = activity;
    }


    public void showDialog(){
        AlertDialog.Builder b = new AlertDialog.Builder(mActivity);
        b.setTitle("Title");
        b.setMessage("message");
        b.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.e(TAG, "showDialog : onClick");
            }
        });
        b.create().show();
    }
}

现在您可以从 Activity 调用 AlertDialog 让我们说在这种情况下 MainActivity 像这样:

TestDialog testDialog = new TestDialog(MainActivity.this);
testDialog.showDialog();

我没有在 Fragment 上尝试过,所以我不知道这是否适用于 Fragment,或者您是否会继续遇到某些设备的问题。出于这些原因,我(和 Google!)仍然强烈建议您改用 DialogFragment,因为它是专门为这种情况设计的。查看 Google 文档。:

https://developer.android.com/guide/topics/ui/dialogs