Android Java層UI渲染實現 三 View的建立
根據前面的分析,我們可以知道,每一個Activity都關聯了一個Window物件,用來描述一個應用程式視窗。每一個應用程式視窗內部又包含一個View物件,用於描述應用程式檢視。應用程式檢視是真正用來實現UI和佈局的。也就是說,每一個Activity的UI內容和佈局都是通過與其所關聯的一個Window物件的內部一個View物件實現的。
還記得Activity的啟動流程嘛?在ActivityThread.performLaunchActivity(ActivityClientRecord r, Intent customIntent)
中,先建立了Context,然後是建立了一個Activity,然後在對Activity的初始化中又建立了一個Window,之後會去呼叫Activity.onCreate()
一般我們會自定義Activity,呼叫的onCreate
方法,也就是我們自己實現的onCreate
方法
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
在onCreate方法中,會呼叫父類的onCreate
Activity.onCreate()
,然後會呼叫setContentView(R.layout.activity_main);
進行檢視的建立:
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
//AppCompatDelegate.java
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return create(activity, activity.getWindow(), callback);
}
private static AppCompatDelegate create(Context context, Window window,
AppCompatCallback callback) {
if (Build.VERSION.SDK_INT >= 24) {
return new AppCompatDelegateImplN(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 23) {
return new AppCompatDelegateImplV23(context, window, callback);
} else {
return new AppCompatDelegateImplV14(context, window, callback);
}
}
在這裡,我們遇到一個新的類:AppCompatDelegate.java
public abstract class AppCompatDelegate
我們看看它的介紹:
This class represents a delegate which you can use to extend AppCompat’s support to any {@link android.app.Activity}.
上面大概是說,它是代表的是一個委託,你可以使用該委託將AppCompat的支援擴充套件到任何Activity。
繼續看,當AppCompatDelegate物件建立完了後,會呼叫setContentView()
方法,我這裡使用的api是27,所以返回的AppCompatDelegate物件是AppCompatDelegateImplV14,而AppCompatDelegateImplV14繼承自AppCompatDelegateImplV9:
class AppCompatDelegateImplV14 extends AppCompatDelegateImplV9
AppCompatDelegateImplV14(Context context, Window window, AppCompatCallback callback) {
super(context, window, callback);
}
AppCompatDelegateImplV9(Context context, Window window, AppCompatCallback callback) {
super(context, window, callback);
}
setContextView方法是在AppCompatDelegateImplV9這個類中實現的。
//AppCompatDelegateImplV9.java
@Override
public void setContentView(View v, ViewGroup.LayoutParams lp) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v, lp);
mOriginalWindowCallback.onContentChanged();
}
我們先看ensureSubDecor()
方法:
private void ensureSubDecor() {
//如果mSubDecor還沒有初始化,就建立SubDecor
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
// If a title was set before we installed the decor, propagate it now
//如果在decor安裝前有設定title,現在就 把它傳進去。
CharSequence title = getTitle();
if (!TextUtils.isEmpty(title)) {
onTitleChanged(title);
}
//適配尺寸在螢幕上,一些引數的初始化
applyFixedSizeWindow();
//空實現
onSubDecorInstalled(mSubDecor);
mSubDecorInstalled = true;
// Invalidate if the panel menu hasn't been created before this.
// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
// being called in the middle of onCreate or similar.
// A pending invalidation will typically be resolved before the posted message
// would run normally in order to satisfy instance state restoration.
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
if (!isDestroyed() && (st == null || st.menu == null)) {
invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
}
}
}
在上面的方法中,如果SubDecor沒有被建立過,就先建立:
//AppCompatDelegateImplV9.java
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
//下面省略的程式碼中,獲取了系統的主題。
... ...
// Now let's make sure that the Window has installed its decor by retrieving it
//保證window是被載入了的
//在這裡也確保DecorView的初始化
mWindow.getDecorView();
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
//根據屬性,建立不同的subDecor
if (!mWindowNoTitle) {
//如果Window沒有Titile
if (mIsFloating) {
//是否懸浮,也就是說該Activity可以使dialog的樣子
// If we're floating, inflate the dialog title decor
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_dialog_title_material, null);
// Floating windows can never have an action bar, reset the flags
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {
//是否有ActionBar
TypedValue outValue = new TypedValue();
mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
Context themedContext;
if (outValue.resourceId != 0) {
themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
} else {
themedContext = mContext;
}
// Now inflate the view using the themed context and set it as the content view
subDecor = (ViewGroup) LayoutInflater.from(themedContext)
.inflate(R.layout.abc_screen_toolbar, null);
mDecorContentParent = (DecorContentParent) subDecor
.findViewById(R.id.decor_content_parent);
mDecorContentParent.setWindowCallback(getWindowCallback());
/**
* Propagate features to DecorContentParent
*/
if (mOverlayActionBar) {
mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
}
if (mFeatureProgress) {
mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
}
if (mFeatureIndeterminateProgress) {
mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
}
}
} else {
if (mOverlayActionMode) {
//是疊加模式,用於設定ActionBar的主題
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_screen_simple_overlay_action_mode, null);
} else {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
}
if (Build.VERSION.SDK_INT >= 21) {
// If we're running on L or above, we can rely on ViewCompat's
// setOnApplyWindowInsetsListener
ViewCompat.setOnApplyWindowInsetsListener(subDecor,
new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v,
WindowInsetsCompat insets) {
final int top = insets.getSystemWindowInsetTop();
final int newTop = updateStatusGuard(top);
if (top != newTop) {
insets = insets.replaceSystemWindowInsets(
insets.getSystemWindowInsetLeft(),
newTop,
insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom());
}
// Now apply the insets on our view
return ViewCompat.onApplyWindowInsets(v, insets);
}
});
} else {
// Else, we need to use our own FitWindowsViewGroup handling
((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
new FitWindowsViewGroup.OnFitSystemWindowsListener() {
@Override
public void onFitSystemWindows(Rect insets) {
insets.top = updateStatusGuard(insets.top);
}
});
}
}
if (subDecor == null) {
throw new IllegalArgumentException(
"AppCompat does not support the current theme features: { "
+ "windowActionBar: " + mHasActionBar
+ ", windowActionBarOverlay: "+ mOverlayActionBar
+ ", android:windowIsFloating: " + mIsFloating
+ ", windowActionModeOverlay: " + mOverlayActionMode
+ ", windowNoTitle: " + mWindowNoTitle
+ " }");
}
if (mDecorContentParent == null) {
mTitleView = (TextView) subDecor.findViewById(R.id.title);
}
// Make the decor optionally fit system windows, like the window's decor
ViewUtils.makeOptionalFitsSystemWindows(subDecor);
final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
R.id.action_bar_activity_content);
//得到Window的ContentView
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
// There might be Views already added to the Window's content view so we need to
// migrate them to our content view
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
// Change our content FrameLayout to use the android.R.id.content id.
// Useful for fragments.
windowContentView.setId(View.NO_ID);
contentView.setId(android.R.id.content);
// The decorContent may have a foreground drawable set (windowContentOverlay).
// Remove this as we handle it ourselves
if (windowContentView instanceof FrameLayout) {
((FrameLayout) windowContentView).setForeground(null);
}
}
// Now set the Window's content view with the decor
mWindow.setContentView(subDecor);
contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
@Override
public void onAttachedFromWindow() {}
@Override
public void onDetachedFromWindow() {
dismissPopups();
}
});
return subDecor;
}
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
//獲取DecorView,然後他是一個FrameLayout
installDecor();
}
return mDecor;
}
在上面程式碼中會根據不同的屬性,載入不同的subDecor,然後看Window中的contentView中有沒有子View,如果有就從contentView中移除並加入到subDecor的contentView中。
然後呼叫了mWindow.setContentView(subDecor);
//PhoneWindow.java
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//FEATURE_CONTENT_TRANSITIONS
//用於請求視窗內容更改的標誌應使用TransitionManager進行動畫處理。
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//有動畫轉場新增
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
//一般會到這裡
//新增subDecor
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
在上面中,如果mContentParent==null,那麼就會呼叫installDecor()
方法,但是在前面有呼叫PhoneWindow的getDecorView
方法中,會呼叫installDecor
方法。mContentParent用來描述一個型別為DecorView的檢視物件,或者型別為DecorView的檢視物件的一個子檢視物件,用作UI容器。當它的值為null的時候,說明正在處理的應用程式建立的檢視還沒有建立.
我們看看installDecor
方法:
//PhoneWindow.java
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
private TextView mTitleView;
DecorContentParent mDecorContentParent;
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
ViewGroup mContentParent;
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
mDecorContentParent = decorContentParent;
mDecorContentParent.setWindowCallback(getCallback());
if (mDecorContentParent.getTitle() == null) {
mDecorContentParent.setWindowTitle(mTitle);
}
... ...
} else {
mTitleView = findViewById(R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
final View titleContainer = findViewById(R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
mContentParent.setForeground(null);
} else {
mTitleView.setText(mTitle);
}
}
}
if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}
... ...
}
}
由於我們是在Activity啟動中分析的,所以在這裡我們可以假設mDecor是為null,因此會呼叫generateDecor()
方法來建立DecorView:
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
/*
*系統是沒有上下文的,在這種情況下,我們需要直接使用我們擁有的上下文,
*否則,我們需要application 的coontext,
*因此,我們不依賴於activity
*/
Context context;
if (mUseDecorContext) {
Context applicationContext
相關推薦
Android Java層UI渲染實現 三 View的建立
根據前面的分析,我們可以知道,每一個Activity都關聯了一個Window物件,用來描述一個應用程式視窗。每一個應用程式視窗內部又包含一個View物件,用於描述應用程式檢視。應用程式檢視是真正用來實現UI和佈局的。也就是說,每一個Activity的UI內容和佈局都是通過與其所關聯的一個
Android Java層UI渲染實現 二 Window的建立
在上一篇中我們看了Context的建立過程,我們知道,每一個Activity都有一個Context,同時它還有一個Window物件,用來描述一個具體的應用程式視窗。 現在我們就來分析分析它的建立
還記得在Activity初始化的時候,呼叫attach方法的時候,會建立一個PhoneW
Android Java層UI渲染實現一(Context的建立)
在Android應用程式的四大元件中,只有Activity元件與UI相關,它描述的是應用程式視窗,因此我們通過它的UI實現來分析Android系統在Java層的UI實現。
首先,我們得從Activity的啟動開始:
再我們呼叫startActivity後,最終會呼叫startAc
android java層實現hook替換method
reflect 過程 ren pic [] hotfix mru andro bsp Android上的熱修復框架 AndFix 大家都很熟悉了,它的原理實際上很簡單:
方法替換——Java層的每一個方法在虛擬機實現裏面都對應著一個ArtMethod的結構體,只要把
Android java層音訊相關的分析與理解(二)音量控制相關
上一篇我們簡單地說了一下Android java層的基本框架。接下來我們就來聊一下在android中音量控制的相關內容。
1.音量定義
在Android中,音量的控制與流型別是密不可分的,每種流型別都獨立地擁有自己的音量設定,各種流型別的音量是互不干擾的,例如音樂音量、通話
android java層直接和kernel互動的最快的方法
http://blog.csdn.net/zengkexu/article/details/8805339
android java 和kernel 的互動方式
按照常規的要通過JNI實現, 然後jni 呼叫HAL的IOCTL ,或者類似Vold 中建立netli
淺談Android java層ServiceManager
概述ServiceManager作為Android程序間通訊binder機制中的重要角色,執行在native層,由c++語言實現,任何Service被使用之前,例如播放音樂的MediaService,例如管理activity的ActivityManagerService,均要
Android java層音訊相關的分析與理解(一)基本框架
最近在整理之前在公司寫的一些文件,於是決定將部分適用比較廣的文件整理在部落格中,供大家參考。第一個系列是AudioService相關的。這個可以算是《深入理解Android 卷Ⅲ》的一個讀書筆記吧。整體的思路基本上與《深入理解Android 卷Ⅲ》的Audio部分差不多。只
Android從零擼美團(三) - Android多標籤tab滑動切換 - 自定義View快速實現高度定製封裝
這是【從零擼美團】系列文章第三篇 【從零擼美團】是一個高仿美團的開源專案,旨在鞏固 Android 相關知識的同時,幫助到有需要的小夥伴。 GitHub 原始碼地址:github.com/cachecats/L…
Android從零擼美團(一) - 統一管理 Gradle 依賴 提取到單獨檔案中
Andr
Android Fk: PKMS(3)之installd及LocalSocket實現Java層與Native層通訊
LOCAL_CLANG := true#Android Fk: PKMS(3)之installd及LocalSocket實現Java層與Native層通訊
一、installd的概述
從上一篇介紹應用安裝與解除安裝的學習文件中知道PKMS在實現部分包管理功能時需要藉助instal
android framework新增自定義服務,實現java層api呼叫
1.在frameworks/base/core/java/android/app/下新增aidl檔案
frameworks/base/core/java/android/app/IGMyTestService.aidl
package android.app;
// De
Java多線程實現的三種方式
get() warning 三種方式 方式 緩存 運行 了解 ren ava Java多線程實現方式主要有三種:繼承Thread類、實現Runnable接口、使用ExecutorService、Callable、Future實現有返回結果的多線程。其中前兩種方式線程執行完後
Android消息機制1-Handler(Java層)(轉)
word get() als php lib light ora out getc 轉自:http://gityuan.com/2015/12/26/handler-message-framework/
相關源碼
framework/base/core/java/an
torch教程[1]用numpy實現三層全連接神經網絡
一個 out () numpy port import 課程 例子程序 bsp torch的第一個例子程序,是用numpy函數實現神經網絡。cs231n的課程中有大量這樣的作業。
import numpy as np
N,D_in,H,D_out=64,1000,100,
實現三層交換機路由功能
mar run sha 結果 3.2 設置 proc log vpd 一、實驗拓撲圖
二、實驗目的1.實現二層交換機不同vlan之間的互通2.實現三層交換機的路由功能可以使PC5、PC6、PC7、PC8之間可以相互ping通
三、所使用的設備四臺pc、兩臺二層交換機、一臺
【Android界面實現】View Animation 使用介紹
ref 轉載 HA ceshi 很多 ons 伸縮 ets 時間設置
? ? 轉載請註明出處:http://blog.csdn.net/zhaokaiqiang19
java 責任鏈模式的三種實現
img public throwable group ons ++ cati 核心 color 責任鏈模式
責任鏈模式的定義:使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系, 將這個對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理他為止。這
基於GNS3思科模擬器實現三層交換機不同vlan通信
虛擬 標記 mage 配置命令 http ESS face 就是 處理過程 實驗拓撲:
實驗說明:SW1為三層交換機,SW2和SW3為二層交換機,PC1屬於VLAN 100,PC2屬於VLAN 110,PC3屬於VLAN 120,現通過三層交換機實現不同VLAN間的通信。
在三層交換機上實現DHCP中繼實驗二
默認路由 shadow interface com serve inter pro 自動獲取 dhcp服務
要求:三層交換機連接兩個部門設計部和市場部,分別處於vlan10和vlan20中,vlan10的網關地址為192.168.10.1/24,vlan20的網關地址為1
Android UI渲染總結
翻譯文推薦:Android UI效能優化詳解 http://mrpeak.cn/android/2016/01/11/android-performance-ui
在Android Studio下使用Hierarchy Viewer http://www.jianshu.com/p/