Dialog源碼學(xué)習(xí)

恍然大悟污秆,Java 萬(wàn)物皆是對(duì)象的真諦筝尾,當(dāng)然Android也不列外丙唧,其實(shí)我們?cè)趯懗绦虻臅r(shí)候也是在給寫每一個(gè)對(duì)象伟阔; 所以我們?cè)?strong>Android Studio中所看到的Java源碼也是一個(gè)個(gè)對(duì)象的封裝體辣之;

一時(shí)如蒙雷擊,我們看源碼也是如此皱炉;

  1. 帶著問(wèn)題進(jìn)入
  2. 如果是我寫我該如何寫怀估?
  3. 每一個(gè)方法是如何調(diào)用?

即然如此我們便能無(wú)需太多的教學(xué)文檔合搅,就能駕馭Android中的基本用法多搀。還能學(xué)到一些設(shè)計(jì)模式和一些寫代碼的技巧;

這是我第一次看試著把自己看的東西寫成博客灾部。寫得不好請(qǐng)觀者見(jiàn)諒康铭;

Dialog繼承結(jié)構(gòu)

UML建模圖

創(chuàng)建
創(chuàng)建 - 構(gòu)造方法 - 重點(diǎn)

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (themeResId == 0) {
                final TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
                themeResId = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, themeResId);
        } else {
            mContext = context;
        }
        //獲取到窗口管理器
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        // 構(gòu)建顯示時(shí)所用的Window;
        final Window w = new PhoneWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);

        mListenersHandler = new ListenersHandler(this);
    }

解說(shuō)一下重要的代碼

 // 1.獲取Window管理器
 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
 //2.創(chuàng)建一個(gè) Windows 來(lái)供 Dialog 掛載布局
 final Window w = new PhoneWindow(mContext);
// 3.設(shè)置Window和 窗口管理器關(guān)聯(lián)
w.setWindowManager(mWindowManager, null, null);
// 4.
mListenersHandler = new ListenersHandler(this);

只要簡(jiǎn)單的3步就完成了Dialog和布局所顯示的關(guān)聯(lián)赌髓。
WindowManager是用來(lái)干什么的呢从藤?
其實(shí)和我們的Activity一樣催跪,我們需要寫XML布局,然后通過(guò)LayoutInflater轉(zhuǎn)化為View設(shè)置給Acvitity是一樣的呛哟。我們都需要一個(gè)顯示的窗口;在Android里面就叫window;

簡(jiǎn)單來(lái)說(shuō)Window是抽象類匿沛,具體實(shí)現(xiàn)是PhoneWindow扫责,通過(guò)WindowManager就可以創(chuàng)建Window。WindowManager是外界訪問(wèn)Window的入口逃呼,但是Window的具體實(shí)現(xiàn)是在WindowManagerService中鳖孤,WindowManager和WindowManagerService的交互是一個(gè)IPC過(guò)程。所有的視圖例如Activity抡笼、Dialog苏揣、Toast都是附加在Window上的。

Window w = new PhoneWindow(mContext);
具體關(guān)系如下圖:

Window圖

需要深入了解Window可以看老羅文章:
Android應(yīng)用程序窗口(Activity)的窗口對(duì)象(Window)的創(chuàng)建過(guò)程分析

顯示

在寫碼的時(shí)候發(fā)現(xiàn)一個(gè)問(wèn)題推姻,我自己繼承寫了一個(gè)dialog

 MyDialog dialog = new MyDialog(context, R.style.default_dialog_style);

為什么不會(huì)調(diào)用生命周期中的OnCreate()方法平匈?一直以為在構(gòu)建的時(shí)候就應(yīng)該創(chuàng)建。后來(lái)才發(fā)現(xiàn)自己理解錯(cuò)誤藏古,要調(diào)用show()方法才會(huì)顯示增炭。如下

 public void show() {

        // 1.判斷是否顯示
   
     ··· 部分代碼
    
        mCanceled = false;  
        // 2.調(diào)用OnCreate(); 
        if (!mCreated) {
            dispatchOnCreate(null);
        }
        // 3.啟動(dòng)
        onStart();
       // 4. 獲取到Window跟布局
        mDecor = mWindow.getDecorView();
        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
            final ApplicationInfo info = mContext.getApplicationInfo();
            mWindow.setDefaultIcon(info.icon);
            mWindow.setDefaultLogo(info.logo);
            mActionBar = new WindowDecorActionBar(this);
        }

        ··· 部分代碼 -- 設(shè)置布局參數(shù)
        try {
         // 5. 添加 布局到 布局管理器 中 
            mWindowManager.addView(mDecor, l);
            mShowing = true;
            // 調(diào)用 show()示監(jiān)聽(tīng)回調(diào)
            sendShowMessage();
        } finally {
        }
    }

可以看到第2條中如果如果 mCreated=false才會(huì)回調(diào)dispatchOnCreate()繼續(xù)深入:

 void dispatchOnCreate(Bundle savedInstanceState) {
        if (!mCreated) {
            onCreate(savedInstanceState);
            mCreated = true;
        }
    }

// 空實(shí)現(xiàn)
 protected void onCreate(Bundle savedInstanceState) {
    }

由上代碼可以看到onCreate是空實(shí)現(xiàn),我們?cè)谧约憾xDialog時(shí)復(fù)寫onCreate()才會(huì)有實(shí)際的作用拧晕;

接著看OnStart(); 也無(wú)太多實(shí)際作用隙姿。設(shè)置顯示mActionBar動(dòng)畫;

    /**
     * Called when the dialog is starting.
     */
    protected void onStart() {
        if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
    }

那么我們是在哪里設(shè)置布局的? 如下厂捞;布局是設(shè)置在mWindow中的输玷;

    public void setContentView(@LayoutRes int layoutResID) {
        mWindow.setContentView(layoutResID);
    }

一般我們是復(fù)寫在OnCreat()中∶夷伲或者直接dialog.setContentView(R.layout.layout_view)來(lái)設(shè)置布局欲鹏;
接著把拿到mDecor設(shè)置個(gè)布局管理器中。就是源碼中的第5條臭墨。那么我們寫得布局就顯示在我們的程序上面了貌虾。由于我們直接加載window。因而我們可以直接顯示在所以布局的頂部裙犹;

關(guān)閉對(duì)話框

所有對(duì)話框都實(shí)現(xiàn)了一個(gè)接口 DialogInterface

public interface DialogInterface{

... 省略 ...

    public void cancel();
    public void dismiss();
    
... 省略 ...

}

Dialog中還有一個(gè)方法hide()尽狠;所以讓一個(gè)對(duì)話框消失有三種方法;我們來(lái)看看有什么不同:

hide() ---- 只是讓布局看不到叶圃,但是沒(méi)有關(guān)閉它袄膏;并沒(méi)有移除屏幕

    public void hide() {
        if (mDecor != null) {
            mDecor.setVisibility(View.GONE);
        }
    }

dismiss

 @Override
    public void dismiss() {
        // 判斷是否在同一線程 
        if (Looper.myLooper() == mHandler.getLooper()) {
            dismissDialog();
        } else {
            //不在同一線程掺冠。發(fā)送關(guān)閉的mHandler來(lái)關(guān)閉對(duì)話框
            mHandler.post(mDismissAction);
        }
    }

// 銷毀對(duì)話框
 void dismissDialog() {
        if (mDecor == null || !mShowing) {
            return;
        }

        if (mWindow.isDestroyed()) {
            return;
        }

        // 1. 移除 WindowManger 中 Decor
        try {
            mWindowManager.removeViewImmediate(mDecor);
        } finally {
            //2. 銷毀必要組件沉馆, 
            if (mActionMode != null) {
                mActionMode.finish();
            }
            mDecor = null;
            mWindow.closeAllPanels();
            //3. 回調(diào) onStop方法
            onStop();
            mShowing = false;

            // 4. 發(fā)送 銷毀 通知的監(jiān)聽(tīng) 最后介紹:
            sendDismissMessage();
        }
    }

注意:官方文檔上同時(shí)提到了一點(diǎn)注意事項(xiàng):如果你要進(jìn)行一些清理工作的話码党,不要在重寫dismiss函數(shù),而應(yīng)該在onStop函數(shù)中進(jìn)行這些清理工作

cancel 和 dismiss 區(qū)別

