日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

android 代码加view,Android中将View添加至窗口的源码分析

發(fā)布時(shí)間:2025/3/15 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android 代码加view,Android中将View添加至窗口的源码分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文主要內(nèi)容是講解一個(gè)視圖View或者一個(gè)ViewGroup對(duì)象是如何添加至應(yīng)用程序窗口中的。下文中提到的窗口可泛指我們能看到的界面,包括一個(gè)Activity呈現(xiàn)的界面(我們可以將之理解為應(yīng)用程序窗口),一個(gè)Dialog,一個(gè)Toast,一個(gè)Menu菜單等。

首先先來(lái)看一個(gè)UI框架的關(guān)系圖

然后再來(lái)對(duì)相關(guān)類的作用進(jìn)行一下簡(jiǎn)單介紹,這樣你的印象才深刻:

Window 類

位于/frameworks/base/core/java/android/view/Window.java

說(shuō)明:該類是一個(gè)抽象類,提供了繪制窗口的一組通用API。可以將之理解為一個(gè)載體,各種View在這個(gè)載體上顯示。

源文件(部分)如下:

public abstract class Window {

//...

//指定Activity窗口的風(fēng)格類型

public static final int FEATURE_NO_TITLE = 1;

public static final int FEATURE_INDETERMINATE_PROGRESS = 5;

//設(shè)置布局文件

public abstract void setContentView(int layoutResID);

public abstract void setContentView(View view);

//請(qǐng)求指定Activity窗口的風(fēng)格類型

public boolean requestFeature(int featureId) {

final int flag = 1<

mFeatures |= flag;

mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag;

return (mFeatures&flag) != 0;

}

//...

}

PhoneWindow類

/frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindow.java

說(shuō)明:該類繼承于Window類,是Window類的具體實(shí)現(xiàn),即我們可以通過(guò)該類具體去繪制窗口。該類內(nèi)部包含了一個(gè)DecorView對(duì)象,該DecorView對(duì)象是所有應(yīng)用窗口(Activity界面)的根View。PhoneWindow類:把一個(gè)FrameLayout類即DecorView對(duì)象進(jìn)行一定的包裝,將其作為應(yīng)用窗口的根View,并提供一組通用的窗口操作接口。

源文件(部分)如下:

public class PhoneWindow extends Window implements MenuBuilder.Callback {

//...

// This is the top-level view of the window, containing the window decor.

private DecorView mDecor; //該對(duì)象是所有應(yīng)用窗口的根視圖 , 是FrameLayout的子類

//該對(duì)象是Activity布局文件的父視圖,一般來(lái)說(shuō)是一個(gè)FrameLayout型的ViewGroup

// 同時(shí)也是DecorView對(duì)象的一個(gè)子視圖

// 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.

private ViewGroup mContentParent;

//設(shè)置標(biāo)題

@Override

public void setTitle(CharSequence title) {

if (mTitleView != null) {

mTitleView.setText(title);

}

mTitle = title;

}

//設(shè)置背景圖片

@Override

public final void setBackgroundDrawable(Drawable drawable) {

if (drawable != mBackgroundDrawable || mBackgroundResource != 0) {

mBackgroundResource = 0;

mBackgroundDrawable = drawable;

if (mDecor != null) {

mDecor.setWindowBackground(drawable);

}

}

}

//...

}

DecorView類

說(shuō)明:該類是FrameLayout的子類,并且是PhoneWindow的內(nèi)部類。該類就是對(duì)普通的FrameLayout進(jìn)行功能的擴(kuò)展,更確切點(diǎn)可以說(shuō)是修飾(Decor的英文全稱是Decoration,即“修飾”的意思),比如說(shuō)添加TitleBar(標(biāo)題欄),以及 TitleBar上的滾動(dòng)條等 。**最重要的一點(diǎn)是,它是所有應(yīng)用窗口的根View 。

如下所示:

**DecorView根視圖結(jié)構(gòu)**

** DecorView 根視圖形式**

源文件(部分)如下:

private final class DecorView extends FrameLayout {

//...

//觸摸事件處理

@Override

public boolean onTouchEvent(MotionEvent event) {

return onInterceptTouchEvent(event);

}

//...

}

打個(gè)不恰當(dāng)比喻吧,Window類相當(dāng)于一幅畫(huà)(抽象概念,什么畫(huà)我們未知) ,PhoneWindow為一副齊白石先生的山水畫(huà)(具體概念,我們知道了是誰(shuí)的、什么性質(zhì)的畫(huà)),DecorView則為該山水畫(huà)的具體內(nèi)容(有山、有水、有樹(shù),各種界面)。DecorView呈現(xiàn)在PhoneWindow上。

當(dāng)系統(tǒng)(一般是ActivityManagerService)配置好啟動(dòng)一個(gè)Activity(activity對(duì)象和window對(duì)象信息)的相關(guān)參數(shù)后,就會(huì)調(diào)用Activity的onCreate()方法,在其中通過(guò)設(shè)置setContentView()方法設(shè)置該Activity的顯示界面,整個(gè)調(diào)用環(huán)節(jié)由此鋪開(kāi)。setContentView()的三個(gè)構(gòu)造方法調(diào)用流程本質(zhì)上一樣,我們分析其中一個(gè)setContentView(int resId)方法。

