恍然大悟污秆,Java
萬(wàn)物皆是對(duì)象的真諦筝尾,當(dāng)然Android
也不列外丙唧,其實(shí)我們?cè)趯懗绦虻臅r(shí)候也是在給寫每一個(gè)對(duì)象伟阔; 所以我們?cè)?strong>Android Studio中所看到的Java源碼也是一個(gè)個(gè)對(duì)象的封裝體辣之;
一時(shí)如蒙雷擊,我們看源碼也是如此皱炉;
- 帶著問(wèn)題進(jìn)入
- 如果是我寫我該如何寫怀估?
- 每一個(gè)方法是如何調(diào)用?
即然如此我們便能無(wú)需太多的教學(xué)文檔合搅,就能駕馭Android
中的基本用法多搀。還能學(xué)到一些設(shè)計(jì)模式和一些寫代碼的技巧;
這是我第一次看試著把自己看的東西寫成博客灾部。寫得不好請(qǐng)觀者見(jiàn)諒康铭;
Dialog繼承結(jié)構(gòu)
創(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
可以看老羅文章:
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ì)模式;里面包含message
和handle
的用法丈秩,所以還是有許多可以學(xué)習(xí)的盯捌。
推薦: