Dialog原始碼學習
恍然大悟,Java
萬物皆是物件的真諦,當然Android
也不列外,其實我們在寫程式的時候也是在給寫每一個物件; 所以我們在Android Studio中所看到的Java原始碼也是一個個物件的封裝體;
一時如蒙雷擊,我們看原始碼也是如此;
- 帶著問題進入
- 如果是我寫我該如何寫?
- 每一個方法是如何呼叫?
即然如此我們便能無需太多的教學文件,就能駕馭Android
中的基本用法。還能學到一些設計模式和一些寫程式碼的技巧;
這是我第一次看試著把自己看的東西寫成部落格。寫得不好請觀者見諒;
Dialog繼承結構
建立
建立 - 構造方法 - 重點
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);
// 構建顯示時所用的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);
}
解說一下重要的程式碼
// 1.獲取Window管理器
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//2.建立一個 Windows 來供 Dialog 掛載佈局
final Window w = new PhoneWindow(mContext);
// 3.設定Window和 視窗管理器關聯
w.setWindowManager(mWindowManager, null, null);
// 4.
mListenersHandler = new ListenersHandler(this);
只要簡單的3步就完成了Dialog
和佈局所顯示的關聯。WindowManager
是用來幹什麼的呢?
其實和我們的Activity
一樣,我們需要寫XML
佈局,然後通過LayoutInflater
轉化為View
設定給Acvitity
是一樣的。我們都需要一個顯示的視窗;在Android
裡面就叫window
;
簡單來說Window是抽象類,具體實現是PhoneWindow,通過WindowManager就可以建立Window。WindowManager是外界訪問Window的入口,但是Window的具體實現是在WindowManagerService中,WindowManager和WindowManagerService的互動是一個IPC過程。所有的檢視例如Activity、Dialog、Toast都是附加在Window上的。
Window w = new PhoneWindow(mContext);
具體關係如下圖:
需要深入瞭解Window
可以看老羅文章:
Android應用程式視窗(Activity)的視窗物件(Window)的建立過程分析
顯示
在寫碼的時候發現一個問題,我自己繼承
寫了一個dialog
MyDialog dialog = new MyDialog(context, R.style.default_dialog_style);
為什麼不會呼叫生命週期中的OnCreate()
方法?一直以為在構建的時候就應該建立。後來才發現自己理解錯誤,要呼叫show()
方法才會顯示。如下
public void show() {
// 1.判斷是否顯示
··· 部分程式碼
mCanceled = false;
// 2.呼叫OnCreate();
if (!mCreated) {
dispatchOnCreate(null);
}
// 3.啟動
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);
}
··· 部分程式碼 -- 設定佈局引數
try {
// 5. 新增 佈局到 佈局管理器 中
mWindowManager.addView(mDecor, l);
mShowing = true;
// 呼叫 show()示監聽回撥
sendShowMessage();
} finally {
}
}
可以看到第2條中如果如果 mCreated=false
才會回撥dispatchOnCreate()
繼續深入:
void dispatchOnCreate(Bundle savedInstanceState) {
if (!mCreated) {
onCreate(savedInstanceState);
mCreated = true;
}
}
// 空實現
protected void onCreate(Bundle savedInstanceState) {
}
由上程式碼可以看到onCreate
是空實現,我們在自己定義Dialog
時複寫onCreate()
才會有實際的作用;
接著看OnStart()
; 也無太多實際作用。設定顯示mActionBar
動畫;
/**
* Called when the dialog is starting.
*/
protected void onStart() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
}
那麼我們是在哪裡設定佈局的? 如下;佈局是設定在mWindow
中的;
public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}
一般我們是複寫在OnCreat()
中。或者直接dialog.setContentView(R.layout.layout_view)
來設定佈局;
接著把拿到mDecor
設定個佈局管理器中。就是原始碼中的第5條。那麼我們寫得佈局就顯示在我們的程式上面了。由於我們直接載入window
上。因而我們可以直接顯示在所以佈局的頂部;
關閉對話方塊
所有對話方塊都實現了一個介面 DialogInterface
public interface DialogInterface{
... 省略 ...
public void cancel();
public void dismiss();
... 省略 ...
}
Dialog
中還有一個方法hide()
;所以讓一個對話方塊消失有三種方法;我們來看看有什麼不同:
hide() ---- 只是讓佈局看不到,但是沒有關閉它;並沒有移除螢幕;
public void hide() {
if (mDecor != null) {
mDecor.setVisibility(View.GONE);
}
}
dismiss
@Override
public void dismiss() {
// 判斷是否在同一執行緒
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
//不在同一執行緒。傳送關閉的mHandler來關閉對話方塊
mHandler.post(mDismissAction);
}
}
// 銷燬對話方塊
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. 回撥 onStop方法
onStop();
mShowing = false;
// 4. 傳送 銷燬 通知的監聽 最後介紹:
sendDismissMessage();
}
}
注意:官方文件上同時提到了一點注意事項:如果你要進行一些清理工作的話,不要在重寫dismiss函式,而應該在onStop函式中進行這些清理工作
cancel 和 dismiss 區別
這樣看下來。除了hide()
和其他兩個方法有點區別,其他兩個好像就沒有太大區別了:先說結論吧:
Hide()只是隱藏對話方塊;dismiss()就是關閉並結束對話方塊的方法;
cancel()如果沒有設定setOnCancelListener()
才會和dismiss()有所不同;
在Dialog
內部有這樣一段程式碼
// 設定開啟關閉監聽
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
設定了相應的回撥監聽,會回撥相應的監聽方法;
我們在來看看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();
}
//回撥dismis方法;
dismiss();
}
如果你設定了回撥監聽
public void setOnCancelListener(final OnCancelListener listener)
上面可以得出的結論cancel()
包含dismiss()
。cancel()
會傳送關閉的訊息通知ListenersHandler
去回撥相應的監聽;
Dialog的原始碼大概就是如此了,其實仔細回想下來還是非常簡單的,就是對原始碼的包裝,window
的載入而已。難的是載入和顯示的過程。這些我就不去深入研究了。
下一篇準備些Dialog
用到的設計模式;裡面包含message
和handle
的用法,所以還是有許多可以學習的。
推薦:
相關文章
- Dialog原始碼分析原始碼
- 原始碼學習原始碼
- go原始碼學習Go原始碼
- 學習HashMap原始碼HashMap原始碼
- 【原始碼學習】ThreadLocal原始碼thread
- EventBus原始碼學習原始碼
- fishhook原始碼學習Hook原始碼
- MMKV原始碼學習原始碼
- vue原始碼學習Vue原始碼
- ObjectMapper原始碼學習ObjectAPP原始碼
- express原始碼學習Express原始碼
- Java容器原始碼學習--ArrayList原始碼分析Java原始碼
- VeraCrypt原始碼學習-序原始碼
- 精讀《原始碼學習》原始碼
- PHP 原始碼加密學習PHP原始碼加密
- Mybatis 原始碼學習(二)MyBatis原始碼
- 學習RadonDB原始碼(二)原始碼
- 學習RadonDB原始碼(一)原始碼
- java原始碼學習-AbstractSequentialListJava原始碼
- Vue 原始碼學習(一)Vue原始碼
- Okio 框架原始碼學習框架原始碼
- java原始碼學習-SpliteratorJava原始碼
- Masonry 原始碼學習整理原始碼
- jQuery原始碼學習之$()jQuery原始碼
- vue observer 原始碼學習VueServer原始碼
- 來聊聊原始碼學習原始碼
- 原始碼學習之EllipsizingTextView原始碼TextView
- EOS原始碼學習系列原始碼
- 【菜鳥讀原始碼】halo✍原始碼學習 (一)原始碼
- 直播平臺原始碼,Android中常用Dialog彈窗效果原始碼Android
- 深度學習03-sklearn.LinearRegression 原始碼學習深度學習原始碼
- spring學習:spring原始碼_BeanDefinitionSpring原始碼Bean
- umi3 原始碼學習原始碼
- redis原始碼學習之slowlogRedis原始碼
- UiAutomator原始碼學習(2)-- UiAutomationBridgeUI原始碼
- UiAutomator原始碼學習(1)-- UiDeviceUI原始碼IDEdev
- Go Gin原始碼學習(一)Go原始碼
- Retrofit原始碼學習筆記原始碼筆記
- goFrame 原始碼學習之 ServerGoFrame原始碼Server