摘要:下面接著分析源碼分析的構造方法如下所示看源碼可知在的構造方法中直接直接構造了一個,并賦值給的成員變量,從這里可以看出其實和的顯示邏輯都是類似的,都是通過對應的變量來實現窗口的加載與顯示的。
目錄介紹
1.簡單用法
2.AlertDialog源碼分析
2.1 AlertDialog.Builder的構造方法
2.2 通過AlertDialog.Builder對象設置屬性
2.3 builder.create方法
2.4 看看create方法中的P.apply(dialog.mAlert)源碼
2.5 看看AlertDialog的show方法
3.Dialog源碼分析
3.1 Dialog的構造方法
3.2 Dialog生命周期
3.3 Dialog中show方法展示彈窗
3.4 Dialog的dismiss銷毀彈窗
4.Dialog彈窗問題分析
5.Dialog彈窗總結
好消息博客筆記大匯總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug匯總,當然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計47篇[近20萬字],轉載請注明出處,謝謝!
鏈接地址:https://github.com/yangchong2...
如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起于忽微,量變引起質變!
DialogFragment封裝庫項目地址:https://github.com/yangchong2...
02.Toast源碼深度分析
最簡單的創建,簡單改造避免重復創建,show()方法源碼分析,scheduleTimeoutLocked吐司如何自動銷毀的,TN類中的消息機制是如何執行的,普通應用的Toast顯示數量是有限制的,用代碼解釋為何Activity銷毀后Toast仍會顯示,Toast偶爾報錯Unable to add window是如何產生的,Toast運行在子線程問題,Toast如何添加系統窗口的權限等等
03.DialogFragment源碼分析
最簡單的使用方法,onCreate(@Nullable Bundle savedInstanceState)源碼分析,重點分析彈窗展示和銷毀源碼,使用中show()方法遇到的IllegalStateException分析
05.PopupWindow源碼分析
顯示PopupWindow,注意問題寬和高屬性,showAsDropDown()源碼,dismiss()源碼分析,PopupWindow和Dialog有什么區別?為何彈窗點擊一下就dismiss呢?
06.Snackbar源碼分析
最簡單的創建,Snackbar的make方法源碼分析,Snackbar的show顯示與點擊消失源碼分析,顯示和隱藏中動畫源碼分析,Snackbar的設計思路,為什么Snackbar總是顯示在最下面
07.彈窗常見問題
DialogFragment使用中show()方法遇到的IllegalStateException,什么常見產生的?Toast偶爾報錯Unable to add window,Toast運行在子線程導致崩潰如何解決?
08.Builder模式
Builder模式使用場景,簡單案例,Builder模式實際案例Demo展示,看看AlertDialog.Builder源代碼如何實現,為什么AlertDialog要使用builder模式呢?builder模式優缺點分析。關于builder模式經典的案例可以參考我的彈窗封裝庫:https://github.com/yangchong2...
1.簡單用法一般都是在使用AlertDialog,但AlertDialog主要也是繼承自Dialog。下面我們來分析分析Dialog源碼。
最簡單用法如下所示
private AlertDialog alertDialog=null; public void showDialog(){ AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setIcon(R.mipmap.ic_launcher); builder.setMessage("瀟湘劍雨"); builder.setTitle("這個是標題"); builder.setView(R.layout.activity_main); builder.setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { alertDialog.dismiss(); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { alertDialog.dismiss(); } }); alertDialog = builder.create(); alertDialog.show(); }2.AlertDialog源碼分析 2.1 AlertDialog.Builder的構造方法
先來看一下AlertDialog.Builder的構造方法,這里的Builder是AlertDialog的內部類,用于封裝AlertDialog的構造過程,看一下Builder的構造方法:
public Builder(Context context) { this(context, resolveDialogTheme(context, 0)); }
然后調用的是Builder的重載構造方法:
public Builder(Context context, int themeResId) { P = new AlertController.AlertParams(new ContextThemeWrapper( context, resolveDialogTheme(context, themeResId))); }
接著這里的P是AlertDialog.Builder中的一個AlertController.AlertParams類型的成員變量,可見在這里執行了P的初始化操作。
public AlertParams(Context context) { mContext = context; mCancelable = true; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); }
可以看到這里主要執行了AlertController.AlertParams的初始化操作,初始化了一些成員變量。這樣執行了一系列操作之后我們的代碼:
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);2.2 通過AlertDialog.Builder對象設置屬性
調用了builder.setIcon方法,這里看一下setIcon方法的具體實現:
可以看到AlertDialog的Builder的setIcon方法,這里執行的就是給類型為AlertController.AlertParams的P的mIconId賦值為傳遞的iconId,并且這個方法返回的類型就是Builder。
public Builder setIcon(@DrawableRes int iconId) { P.mIconId = iconId; return this; }
調用了builder.setMessage方法,可以看一下builder.setMessage方法的具體實現:
跟setIcon方法的實現邏輯類似,都是給成員變量的mMessage賦值為我們傳遞的Message值,且和setIcon方法類似的,這個方法返回值也是Builder。
public Builder setMessage(CharSequence message) { P.mMessage = message; return this; }
然后看一下builder.setTitle方法:
發現builder的setIcon、setMessage、setTitle等方法都是給Builder的成員變量P的icon,message,title賦值。
public Builder setTitle(CharSequence title) { P.mTitle = title; return this; }
接著看一下builder.setView方法:
發現這里的setView和setIcon,setMessage,setTitle等方法都是類似的,都是將我們傳遞的數據值賦值給Builder的成員變量P。
public Builder setView(int layoutResId) { P.mView = null; P.mViewLayoutResId = layoutResId; P.mViewSpacingSpecified = false; return this; }2.3 builder.create方法
然后調用了builder.create方法,并且這個方法返回了AlertDialog。
可以看到這里首先構造了一個AlertDialog,我們可以看一下這個構造方法的具體實現:
可以看到這里首先調用了super的構造方法,而我們的AlertDialog繼承于Dialog,所以這里執行的就是Dialog的構造方法【備注:高版本是繼承AppCompatDialog,然后AppCompatDialog再繼承Dialog】
回到AlertDialog的構造方法中,在構造方法中,除了調用Dialog的構造方法之外還執行了
相當于初始化了AlertDiaog的成員變量mAlert。
mAlert = new AlertController(getContext(), this, getWindow());2.4 看看create方法中的P.apply(dialog.mAlert)源碼
再AlertDialog.Builder.create方法,在創建了一個AlertDialog之后,又執行了P.apply(dialog.mAlert);這里的P是一個AlertController.AlertParams的變量,而dialog.mAlert是剛剛創建的AlertDialog中的一個AlertController類型的變量,來看一下apply方法的具體實現:
在初始化AlertDialog.Builder的時候設置的icon、title、message賦值給了AlertController.AlertParams,這里就是將初始化時候設置的屬性值賦值給我們創建的Dialog對象的mAlert成員變量
ublic void apply(AlertController dialog) { if (mCustomTitleView != null) { dialog.setCustomTitle(mCustomTitleView); } else { if (mTitle != null) { dialog.setTitle(mTitle); } if (mIcon != null) { dialog.setIcon(mIcon); } if (mIconId != 0) { dialog.setIcon(mIconId); } if (mIconAttrId != 0) { dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId)); } } if (mMessage != null) { dialog.setMessage(mMessage); } if (mPositiveButtonText != null) { dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText, mPositiveButtonListener, null); } if (mNegativeButtonText != null) { dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText, mNegativeButtonListener, null); } if (mNeutralButtonText != null) { dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText, mNeutralButtonListener, null); } if (mForceInverseBackground) { dialog.setInverseBackgroundForced(true); } // For a list, the client can either supply an array of items or an // adapter or a cursor if ((mItems != null) || (mCursor != null) || (mAdapter != null)) { createListView(dialog); } if (mView != null) { if (mViewSpacingSpecified) { dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom); } else { dialog.setView(mView); } } else if (mViewLayoutResId != 0) { dialog.setView(mViewLayoutResId); } }2.5 看看AlertDialog的show方法
看看如下所示,可以發現直接調用了dialog中的show方法。下面接著分析
3.Dialog源碼分析 3.1 Dialog的構造方法
如下所示
看源碼可知在Dialog的構造方法中直接直接構造了一個PhoneWindow,并賦值給Dialog的成員變量mWindow,從這里可以看出其實Dialog和Activity的顯示邏輯都是類似的,都是通過對應的Window變量來實現窗口的加載與顯示的。然后我們執行了一些Window對象的初始化操作,比如設置回調函數為本身,然后調用Window類的setWindowManager方法,并傳入了WindowManager。然后創建一個對話框監聽handler對象。
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; } //創建一個Context mContext = new ContextThemeWrapper(context, themeResId); } else { mContext = context; } //獲取一個WindowManager對象 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); //創建一個Window對象 final Window w = new PhoneWindow(mContext); //將Window對象w賦值給mWindow mWindow = w; //為Windowd對象設置回調,并且它本身實現了這些回調函數 w.setCallback(this); w.setOnWindowDismissedCallback(this); //為Window對象設置WindowManager對象 w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); //創建一個對話框監聽Handler mListenersHandler = new ListenersHandler(this); }
接著看看w.setWindowManager(mWindowManager, null, null)里面的源代碼
可以看到跟Activity的Window對象的windowManager的獲取方式是相同的,都是通過new的方式創建一個新的WindowManagerImpl對象。
3.2 Dialog生命周期
dialog的生命周期如下所示
/** * 類似于Activity的onCreate函數,可以在這個方法中進行Dialog的一些初始化操作
*/ protected void onCreate(Bundle savedInstanceState) { } /** * 當對話框啟動的時候被調用. */ protected void onStart() { } /** * 當對話框停止的時候被調用. */ protected void onStop() { } ```3.3 Dialog中show方法展示彈窗
源碼如下所示,關于重點的邏輯,我在這里只是簡單的注釋了一下。
public void show() { //首先判斷對話框是否顯示 if (mShowing) { if (mDecor != null) { if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR); } mDecor.setVisibility(View.VISIBLE); } return; } mCanceled = false; /* 判斷對話框是否創建過,如果沒有創建過
*/ if (!mCreated) { dispatchOnCreate(null); } //回調onStart函數 onStart(); //獲取Window對象總的DecorView,如果調用了setContentView就會創建DecorView 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); } WindowManager.LayoutParams l = mWindow.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) { WindowManager.LayoutParams nl = new WindowManager.LayoutParams(); nl.copyFrom(l); nl.softInputMode |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; l = nl; } try { //將DecorView添加到WindowManager中,這些就會顯示了 mWindowManager.addView(mDecor, l); //將mShowing置為true mShowing = true; sendShowMessage(); } finally { } } ```
方法體的內容比較多,由于一開始mShowing變量用于表示當前dialog是否正在顯示,由于我們剛剛開始調用執行show方法,所以這里的mShowing變量的值為false,所以if分支的內容不會被執行,繼續往下看:
if (!mCreated) { dispatchOnCreate(null); }
mCreated這個控制變量控制dispatchOnCreate方法只被執行一次,由于是第一次執行,所以這里會執行dispatchOnCreate方法,好吧,看一下dispatchOnCreate方法的執行邏輯:
可以看到代碼的執行邏輯很簡單就是回調了Dialog的onCreate方法
void dispatchOnCreate(Bundle savedInstanceState) { if (!mCreated) { onCreate(savedInstanceState); mCreated = true; } }
調用了onStart方法,這個方法主要用于設置ActionBar,這里不做過多的說明,然后初始化WindowManager.LayoutParams對象,并最終調用我們的mWindowManager.addView()方法。
protected void onStart() { if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true); }
最后調用了sendShowMessage方法,可以看一下這個方法的實現:
那么發送這個消息主要是什么作用呢,逗比們,接著往下看看。
private void sendShowMessage() { if (mShowMessage != null) { // Obtain a new message so this dialog can be re-used Message.obtain(mShowMessage).sendToTarget(); } }
這里會發送一個Dialog已經顯示的異步消息,該消息最終會在ListenersHandler中的handleMessage方法中被執行:
private static final class ListenersHandler extends Handler { private WeakReferencemDialog; public ListenersHandler(Dialog dialog) { mDialog = new WeakReference (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; } } }
由于我們的msg.what = SHOW,所以會執行OnShowListener.onShow方法,那么這個OnShowListener是何時賦值的呢?還記得我們構造AlertDialog.Builder么?
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { } });
這樣就為我們的AlertDialog.Builder設置了OnShowListener,可以看一下setOnShowListener方法的具體實現:
這樣就為我們的Dialog中的mListenersHandler構造了Message對象,并且在Dialog中發送showMessage的時候被mListenersHandler所接收。
public void setOnShowListener(OnShowListener listener) { if (listener != null) { mShowMessage = mListenersHandler.obtainMessage(SHOW, listener); } else { mShowMessage = null; } }3.4 Dialog的dismiss銷毀彈窗
調用alertDialog.cancel()或者alertDialog.dismiss()都可以達到銷毀彈窗的效果。
首先看一下Dialog的cannel方法的具體實現:
public void cancel() { if (!mCanceled && mCancelMessage != null) { mCanceled = true; // Obtain a new message so this dialog can be re-used Message.obtain(mCancelMessage).sendToTarget(); } dismiss(); }
可以看到方法體中,若當前Dialog沒有取消,并且設置了取消message,則調用Message.obtain(mCancel).sendToTarget(),前面已經分析過這里的sendToTarget方法會回調注冊的異步消息處理邏輯:
public void setOnCancelListener(final OnCancelListener listener) { if (mCancelAndDismissTaken != null) { throw new IllegalStateException( "OnCancelListener is already taken by "
} if (listener != null) { mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener); } else { mCancelMessage = null; } } ```
可以看到如果在初始化AlertDialog.Builder時,設置了setOnCancelListener,那么就會執行mListenersHandler的異步消息處理,好吧,這里看一下mListenersHandler的定義:
private static final class ListenersHandler extends Handler { private WeakReferencemDialog; public ListenersHandler(Dialog dialog) { mDialog = new WeakReference (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; } } }
調用的是設置的OnCancelListener的onCancel方法,也就是說調用dialog.cancel方法時首先會判斷dialog是否調用了setOnCancelListener若設置了,則先調用OnCancelListener的onCancel方法,然后再次執行dismiss方法,若我們沒有為Dialog.Builder設置OnCancelListener那么cancel方法和dismiss方法是等效的。
如下所示
可以看到,這里首先判斷當前線程的Looper是否是主線程的Looper(由于mHandler是在主線程中創建的,所以mHandler.getLooper返回的是主線程中創建的Looper對象),若是的話,則直接執行dismissDialog()方法,否則的話,通過mHandler發送異步消息至主線程中,簡單來說就是判斷當前線程是否是主線程,若是主線程則執行dismissDialog方法否則發送異步消息
public void dismiss() { if (Looper.myLooper() == mHandler.getLooper()) { dismissDialog(); } else { mHandler.post(mDismissAction); } }
然后看一下mHandler對異步消息的處理機制,由于這里的mDismissAction是一個Runnable對象,所以這里直接看一下mDismissAction的定義:
這里的異步消息最終也是調用的dismissDialog方法
private final Runnable mDismissAction = new Runnable() { public void run() { dismissDialog(); } };
所以無論執行的cancel方法還是dismiss方法,無論方法是在主線程執行還是子線程中執行,最終調用的都是dismissDialog方法,那么就看一下dismissDialog是怎么個執行邏輯。
首先判斷當前的mDector是否為空,或者當前Dialog是否在顯示,若為空或者沒有在顯示,則直接return掉,也就是說當前我們的dialog已經不再顯示了,則不需要往下在執行。然后調用了mWindow.isDestroyed()方法,判斷Window對象是否已經被銷毀,若已經被銷毀,則直接return,并打印錯誤日志。
然后再調用了mWindowManager.removeViewImmediate(mDector),這里的mDector是Dialog窗口的根布局,看這個方法的名字應該就是Dialog去除根布局的操作了,可以看一下這個方法的具體實現。
void dismissDialog() { if (mDecor == null || !mShowing) { return; } if (mWindow.isDestroyed()) { Log.e(TAG, "Tried to dismissDialog() but the Dialog"s window was already destroyed!"); return; } try { mWindowManager.removeViewImmediate(mDecor); } finally { if (mActionMode != null) { mActionMode.finish(); } mDecor = null; mWindow.closeAllPanels(); onStop(); mShowing = false; sendDismissMessage(); } }
mWindowManager其實是WindowManagerImpl的實例,所以這里的removeViewImmediate方法應該是WindowManagerImpl中的方法,看一下它的具體實現:
@Override public void removeViewImmediate(View view) { mGlobal.removeView(view, true); }
可以發現,這里它調用了mGlobal.removeView方法,而這里的mGlobal是WindowManagerGlobal的實例,所以再看一下WIndowManagerGlobal中removeView的實現邏輯:
public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { int index = findViewLocked(view, true); View curView = mRoots.get(index).getView(); removeViewLocked(index, immediate); if (curView == view) { return; } throw new IllegalStateException("Calling with view " + view
} } ```
可以發現,這里在獲取了保存的mDector組件之后,又調用了removeViewLocked方法,在看一下這個方法的具體實現邏輯:
private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); View view = root.getView(); if (view != null) { InputMethodManager imm = InputMethodManager.getInstance(); if (imm != null) { imm.windowDismissed(mViews.get(index).getWindowToken()); } } boolean deferred = root.die(immediate); if (view != null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); } } }
看到了么,我們獲取了mDector組件的ViewRootImpl,然后調用了其的die方法,通過這個方法實現Window組件的銷毀流程。
boolean die(boolean immediate) { // Make sure we do execute immediately if we are in the middle of a traversal or the damage // done by dispatchDetachedFromWindow will cause havoc on return. if (immediate && !mIsInTraversal) { doDie(); return false; } if (!mIsDrawing) { destroyHardwareRenderer(); } else { Log.e(TAG, "Attempting to destroy the window while drawing! " + " window=" + this + ", title=" + mWindowAttributes.getTitle()); } mHandler.sendEmptyMessage(MSG_DIE); return true; }
可以看到在方法體中有調用了doDie方法,看名字應該就是真正執行window銷毀工作的方法了,我們在看一下doDie方法的具體實現:
void doDie() { checkThread(); if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface); synchronized (this) { if (mRemoved) { return; } mRemoved = true; if (mAdded) { dispatchDetachedFromWindow(); } if (mAdded && !mFirst) { destroyHardwareRenderer(); if (mView != null) { int viewVisibility = mView.getVisibility(); boolean viewVisibilityChanged = mViewVisibility != viewVisibility; if (mWindowAttributesChanged || viewVisibilityChanged) { // If layout params have been changed, first give them // to the window manager to make sure it has the correct // animation info. try { if ((relayoutWindow(mWindowAttributes, viewVisibility, false) & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { mWindowSession.finishDrawing(mWindow); } } catch (RemoteException e) { } } mSurface.release(); } } mAdded = false; } WindowManagerGlobal.getInstance().doRemoveView(this); }
可以看到方法體中,首先調用了checkThread方法,判斷當前執行代碼的線程,若不是主線程,則拋出異常:
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
順著doDie的方法往下看,又調用了dispatchDetachedFromWindow()方法,這個方法主要是銷毀Window中的各中成員變量,臨時變量等
void dispatchDetachedFromWindow() { if (mView != null && mView.mAttachInfo != null) { mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false); mView.dispatchDetachedFromWindow(); } mAccessibilityInteractionConnectionManager.ensureNoConnection(); mAccessibilityManager.removeAccessibilityStateChangeListener( mAccessibilityInteractionConnectionManager); mAccessibilityManager.removeHighTextContrastStateChangeListener( mHighContrastTextManager); removeSendWindowContentChangedCallback(); destroyHardwareRenderer(); setAccessibilityFocus(null, null); mView.assignParent(null); mView = null; mAttachInfo.mRootView = null; mSurface.release(); if (mInputQueueCallback != null && mInputQueue != null) { mInputQueueCallback.onInputQueueDestroyed(mInputQueue); mInputQueue.dispose(); mInputQueueCallback = null; mInputQueue = null; } if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); mInputEventReceiver = null; } try { mWindowSession.remove(mWindow); } catch (RemoteException e) { } // Dispose the input channel after removing the window so the Window Manager // doesn"t interpret the input channel being closed as an abnormal termination. if (mInputChannel != null) { mInputChannel.dispose(); mInputChannel = null; } mDisplayManager.unregisterDisplayListener(mDisplayListener); unscheduleTraversals(); }
可以看到在方法中調用了mView.dispatchDetachedFromWindow方法,這個方法的作用就是將mView從Window中detach出來,可以看一下這個方法的具體實現:
void dispatchDetachedFromWindow() { AttachInfo info = mAttachInfo; if (info != null) { int vis = info.mWindowVisibility; if (vis != GONE) { onWindowVisibilityChanged(GONE); } } onDetachedFromWindow(); onDetachedFromWindowInternal(); InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { imm.onViewDetachedFromWindow(this); } ListenerInfo li = mListenerInfo; final CopyOnWriteArrayListlisteners = li != null ? li.mOnAttachStateChangeListeners : null; if (listeners != null && listeners.size() > 0) { // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. for (OnAttachStateChangeListener listener : listeners) { listener.onViewDetachedFromWindow(this); } } if ((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0) { mAttachInfo.mScrollContainers.remove(this); mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED; } mAttachInfo = null; if (mOverlay != null) { mOverlay.getOverlayView().dispatchDetachedFromWindow(); } }
其中onDetachedFromWindow方法是一個空的回調方法,這里重點看一下onDetachedFromWindowInternal方法:
protected void onDetachedFromWindowInternal() { mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT; removeUnsetPressCallback(); removeLongPressCallback(); removePerformClickCallback(); removeSendViewScrolledAccessibilityEventCallback(); stopNestedScroll(); // Anything that started animating right before detach should already // be in its final state when re-attached. jumpDrawablesToCurrentState(); destroyDrawingCache(); cleanupDraw(); mCurrentAnimation = null; }
onDetachedFromWindowInternal方法的方法體也不是特別長,都是一些調用函數,這里看一下destropDrawingCache方法,這個方法主要是銷毀View的緩存Drawing,我們來看一下具體實現:
這里的mDrawingCache其實就是一個Bitmap類型的成員變量,而這里調用的recycler和置空操作其實就是把View中執行draw方法之后緩存的bitmap清空。
這里需要說明的是,我們View組件的最終顯示落實是通過draw方法實現繪制的,而我們的draw方法的參數是一個Canvas,這是一個畫布的對象,通過draw方法就是操作這個對象并顯示出來,而Canvas對象之所以能夠實現顯示的效果是因為其內部保存著一個Bitmap對象,通過操作Canvas對象實質上是操作Canvas對象內部的Bitmap對象,而View組件的顯示也就是通過這里的Bitmap來實現的。
而我們上文中置空了bitmap對象就相當于把View組件的顯示效果置空了,就是相當于我們取消了View的draw方法的執行效果,繼續回到我們的dispatchDetachedFromWindow方法,在執行了mView.dispatchDetachedFromWindow()方法之后,又調用了mView = null;方法,這里設置mView為空,這樣我們有取消了View的meature和layouot的執行效果。
public void destroyDrawingCache() { if (mDrawingCache != null) { mDrawingCache.recycle(); mDrawingCache = null; } if (mUnscaledDrawingCache != null) { mUnscaledDrawingCache.recycle(); mUnscaledDrawingCache = null; } }5.Dialog彈窗總結
Dialog中的Window對象與Activity中的Window對象是相似的,都對應著一個WindowManager對象;Dialog和Activity的顯示邏輯是相似的都是內部管理這一個Window對象,用WIndow對象實現界面的加載與顯示邏輯。
Dialog相關的幾個類:Dialog,AlertDialog,AlertDialog.Builder,AlertController,AlertController.AlertParams,其中Dialog是窗口的父類,主要實現Window對象的初始化和一些共有邏輯,而AlertDialog是具體的Dialog的操作實現類,AlertDialog.Builder類是AlertDialog的內部類,主要用于構造AlertDialog,AlertController是AlertDialog的控制類,AlertController.AlertParams類是控制參數類;
構造AlertDialog用到了很經典的buidler構造者模式。關于buidler模式,可以參考Builder模式。構造顯示Dialog的一般流程,構造AlertDialog.Builder,然后設置各種屬性,最后調用AlertDialog.Builder.create方法獲取AlertDialog對象,并且create方法中會執行,構造AlertDialog,設置dialog各種屬性的操作。最后我們調用Dialog.show方法展示窗口,初始化Dialog的布局文件,Window對象等,然后執行mWindowManager.addView方法,開始執行繪制View的操作,并最終將Dialog顯示出來。
窗口的取消繪制流程是相似的,包括Activity和Dialog等;通過調用WindowManager.removeViewImmediate方法,開始執行Window窗口的取消繪制流程;Window窗口的取消繪制流程,通過清空bitma撤銷draw的執行效果,通過置空View撤銷meature和layout的執行效果。
關于其他內容介紹 01.關于博客匯總鏈接1.技術博客匯總
2.開源項目匯總
3.生活博客匯總
4.喜馬拉雅音頻匯總
5.其他匯總
02.關于我的博客我的個人站點:www.yczbj.org,www.ycbjie.cn
github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡書:http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽書:http://www.ximalaya.com/zhubo...
開源中國:https://my.oschina.net/zbj161...
泡在網上的日子:http://www.jcodecraeer.com/me...
郵箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV
segmentfault頭條:https://segmentfault.com/u/xi...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/71718.html
摘要:指向的主要是實現和通信的。子不能單獨存在,需附屬特定的父。系統需申明權限才能創建。和類似,同樣是通過來實現。將添加到中顯示。方法完成的顯示。執行的檢查參數等設置檢查將保存到中將保存到中。因為通過和的將無法獲取到從而導致失敗。 目錄介紹 10.0.0.1 Window是什么?如何通過WindowManager添加Window(代碼實現)?WindowManager的主要功能是什么? 1...
摘要:在代碼中的直接應用是或者是。就像一個控制器,統籌視圖的添加與顯示,以及通過其他回調方法,來與以及進行交互。創建需要通過創建,通過將加載其中,并將交給,進行視圖繪制以及其他交互。創建機制分析實例的創建中執行,從而生成了的實例。 目錄介紹 01.Window,View,子Window 02.什么是Activity 03.什么是Window 04.什么是DecorView 05.什么是Vi...
摘要:但是,最后一步,事件怎么綁定呢這塊沒有深入研究了,不過我想,應該這樣去實現也是沒有問題的。的具體做法是,把方法放到了一個叫做的組件上去實現這個功能,然后再把內容放進這個組件。其他的邏輯比如顯示隱藏之類,全部都放到組件自身上去實現。 1、Dialog組件提供什么功能,解決什么問題? zent的Dialog組件,使用姿勢是這樣的(代碼摘自zent官方文檔:https://www.youza...
摘要:分別對應于中的幾個常量值。源碼分析的方法源碼分析創建需要使用靜態的方法,并且其中的參數是一個查找父布局的起點這里可以看到,的布局是,假如我們需要自定義并且設置字體顏色,大小等屬性。表示回調已在隊列中。 目錄介紹 1.最簡單創造方法 1.1 Snackbar作用 1.2 最簡單的創建 1.3 Snackbar消失的幾種方式 2.源碼分析 2.1 Snackbar的make方...
摘要:這些依賴對象也進一步暴露了其設計思想。關鍵功能包括在上下文內掛載在上下文外掛載在上下文外共享數據。在構造必須依賴,所以可以直接創建嵌入視圖,然后手動強制執行變更檢測。提供了兩個指令和。 @angular/material 是 Angular 官方根據 Material Design 設計語言提供的 UI 庫,開發人員在開發 UI 庫時發現很多 UI 組件有著共同的邏輯,所以他們把這些共...
閱讀 1346·2023-04-25 23:47
閱讀 924·2021-11-23 09:51
閱讀 4469·2021-09-26 10:17
閱讀 3719·2021-09-10 11:19
閱讀 3265·2021-09-06 15:10
閱讀 3554·2019-08-30 12:49
閱讀 2428·2019-08-29 13:20
閱讀 1738·2019-08-28 18:14