1. 程式人生 > >Android Java層UI渲染實現 三 View的建立

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 JavaUI渲染實現 View建立

根據前面的分析,我們可以知道,每一個Activity都關聯了一個Window物件,用來描述一個應用程式視窗。每一個應用程式視窗內部又包含一個View物件,用於描述應用程式檢視。應用程式檢視是真正用來實現UI和佈局的。也就是說,每一個Activity的UI內容和佈局都是通過與其所關聯的一個

Android JavaUI渲染實現 二 Window的建立

在上一篇中我們看了Context的建立過程,我們知道,每一個Activity都有一個Context,同時它還有一個Window物件,用來描述一個具體的應用程式視窗。 現在我們就來分析分析它的建立 還記得在Activity初始化的時候,呼叫attach方法的時候,會建立一個PhoneW

Android JavaUI渲染實現一(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 javaServiceManager

概述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新增自定義服務,實現javaapi呼叫

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/