Android自定义View之Activity页面的组成

来源:互联网 发布:龙城霸业 boss积分数据 编辑:程序博客网 时间:2024/05/18 15:26
Activity 代表一个窗口,实际上这个窗口并不是Activity,而是由 一个 继承至Window抽象类的PhoneWindow 对象mWindow 来表示的,mWindow 是Activity 的成员变量,主要作用就是负责窗口的管理。之所以说是管理窗口的,是因为它并不能将界面展示出来,展现界面是由它管理的 DecorView对象来完成, 在Activity中我们可以通过getWindow().getDecorView()方法来获取DecorView对象,DecorView 类是 FrameLayout 的子类, 也是整个 View 树的根布局(这里也能说明Android事件的分发是从ViewGroup开始的,如果需要了解更多关于Android事件分发问题,可以浏览《Android中的事件分发机制》这篇博客)。DecorView 由三部分构成: ActionBar、标题区和内容区。在Androidsdk版本为23的 Android\sdk\platforms\android-23\data\res\layout目录下有一个名为screen.xml 的布局文件(还有一个screen_simple.xml文件,比screen.xml少了RelativeLayout android:id="@android:id/title_container"标题部分), 具体内容如下
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2006 The Android Open Source Project     Licensed under the Apache License, Version 2.0 (the "License");     you may not use this file except in compliance with the License.     You may obtain a copy of the License at            http://www.apache.org/licenses/LICENSE-2.0       Unless required by applicable law or agreed to in writing, software     distributed under the License is distributed on an "AS IS" BASIS,     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.     See the License for the specific language governing permissions and     limitations under the License.--><!--This is the basic layout for a screen, with all of its features enabled.--><!-- Title bar and content --><LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"    android:fitsSystemWindows="true"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent">    <!-- Popout bar for action modes -->    <ViewStub android:id="@+id/action_mode_bar_stub"              android:inflatedId="@+id/action_mode_bar"              android:layout="@layout/action_mode_bar"              android:layout_width="match_parent"              android:layout_height="wrap_content"              android:theme="?attr/actionBarTheme" />    <!-- Title bar -->    <RelativeLayout android:id="@android:id/title_container"        style="?android:attr/windowTitleBackgroundStyle"        android:layout_width="match_parent"        android:layout_height="?android:attr/windowTitleSize"    >        <ImageView android:id="@android:id/left_icon"            android:layout_width="16dip"            android:layout_height="16dip"            android:layout_marginEnd="5dip"            android:layout_alignParentStart="true"            android:layout_centerVertical="true"            android:visibility="gone"            android:scaleType="fitCenter"        />        <LinearLayout android:id="@+android:id/right_container"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignParentEnd="true"            android:layout_centerVertical="true"        >            <ImageView android:id="@android:id/right_icon"                android:layout_width="16dip"                android:layout_height="16dip"                android:layout_marginStart="5dip"                android:layout_gravity="center_vertical"                android:visibility="gone"                android:scaleType="fitCenter"            />            <ProgressBar android:id="@+id/progress_circular"                style="?android:attr/progressBarStyleSmallTitle"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_marginStart="5dip"                android:layout_gravity="center_vertical"                android:visibility="gone"                android:max="10000"            />        </LinearLayout>        <ProgressBar android:id="@+id/progress_horizontal"            style="?android:attr/progressBarStyleHorizontal"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_marginStart="2dip"            android:layout_toStartOf="@android:id/right_container"            android:layout_toEndOf="@android:id/left_icon"            android:layout_centerVertical="true"            android:visibility="gone"            android:max="10000"        />        <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:scrollHorizontally="true"            android:gravity="center_vertical"            android:layout_toStartOf="@android:id/right_container"            android:layout_toEndOf="@id/left_icon"        />    </RelativeLayout>    <!-- Content -->    <FrameLayout android:id="@android:id/content"        android:layout_width="match_parent"        android:layout_height="0dip"        android:layout_weight="1"        android:foregroundGravity="fill_horizontal|top"        android:foreground="?android:attr/windowContentOverlay"    /></LinearLayout>

由代码可以看出,ActionBar 由ViewStub标签定义,内容区包括2个部分,一个由RelativeLayout定义的标题区和一个由FrameLayout定义的正文区。当开发者调用setContentView()方法设置布局资源文件的id时,其实就是将定义的布局资源文件inFlate()成View后作为正文区的子View显示出来。如图(图片来源网络):

Activity

需要说明的是:在上图的包含过程中缺少了一步,DecorView并不是直接包含ActionBar、TitleBar和ContentParent的,通过screen.xml可以看出,包含ActionBar、TitleBar和ContentParent的是一个LinearLayout布局,而这个LinearLayout布局就是DecorView的唯一一个直接孩子。

由图可知:

