Window

public abstract class Window {public <T extends View> T findViewById(@IdRes int id) {return getDecorView().findViewById(id);   }public abstract void setContentView(@LayoutRes int layoutResID);public abstract void onConfigurationChanged(Configuration newConfig);}

一个顶级窗口查看和行为的一个抽象基类。这个类的实例作为一个顶级View添加到Window Manager。它提供了一套标准的UI方法,比如添加背景,标题等等。当你需要用到Window的时候,你应该使用它的唯一实现类PhoneWindow。可以看到,Window是一个抽象基类,它提供了一系列窗口的方法,比如设置背景,标题等等,而它的唯一实现类则是PhoneWindow。

PhoneWindow

public class PhoneWindow extends Window implements MenuBuilder.Callback {//This is the top-level view of the window, containing the window decor.    private DecorView mDecor;// 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;}

可以看到,在PhoneWindow里面,出现了成员变量DecorView,而DecorView是继承与FrameLayout。

DecorView

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {private PhoneWindow mWindow;void setWindow(PhoneWindow phoneWindow) {        mWindow = phoneWindow;        Context context = getContext();        if (context instanceof DecorContext) {            DecorContext decorContext = (DecorContext) context;            decorContext.setPhoneWindow(mWindow);        }    }}

既然是FrameLayout,就可以加载布局文件,也就是说,我们那些标题栏,内容栏,顶级上看是加载在DecorView上的,而DecorView则是由PhoneWindow负责添加。

从setContentView源码流程分析

从Activity里面的setContentView,就是我们平常把布局内容显示到界面上的一个方法。

public void setContentView(View view) {getWindow().setContentView(view);initWindowDecorActionBar();}//mWindow = new PhoneWindow(),后续将介绍public Window getWindow() {return mWindow;}

这里的mWindow.setContentView(),实际上调用到的是它的实现类方法PhoneWindow.setContentView()。

public void setContentView(int layoutResID) {        // 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) {        //创建DecorView,并添加到mContentParent上            installDecor();        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            mContentParent.removeAllViews();        }        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                    getContext());            transitionTo(newScene);        } else {        //将要加载的资源添加到mContentParent上            mLayoutInflater.inflate(layoutResID, mContentParent);        }        mContentParent.requestApplyInsets();        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {        //回调通知表示完成界面加载            cb.onContentChanged();        }        mContentParentExplicitlySet = true;    }

如果当前内容还未放置到窗口,此时mContentParent==null,也就是第一次调用的时候,调用那个installDecor方法,而后将我们的资源文件通过LayoutInflater对象转换为View树,并且添加至mContentParent视图中。

private void installDecor() {mForceDecorInstall = false;        if (mDecor == null) {        //调用该方法创建new一个DecorView            mDecor = generateDecor(-1);            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);            mDecor.setIsRootNamespace(true);            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);            }        } else {            mDecor.setWindow(this);        }         //一开始DecorView未加载到mContentParent,所以此时mContentParent=null        if (mContentParent == null) {           //该方法将mDecorView添加到Window上绑定布局            mContentParent = generateLayout(mDecor);}
protected DecorView generateDecor(int featureId) {return new DecorView(context, featureId, this, getAttributes());}protected ViewGroup generateLayout(DecorView decor) {        // Apply data from current theme.        //根据当前设置的主题来加载默认布局        TypedArray a = getWindowStyle();        //如果你在theme中设置了window_windowNoTitle,则这里会调用到,其他方法同理,        //这里是根据你在theme中的设置去设置的        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {            // Don't allow an action bar if there is no title.            requestFeature(FEATURE_ACTION_BAR);        }        //是否有设置全屏        if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));        }                ...//省略其他加载资源                /*添加布局到DecorView,前面说到,DecorView是继承与FrameLayout,        它本身也是一个ViewGroup,而我们前面创建它的时候,只是调用了new DecorView,        此时里面并无什么东西。而下面的步奏则是根据用户设置的Feature来创建相应的默认布局主题。举个例子,如果我在setContentView之前调用了requestWindowFeature(Window.FEATURE_NO_TITLE),这里则会通过getLocalFeatures来获取你设置的feature,进而选择加载对应的布局,此时则是加载没有标题栏的主题,对应的就是R.layout.screen_simple*/        int layoutResource;        int features = getLocalFeatures();        // System.out.println("Features: 0x" + Integer.toHexString(features));        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {            layoutResource = R.layout.screen_swipe_dismiss;        } ... //省略其他判断方法        } else {            // Embedded, so no decoration is needed.            layoutResource = R.layout.screen_simple;            // System.out.println("Simple!");        }        mDecor.startChanging();        //选择对应布局创建添加到DecorView中        View in = mLayoutInflater.inflate(layoutResource, null);        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));        mContentRoot = (ViewGroup) in;        //@android:id/content添加到contentParent中        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);        ...        return contentParent;    }

首先generateLayout会根据当前用户设置的主题去设置对应的Feature,接着,根据对应的Feature来选择加载对应的布局文件,(Window.FEATURE_NO_TITLE)接下来通过getLocalFeatures来获取你设置的feature,进而选择加载对应的布局,这也就是为什么我们要在setContentView之前调用requesetFeature的原因。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:fitsSystemWindows="true">        <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" />    <FrameLayout        android:layout_width="match_parent"         android:layout_height="?android:attr/windowTitleSize"        style="?android:attr/windowTitleBackgroundStyle">        <TextView android:id="@android:id/title"             style="?android:attr/windowTitleStyle"            android:background="@null"            android:fadingEdge="horizontal"            android:gravity="center_vertical"            android:layout_width="match_parent"            android:layout_height="match_parent" />    FrameLayout>    <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>

DecorView只有一个子元素为LinearLayout。代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域。注意FrameLayout里面的id,@android:id/content ,我们setContentView的内容就是添加到这个FrameLayout中。

generateLayout的返回是contentParent,而它的获取则是ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

正是id为content的FrameLayout,接着就是将你setContentView的内容添加到mContentParent中。

mLayoutInflater.inflate(layoutResID, mContentParent);或者mContentParent.addView(view, params);

最后调用Callback来通知界面发生改变。Callback是Window里面的一个接口,里面声明了当界面更改触摸时调用的各种方法。

public interface Window.Callback {public boolean dispatchKeyEvent(KeyEvent event);public boolean dispatchKeyShortcutEvent(KeyEvent event);public boolean dispatchTouchEvent(MotionEvent event);public boolean onMenuOpened(int featureId, Menu menu);public void onContentChanged();public void onAttachedToWindow();public void onDetachedFromWindow();    ...}
public void setCallback(Callback callback) {mCallback = callback;}public final Callback getCallback() {return mCallback;}public class Activity extends ContextThemeWrapper        implements LayoutInflater.Factory2,        Window.Callback, KeyEvent.Callback,        OnCreateContextMenuListener, ComponentCallbacks2,        Window.OnWindowDismissedCallback { ... }

可以看到Activity里面实现了Window.Callback接口而里面onContentChanged则是空的,也就是我们可以通过重写该方法来监听布局内容的改变了。

总结

  • Window是一个抽象类,PhoneWindow则是Window的唯一实现类,提供了各种窗口操作的方法,比如设置背景标题ContentView等等;
  • 每一个Activity上面都有一个Window,可以通过getWindow获取;
  • DecorView,顶级视图,继承与FramentLayout,setContentView则是添加在它里面的@id/content里;
  • setContentView里面创建了DecorView,根据Theme,Feature添加了对应的布局文件,当setContentView设置显示后会回调Activity的onContentChanged方法;

更多相关文章

  1. 【Android】 dialog 设置maxHeight 最大高度
  2. android获取versionName和versionCode
  3. android 基础知识
  4. android 调用webservice
  5. 为ListActivity 添加Button
  6. 三步搞定:Vue.js调用Android原生操作
  7. Android(安卓)--- 图片处理的方法
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Android 水平的ListView
  2. AndroidStudio 编译项目报错 Android(安
  3. android view子类的构建
  4. android里图片下载工具类AsyncImageLoade
  5. Android状态栏通知
  6. android蓝牙开发入门到精通4------通信标
  7. Android Service学习之IntentService 深
  8. Android手机号、串号获取
  9. android获取屏幕的宽高
  10. Android获取手屏幕尺寸