###Step1、Activity.setContentView(int resId)該方法在Activity類中,該方法只是簡(jiǎn)單的回調(diào)Window對(duì)象,具體為PhoneWindow對(duì)象的setContentView()方法實(shí)現(xiàn) 。

public void setContentView(int layoutResID) {

getWindow().setContentView(layoutResID);

}

public Window getWindow() {

return mWindow; //Window對(duì)象,本質(zhì)上是一個(gè)PhoneWindow對(duì)象

}

###Step2、PhoneWindow.setContentView() 該方法在PhoneWindow類中

@Override

public void setContentView(int layoutResID) {

//是否是第一次調(diào)用setContentView方法, 如果是第一次調(diào)用,則mDecor和mContentParent對(duì)象都為空

if (mContentParent == null) {

installDecor();

} else {

mContentParent.removeAllViews();

}

mLayoutInflater.inflate(layoutResID, mContentParent);

final Callback cb = getCallback();

if (cb != null) {

cb.onContentChanged();

}

}

首先判斷是否已經(jīng)由setContentView()獲取了mContentParent(即View)對(duì)象,即是否第一次調(diào)用PhoneWindow對(duì)象的setContentView()方法。 如果第一次調(diào)用,則調(diào)用installDecor()方法,否則移除該View內(nèi)的所有子View。最后將我們的資源文件(一般為XML文件)通過(guò)LayoutInflater對(duì)象轉(zhuǎn)化成View樹(shù),并且添加到mContentParent視圖中。

###Step3、PhoneWindow.installDecor()

private void installDecor() {

if (mDecor == null) {

//mDecor為空,則創(chuàng)建一個(gè)Decor對(duì)象

mDecor = generateDecor();

mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

mDecor.setIsRootNamespace(true);

}

if (mContentParent == null) {

//generateLayout()方法會(huì)根據(jù)窗口的風(fēng)格修飾,選擇對(duì)應(yīng)的修飾布局文件

//并且將id為content(android:id="@+id/content")的FrameLayout賦值給mContentParent

mContentParent = generateLayout(mDecor);

//...

}

首先判斷mDecor對(duì)象是否為空,不為空,則調(diào)用generateDecor()創(chuàng)建一個(gè)DecorView(該類是FrameLayout子類,即一個(gè)ViewGroup視圖)

generateDecor()方法原型:

proteced DecorView generateDecor(){

return new DecorView(getContext(), -1);

}

其次繼續(xù)判斷mContentParent對(duì)象是否為空,如果不為空,則調(diào)用generateLayout()方法去創(chuàng)建mContentParent對(duì)象。

generateLayout()方法:

protected ViewGroup generateLayout(DecorView decor) {

// Apply data from current theme.

//...1、根據(jù)requestFreature()和Activity節(jié)點(diǎn)的android:theme="" 設(shè)置好 features值

//2 根據(jù)設(shè)定好的 features值,即特定風(fēng)格屬性,選擇不同的窗口修飾布局文件

int layoutResource; //窗口修飾布局文件

int features = getLocalFeatures();

// System.out.println("Features: 0x" + Integer.toHexString(features));

if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {

if (mIsFloating) {

layoutResource = com.android.internal.R.layout.dialog_title_icons;

} else {

layoutResource = com.android.internal.R.layout.screen_title_icons;

}

// System.out.println("Title Icons!");

} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) {

// Special case for a window with only a progress bar (and title).

// XXX Need to have a no-title version of embedded windows.

layoutResource = com.android.internal.R.layout.screen_progress;

// System.out.println("Progress!");

}

//...

//3 選定了窗口修飾布局文件 ,添加至DecorView對(duì)象里,并且指定mcontentParent值

View in = mLayoutInflater.inflate(layoutResource, null);

decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

if (contentParent == null) {

throw new RuntimeException("Window couldn't find content container view");

}

if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {

ProgressBar progress = getCircularProgressBar(false);

if (progress != null) {

progress.setIndeterminate(true);

}

}

//...

return contentParent;

}

該方法會(huì)做如下事情:

1、根據(jù)窗口的風(fēng)格修飾類型為該窗口選擇不同的窗口布局文件(根視圖)。這些窗口修飾布局文件指定一個(gè)用來(lái)存放Activity自定義布局文件的ViewGroup視圖,一般為FrameLayout 其id 為:

android:id="@android:id/content"

例如窗口修飾類型包括FullScreen(全屏)、NoTitleBar(不含標(biāo)題欄)等。選定窗口修飾類型有兩種:

①、指定requestFeature()窗口修飾符,PhoneWindow對(duì)象調(diào)用getLocalFeature()方法獲取值;

