目錄介紹
- 01.Window,View,子Window
- 02.什麼是Activity
- 03.什麼是Window
- 04.什麼是DecorView
- 05.什麼是View
- 06.關系結構圖
- 07.Window建立過程
- 08.建立機制分析
- 8.1 Activity執行個體的建立
- 8.2 Activity中Window的建立
- 8.3 DecorView的建立
彈窗系列部落格
- 01.Activity、Window、View三者關系
- 深入分析Activity、Window、View三者之間的關系
- 02.Toast源碼深度分析
- 最簡單的建立,簡單改造避免重複建立,show()方法源碼分析,scheduleTimeoutLocked吐司如何自動銷毀的,TN類中的消息機制是如何執行的,普通應用的Toast顯示數量是有限制的,用代碼解釋為何Activity銷毀後Toast仍會顯示,Toast偶爾報錯Unable to add window是如何産生的,Toast運作在子線程問題,Toast如何添加系統視窗的權限等等
- 03.DialogFragment源碼分析
- 最簡單的使用方法,onCreate(@Nullable Bundle savedInstanceState)源碼分析,重點分析彈窗展示和銷毀源碼,使用中show()方法遇到的IllegalStateException分析
- 04.Dialog源碼分析
- AlertDialog源碼分析,通過AlertDialog.Builder對象設定屬性,Dialog生命周期,Dialog中show方法展示彈窗分析,Dialog的dismiss銷毀彈窗,Dialog彈窗問題分析等等
- 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運作在子線程導緻崩潰如何解決?
- 09.onAttachedToWindow和onDetachedFromWindow
- onAttachedToWindow的調用過程,onDetachedFromWindow可以做什麼?
- 10.DecorView介紹
- 什麼是DecorView,DecorView的建立,DecorView的顯示,深度解析
- 彈窗有哪些類型
- 使用子視窗:在 Android 程序内,我們可以直接使用類型為子視窗類型的視窗。在 Android 代碼中的直接應用是 PopupWindow 或者是 Dialog 。這當然可以,不過這種視窗依賴于它的宿主視窗,它可用的條件是你的宿主視窗可用
- 采用View系統:使用 View 系統去模拟一個視窗行為,且能更加快速的實作動畫效果,比如SnackBar 就是采用這套方案
- 使用系統視窗:比如吐司Toast
- Activity并不負責視圖控制,它隻是控制生命周期和處理事件。真正控制視圖的是Window。一個Activity包含了一個Window,Window才是真正代表一個視窗。
- Activity就像一個控制器,統籌視圖的添加與顯示,以及通過其他回調方法,來與Window、以及View進行互動。
- Window是什麼?
- 表示一個視窗的概念,是所有View的直接管理者,任何視圖都通過Window呈現(點選事件由Window->DecorView->View; Activity的setContentView底層通過Window完成)
- Window是一個抽象類,具體實作是PhoneWindow。PhoneWindow中有個内部類DecorView,通過建立DecorView來加載Activity中設定的布局
。R.layout.activity_main
- 建立Window需要通過WindowManager建立,通過WindowManager将DecorView加載其中,并将DecorView交給ViewRoot,進行視圖繪制以及其他互動。
- WindowManager是外界通路Window的入口
- Window具體實作位于WindowManagerService中
- WindowManager和WindowManagerService的互動是通過IPC完成
- 如何通過WindowManager添加Window(代碼實作)?
- 如下所示
//1. 控件 Button button = new Button(this); button.setText("Window Button"); //2. 布局參數 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat.TRANSPARENT); layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; layoutParams.gravity = Gravity.LEFT | Gravity.TOP; layoutParams.x = 100; layoutParams.y = 300; // 必須要有type不然會異常: the specified window type 0 is not valid layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; //3. 擷取WindowManager并添加控件到Window中 WindowManager windowManager = getWindowManager(); windowManager.addView(button, layoutParams);
- 如下所示
- WindowManager的主要功能是什麼?
- 添加、更新、删除View
public interface ViewManager{ public void addView(View view, ViewGroup.LayoutParams params); //添加View public void updateViewLayout(View view, ViewGroup.LayoutParams params); //更新View public void removeView(View view); //删除View }
- 添加、更新、删除View
- DecorView是FrameLayout的子類,它可以被認為是Android視圖樹的根節點視圖。
- DecorView作為頂級View,一般情況下它内部包含一個豎直方向的LinearLayout,在這個LinearLayout裡面有上下三個部分,上面是個ViewStub,延遲加載的視圖(應該是設定ActionBar,根據Theme設定),中間的是标題欄(根據Theme設定,有的布局沒有),下面的是内容欄。
- 具體情況和Android版本及主體有關,以其中一個布局為例,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:fitsSystemWindows="true" android:orientation="vertical"> <!-- Popout bar for action modes --> <ViewStub android:id="@+id/action_mode_bar_stub" android:layout_width="match_parent" android:layout_height="wrap_content" android:inflatedId="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:theme="?attr/actionBarTheme" /> <FrameLayout style="?android:attr/windowTitleBackgroundStyle" android:layout_width="match_parent" android:layout_height="?android:attr/windowTitleSize"> <TextView android:id="@android:id/title" style="?android:attr/windowTitleStyle" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@null" android:fadingEdge="horizontal" android:gravity="center_vertical" /> </FrameLayout> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:foreground="?android:attr/windowContentOverlay" android:foregroundGravity="fill_horizontal|top" /> </LinearLayout>
- 在Activity中通過setContentView所設定的布局檔案其實就是被加到内容欄之中的,成為其唯一子View,就是上面的id為content的FrameLayout中,在代碼中可以通過content來得到對應加載的布局。
ViewGroup content = (ViewGroup)findViewById(android.R.id.content); ViewGroup rootView = (ViewGroup) content.getChildAt(0);
- Activity 與 PhoneWindow 與 DecorView 關系圖
- App點選桌面圖檔啟動過程
- window啟動流程
- Activity 與 PhoneWindow 與 DecorView 之間什麼關系?
- 一個 Activity 對應一個 Window 也就是 PhoneWindow,一個 PhoneWindow 持有一個 DecorView 的執行個體,DecorView 本身是一個 FrameLayout。
- ActivityThread中執行performLaunchActivity,進而生成了Activity的執行個體。源碼如下所示,ActivityThread類中源碼
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); ... } catch (Exception e) { ... } try { ... if (activity != null) { ... activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor); ... } ... } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { ... } return activity; }
- 從上面的performLaunchActivity可以看出,在建立Activity執行個體的同時,會調用Activity的内部方法attach
- 在attach該方法中完成window的初始化。源碼如下所示,Activity類中源碼
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback) { mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); } }
- 使用者執行Activity的setContentView方法,内部是調用PhoneWindow的setContentView方法,在PhoneWindow中完成DecorView的建立。流程
- 1.Activity中的setContentView
- 2.PhoneWindow中的setContentView
- 3.PhoneWindow中的installDecor
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); } @Override public void setContentView(int layoutResID) { ... if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } ... } private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } ... }
關于其他内容介紹
01.關于部落格彙總連結
02.關于我的部落格
- github: https://github.com/yangchong211
- 知乎: https://www.zhihu.com/people/yczbj/activities
- 簡書: http://www.jianshu.com/u/b7b2c6ed9284
- csdn: http://my.csdn.net/m0_37700275
- 喜馬拉雅聽書: http://www.ximalaya.com/zhubo/71989305/
- 開源中國: https://my.oschina.net/zbj1618/blog
- 泡在網上的日子: http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 郵箱:[email protected]
- 阿裡雲部落格: https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault頭條: https://segmentfault.com/u/xiangjianyu/articles
- 掘金: https://juejin.im/user/5939433efe88c2006afa0c6e