這樣看下來(lái)斥黑。除了hide()和其他兩個(gè)方法有點(diǎn)區(qū)別揖盘,其他兩個(gè)好像就沒(méi)有太大區(qū)別了:先說(shuō)結(jié)論吧:
Hide()只是隱藏對(duì)話框;dismiss()就是關(guān)閉并結(jié)束對(duì)話框的方法锌奴;
cancel()如果沒(méi)有設(shè)置setOnCancelListener()才會(huì)和dismiss()有所不同兽狭;
Dialog內(nèi)部有這樣一段代碼


// 設(shè)置打開(kāi)關(guān)閉監(jiān)聽(tīng)
    private static final class ListenersHandler extends Handler {
        private WeakReference<DialogInterface> mDialog;

        public ListenersHandler(Dialog dialog) {
            mDialog = new WeakReference<DialogInterface>(dialog);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case DISMISS:
                    ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
                    break;
                case CANCEL:
                    ((OnCancelListener) msg.obj).onCancel(mDialog.get());
                    break;
                case SHOW:
                    ((OnShowListener) msg.obj).onShow(mDialog.get());
                    break;
            }
        }
    }

如上代碼所理解,如果你在使用Dialog設(shè)置了相應(yīng)的回調(diào)監(jiān)聽(tīng)鹿蜀,會(huì)回調(diào)相應(yīng)的監(jiān)聽(tīng)方法箕慧;
我們?cè)趤?lái)看看cancel方法:

    public void cancel() {
        if (!mCanceled && mCancelMessage != null) {
            mCanceled = true;
            // Obtain a new message so this dialog can be re-used
            Message.obtain(mCancelMessage).sendToTarget();
        }
        //回調(diào)dismis方法; 
        dismiss();
    }

如果你設(shè)置了回調(diào)監(jiān)聽(tīng)

  public void setOnCancelListener(final OnCancelListener listener)

上面可以得出的結(jié)論cancel()包含dismiss()茴恰。cancel()會(huì)發(fā)送關(guān)閉的消息通知ListenersHandler去回調(diào)相應(yīng)的監(jiān)聽(tīng)颠焦;

Dialog的源碼大概就是如此了,其實(shí)仔細(xì)回想下來(lái)還是非常簡(jiǎn)單的往枣,就是對(duì)源碼的包裝伐庭,window的加載而已。難的是加載和顯示的過(guò)程分冈。這些我就不去深入研究了似忧。
下一篇準(zhǔn)備些Dialog用到的設(shè)計(jì)模式;里面包含messagehandle的用法丈秩,所以還是有許多可以學(xué)習(xí)的盯捌。

推薦:

一個(gè)不錯(cuò)Dialog開(kāi)源項(xiàng)目

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蘑秽,隨后出現(xiàn)的幾起案子饺著,更是在濱河造成了極大的恐慌,老刑警劉巖肠牲,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幼衰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缀雳,警方通過(guò)查閱死者的電腦和手機(jī)渡嚣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)肥印,“玉大人识椰,你說(shuō)我怎么就攤上這事∩罴睿” “怎么了腹鹉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)敷硅。 經(jīng)常有香客問(wèn)我功咒,道長(zhǎng)愉阎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任力奋,我火速辦了婚禮榜旦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘景殷。我一直安慰自己溅呢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布滨彻。 她就那樣靜靜地躺著藕届,像睡著了一般挪蹭。 火紅的嫁衣襯著肌膚如雪亭饵。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天梁厉,我揣著相機(jī)與錄音辜羊,去河邊找鬼。 笑死词顾,一個(gè)胖子當(dāng)著我的面吹牛八秃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼经伙!你這毒婦竟也來(lái)了备籽?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤俗慈,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體腋颠,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年吓笙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了淑玫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡面睛,死狀恐怖絮蒿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叁鉴,我是刑警寧澤歌径,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站亲茅,受9級(jí)特大地震影響回铛,放射性物質(zhì)發(fā)生泄漏狗准。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一茵肃、第九天 我趴在偏房一處隱蔽的房頂上張望腔长。 院中可真熱鬧,春花似錦验残、人聲如沸捞附。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸟召。三九已至,卻和暖如春氨鹏,著一層夾襖步出監(jiān)牢的瞬間欧募,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工仆抵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跟继,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓镣丑,卻偏偏與公主長(zhǎng)得像舔糖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子莺匠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容