②、為我們的Activity配置相應(yīng)屬性,即android:theme=“”,PhoneWindow對(duì)象調(diào)用getWindowStyle()方法獲取值。

舉例如下,隱藏標(biāo)題欄有如下方法:requestWindowFeature(Window.FEATURE_NO_TITLE);或者 為Activity配置xml屬性:android:theme=”@android:style/Theme.NoTitleBar”。

PS:因此,在Activity中必須在setContentView之前調(diào)用requestFeature()方法。

確定好窗口風(fēng)格之后,選定該風(fēng)格對(duì)應(yīng)的布局文件,這些布局文件位于 frameworks/base/core/res/layout/ ,

典型的窗口布局文件有:

R.layout.dialog_titile_icons

R.layout.screen_title_icons

R.layout.screen_progress

R.layout.dialog_custom_title

R.layout.dialog_title

R.layout.screen_title// 最常用的Activity窗口修飾布局文件

R.layout.screen_simple//全屏的Activity窗口布局文件

** 分析Activity最常用的一種窗口布局文件,R.layout.screen_title :**

android:orientation="vertical"

android:fitsSystemWindows="true">

android:layout_width="match_parent"

android:layout_height="?android:attr/windowTitleSize"

style="?android:attr/windowTitleBackgroundStyle">

style="?android:attr/windowTitleStyle"

android:background="@null"

android:fadingEdge="horizontal"

android:gravity="center_vertical"

android:layout_width="match_parent"

android:layout_height="match_parent" />

android:layout_width="match_parent"

android:layout_height="0dip"

android:layout_weight="1"

android:foregroundGravity="fill_horizontal|top"

android:foreground="?android:attr/windowContentOverlay" />

該布局文件很簡(jiǎn)單,一個(gè)LinearLayout下包含了兩個(gè)子FrameLayout視圖,第一個(gè)FrameLayout用來(lái)顯示標(biāo)題欄(TitleBar),該TextView 視圖id為title(android:id="@android:id/title");第二個(gè)FrameLayout用來(lái)顯示我們Activity的布局文件的父視圖,該FrameLayoutid為content(android:id="@android:id/content") 。

全屏的窗口布局文件 R.layout.screen_simple:\

enabled.

-->

android:id="@android:id/content"

android:fitsSystemWindows="true"

android:foregroundInsidePadding="false"

android:foregroundGravity="fill_horizontal|top"

android:foreground="?android:attr/windowContentOverlay" />

該布局文件只有一個(gè)FrameLayout,用來(lái)顯示我們Activity的布局文件,該FrameLayoutid為 android:id="@android:id/content"

2、前面我們確定窗口修飾布局文件后,mDecor作為根視圖將窗口布局對(duì)應(yīng)的視圖(view)添加進(jìn)去,并且獲取id為content的View,將其賦值給mContentParent對(duì)象,即我們前面講的第二個(gè)FrameLayout.

###At Last、產(chǎn)生了mDecor和mContentParent對(duì)象后,將Activity布局文件直接添加到mContentParent父View中。即step2代碼中的如下所示:

mLayoutInflater.inflate(layoutResID, mContentParent);

###整理:整個(gè)過(guò)程主要是如何把Activity的布局文件添加至窗口中(即PhoneWindow的DecorView中),上面過(guò)程概括為:

1、創(chuàng)建一個(gè)DecorView對(duì)象,作為應(yīng)用窗口的根View。

2、創(chuàng)建不同的窗口修飾布局文件,并且獲取Activity的布局文件該存放的地方,由該窗口修飾布局文件內(nèi)id為content的FrameLayout指定 。

3、將Activity的布局文件加至id為content的FrameLayout內(nèi)。

4、最后,當(dāng)ActivityManagerService準(zhǔn)備resume一個(gè)Activity時(shí),會(huì)回調(diào)該Activity的handleResumeActivity()方法, 該方法會(huì)調(diào)用Activity的makeVisible方法 ,顯示我們剛才創(chuàng)建的mDecor 視圖族。

//系統(tǒng)resume一個(gè)Activity時(shí),調(diào)用此方法

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {

ActivityRecord r = performResumeActivity(token, clearHide);

//...

if (r.activity.mVisibleFromClient) {

r.activity.makeVisible();

}

}

handleResumeActivity()方法原型如下:位于ActivityThread類中

void makeVisible() {

if (!mWindowAdded) {

ViewManager wm = getWindowManager(); // 獲取WindowManager對(duì)象

wm.addView(mDecor, getWindow().getAttributes());

mWindowAdded = true;

}

mDecor.setVisibility(View.VISIBLE); //使其處于顯示狀況

}

接下來(lái)就是,如何把我們已經(jīng)創(chuàng)建好的窗口通知給WindowManagerService ,以便它能夠把這個(gè)窗口顯示在屏幕上。關(guān)于這方面內(nèi)容大家可以去看鄧凡平老師的這篇博客《Android深入淺出之Surface[1]》

總結(jié)

以上是生活随笔為你收集整理的android 代码加view,Android中将View添加至窗口的源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。