Android對話框之Context

代碼就這麼一段:
new AlertDialog.Builder(getApplicationContext(),R.style.MyAlertDialogStyle)
        .setTitle("溫柔")
        .setMessage("不知道 不明瞭 不想要\n" +
                "爲何 個人心")
        .setPositiveButton("肯定",null)
        .setNegativeButton("取消",null)
        .show();

 

這個時候報錯了: Unable to add window -- token null is not for an applicationhtml

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:576)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:269)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
at android.app.Dialog.show(Dialog.java:289)
at android.app.AlertDialog$Builder.show(AlertDialog.java:951)
at cn.bvin.app.androidtest_asgit.MainActivity$1.onClick(MainActivity.java:24)
at android.view.View.performClick(View.java:4446)
at android.view.View$PerformClick.run(View.java:18480)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5294)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:864)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:680)
at dalvik.system.NativeStart.main(Native Method)java

 

爲何會這樣呢,看一下源碼:android

 /**
     * Create a Dialog window that uses the default dialog frame style.
     * 
     * @param context The Context the Dialog is to run it.  In particular, it
     *                uses the window manager and theme in this context to
     *                present its UI.
     */
    public Dialog(Context context) {
        this(context, 0, true);
    }

    /**
     * Create a Dialog window that uses a custom dialog style.
     * 
     * @param context The Context in which the Dialog should run. In particular, it
     *                uses the window manager and theme from this context to
     *                present its UI.
     * @param theme A style resource describing the theme to use for the 
     * window. See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style 
     * and Theme Resources</a> for more information about defining and using 
     * styles.  This theme is applied on top of the current theme in 
     * <var>context</var>.  If 0, the default dialog theme will be used.
     */
    public Dialog(Context context, int theme) {
        this(context, theme, true);
    }

    Dialog(Context context, int theme, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (theme == 0) {
                TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
                        outValue, true);
                theme = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, theme);
        } else {
            mContext = context;
        }

        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Window w = PolicyManager.makeNewWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);
        mListenersHandler = new ListenersHandler(this);
    }

看到上面public的構造方法都是重載下面那個缺省構造方法,三個形參,可是咱們看到public的構造方法裏最後的createContextThemeWrapper參數都爲truegit

Context context, int theme, boolean createContextThemeWrapper
因此這時mContext的是建立了一個ContextThemeWrapper對象,這下就明白了。

Service,Application,Broadcast都是繼承ContextWraper,只有Activity是繼承ContextThemeWrapper。
而ContextThemeWrapper又是繼承ContextWraper,ContextWraper繼承Context。

因此傳Activity.this拿到的Context是能夠建立ContextThemeWrapper對象的。
再看一下ContextThemeWrapper的源碼:
    public ContextThemeWrapper() {
        super(null);
    }
    
    public ContextThemeWrapper(Context base, int themeres) {
        super(base);
        mThemeResource = themeres;
    }

    @Override protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }

    ...

    @
    Override public void setTheme(int resid) {
        mThemeResource = resid;
        initializeTheme();
    }
ContextThemeWrapper有兩個構造函數,一個是不帶參的,一個是帶一個Context和themeRes的,上面dialog的構造方法中是經過後者來建立的Context。
固然用無構造方法也能夠建立ContextThemeWrapper對象,就如上圖,attachBaseContext方法能夠把Context傳遞進去,
setTheme則能夠把themeRes設置進去。。。

最前沿Android技術分享盡在Android技術分享社,拿起大家的手機打開微信掃一掃,關注個人公衆號就給你推薦優秀的知識文章或技術分享了!微信

相關文章
相關標籤/搜索