① Activity相当于一个框,它包含了Window(实际上是Window的子类PhoneWindow)来管理窗口,Activity其他功能这里不做说明;

② PhoneWindow管理窗口,它包含了DecorView用来渲染和显示内容(包括ActionBar、TitleBar和ContentParent部分);

③ DecorView是FrameLayout的子类,也是整个视图的根节点,开发者写的布局layout.xml文件最终是被添加到DecorView的ContentParent部分;

④ 当在讨论Android事件的分发是从View还是ViewGroup开始时,从DecorView extends FrameLayout extends ViewGroup就能得到Android事件是从ViewGroup开始的(如果需要了解更多关于Android事件分发问题,可以浏览《Android中的事件分发机制》这篇博客)。


最后可以得出Activity中View树的根节点是一个继承至FrameLayout的DecorView,而Android\sdk\platforms\android-23\data\res\layout目录下的名为screen.xml 的布局文件最后也是被增加到DecorView中。一句话就是Activity包含了一个管理窗口的继承至Window的PhoneWindow对象,而在PhoneWindow中创建了一个继承至FrameLayout类的DecorView对象,并且把包含了ActionBar、TitleBar和Content的screen.xml布局文件通过addView()方法添加到了DecorView中,当开发者定义了layout.xml布局并调用了setContentView()方法时,实际上是调用了PhoneWindow类中的setContentView(),最终通过mContentParent.addView(view, params)方法把定义的布局文件添加到contentParent中显示出来。


了解更多:

① DecorView类的定义:

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {    ......}
从Android中对DecorView的定义可以看到DecorView同上面文章中提到的一样,它是继承至FrameLayout的。

② Activity中PhoneWindow在什么时候创建的:

通过查看Activity的源码可以找到PhoneWindow的创建是在Activity的attach()方法中(attach()方法在Activity中是onCreate()方法调用之前系统自动调用的一个方法),同时也获取到了WindowManager对象

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) {attachBaseContext(context);...// 创建PhoneWindow对象mWindow = new PhoneWindow(this);... // 将各种参数赋给Activity的成员变量// 通过PhoneWindow对象获取mWindowManager对象mWindowManager = mWindow.getWindowManager();...}

③ 在PhoneWindow中DecorView的创建过程:

首先看到系统的getDecorView()方法

@Overridepublic final View getDecorView() {if (mDecor == null) {installDecor(); // 创建一个DecorView对象}return mDecor;}

跟进installDecor()方法:其中的代码比较多,在这里只选取了创建DecorView的代码,进行了判断,如果DecorView为null就调用方法创建

private void installDecor() {if (mDecor == null) {mDecor = generateDecor();// 创建DecorView...}if (mContentParent == null) {mContentParent = generateLayout(mDecor); // 获取ContentParent,也就是开发者定义的layout.xml布局的父节点...} else {mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);...}}}}

接着跟进generateDecor()方法,可以看到就只是简单的new 出了一个DecorView对象,到这里,DecorView对象在PhoneWIndow中就创建完成了

protected DecorView generateDecor() {       return new DecorView(getContext(), -1);}

④ 开发者定义的layout.xml文件增加到ContentParent的过程:

我们在Activity中调用setContentView()方法时,实际上是调用的PhoneWIndow中的setContentView()方法

Activity中setContentView()方法代码(getWindow()方法是返回Window对象,实际就是PhoneWindow对象):

public void setContentView(@LayoutRes int layoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();} public void setContentView(View view) {getWindow().setContentView(view);initWindowDecorActionBar();}public void setContentView(View view, ViewGroup.LayoutParams params) {getWindow().setContentView(view, params);initWindowDecorActionBar();}

PhoneWindow类中setContentView()方法(Window中定义抽象方法,PhoneWindow类实现):

@Overridepublic void setContentView(int layoutResID) {// 直接通过layoutResID添加if (mContentParent == null) {    // DecorView为null就创建            installDecor();        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {    // 不为null,就移除mContentParent内所有的所有子View,否者会报错            mContentParent.removeAllViews();        }        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                    getContext());            transitionTo(newScene);        } else {    // 将我们的资源文件通过LayoutInflater对象转换为View树,并且添加至mContentParent中            mLayoutInflater.inflate(layoutResID, mContentParent);        }        mContentParent.requestApplyInsets();        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }}@Overridepublic void setContentView(View view) {// 设置View对象,调用另一个重载方法,使用系统定义的LayoutParamssetContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}@Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) {// 通过View对象和布局参数if (mContentParent == null) {            installDecor();        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            mContentParent.removeAllViews();        }        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            view.setLayoutParams(params);            final Scene newScene = new Scene(mContentParent, view);            transitionTo(newScene);        } else {    // 直接通过mContentParent的addView()方法将定义的view添加到mContentParent中            mContentParent.addView(view, params);        }        mContentParent.requestApplyInsets();        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }}



0 0
原创粉丝点击