1.Android 视图及View绘制分析笔记之setContentView

自从1983年第一台图形用户界面的个人电脑问世以来,几乎所有的PC操作系统都支持可视化操作,Android也不例外。对于所有Android Developer来说,我们接触最多的控件就是View。通常,我们使用自定义View,需要了解最多的除了事件分发,就是View的绘制过程。然而关于View的绘制,涉及到的知识点纷繁复杂,这么多的代码知识,要梳理起来,肯定是先要找个头。那么平常我们用的最多的方法是哪个方法呢?当然是setContentView()

setContentView

首先我们直接在Android Studio中找到一个Activity(请注意,本文分析的是Activity,如果你看的是AppCompatActivity,实际代码会有出入),然后找到setContent方法然后点进去,我们可以看到

public void setContentView(@LayoutRes int layoutResID) {    getWindow().setContentView(layoutResID);    ···}

然后查找getWindow()方法

private Window mWindow;public Window getWindow() {    return mWindow;}

得知调用的是Window类的setContent()方法。然后再全类搜索mWindow,在attach方法中找到了赋值语句。

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,        Window window) {    ···    mWindow = new PhoneWindow(this, window);    mWindow.setWindowControllerCallback(this);    mWindow.setCallback(this);    mWindow.setOnWindowDismissedCallback(this);    mWindow.getLayoutInflater().setPrivateFactory(this);    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {        mWindow.setSoftInputMode(info.softInputMode);    }    if (info.uiOptions != 0) {        mWindow.setUiOptions(info.uiOptions);    }    ···    mWindow.setWindowManager(            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),            mToken, mComponent.flattenToString(),            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);    if (mParent != null) {        mWindow.setContainer(mParent.getWindow());    }    mWindowManager = mWindow.getWindowManager();    ···}

然后就引出了我们今天主要分析的对象PhoneWindow。

PhoneWindow

查找PhoneWindow的setContentView方法,可以看到有三个重载方法。

@Overridepublic void setContentView(int layoutResID) {    if (mContentParent == null) {        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 {        mLayoutInflater.inflate(layoutResID, mContentParent);    }    mContentParent.requestApplyInsets();    final Callback cb = getCallback();    if (cb != null && !isDestroyed()) {        cb.onContentChanged();    }    mContentParentExplicitlySet = true;}

@Overridepublic void setContentView(View view) {    setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}

@Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) {    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, params);    }    mContentParent.requestApplyInsets();    final Callback cb = getCallback();    if (cb != null && !isDestroyed()) {        cb.onContentChanged();    }    mContentParentExplicitlySet = true;}

三个方法大体的处理流程是:

  1. 初始化DecorView
  2. 检查并处理转场动画
  3. 将实际要显示的Layout或者View添加到mContentParent中
  4. 通知Callback(即Activity)调用onContentChanged方法

DecorView

在上面的流程里面,相对比较重要的就是第一步的初始化DecorView,也就是installDecor()方法,下面我们继续分析这个方法。

private void installDecor() {    ···    if (mDecor == null) {        mDecor = generateDecor(-1);        ···        }    } else {        mDecor.setWindow(this);    }    if (mContentParent == null) {        mContentParent = generateLayout(mDecor);        ···    }    ···}

installDecor()方法里,主要做的就是两件事,一个是generateDecor,生成mDecor,还有一个是generateLayout,生成mContentParent。generateDecor中,没有太多复杂逻辑,就是做一些判断,然后实例化出来一个DecorView,这里需要说一点的就是在Android7.0以前,DecorView是PhoneWindow的内部类,7.0以后,DecorView单独提出来变成了一个类,所以如果有用到反射的话,这里可能会出现问题,需要做好版本判断。然后我们看下generateLayout的逻辑:

protected ViewGroup generateLayout(DecorView decor) {    //通过系统获取样式    TypedArray a = getWindowStyle();    //然后一系列的判断,获取样式里的属性,然后设置features    //例如是否悬浮,是否有Title等等    ···    int layoutResource;    int features = getLocalFeatures();    //拿到刚才设置的features,作出一系列if eles判断,找出对应的resourceId    //然后调用DecorView的onResourcesLoaded,对这个layout进行inflate    ···    mDecor.startChanging();    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

//通过com.android.internal.R.id.content找到对应的mContentParent    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);    if (contentParent == null) {        throw new RuntimeException("Window couldn‘t find content container view");    }    //在经过一系列后续设置    ···    mDecor.finishChanging();    //最后返回contentParent    return contentParent;}

在generateLayout中,大概的流程是这样的。

  1. 先获取样式
  2. 设置样式到Window的features里
  3. 拿到features,判断对应的resourceId
  4. 通过resourceId,inflate出来一个ViewGroup,添加到mDecor中
  5. 再通过findViewById,找到刚刚inflate的ViewGroup中的 com.android.internal.R.id.content,作为mContentParent
  6. 在经过一系列设置
  7. 返回contentParent

小结

到此,我们的setContentView就已经基本走完了,剩下的就等着Activity、WindowManager、WindowManagerGlobal、ViewRootImpl去调用了,这些类的调用,涉及到了Activity的启动流程,我们会在其他笔记中详细分析这一过程。

下面会上一张整个setContentView的时序图,用来巩固一下刚才的流程。

上图为Android setContentView的时序图

系列文章

Android 视图及View绘制分析笔记之setContentView
View绘制分析笔记之onMeasure
View绘制分析笔记之onLayout
View绘制分析笔记之onDraw

时间: 2024-12-25 16:58:10

1.Android 视图及View绘制分析笔记之setContentView的相关文章

3.View绘制分析笔记之onLayout

上一篇文章我们了解了View的onMeasure,那么今天我们继续来学习Android View绘制三部曲的第二步,onLayout,布局. ViewRootImpl#performLayout private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { mLayoutRequested = false; mScrollMayChange

2.View绘制分析笔记之onMeasure

今天主要学习记录一下Android View绘制三部曲的第一步,onMeasure,测量. 起源 在Activity中,所有的View都是DecorView的子View,然后DecorView又是被ViewRootImpl所控制,当Activity显示的时候,ViewRootImpl的performTranversals方法开始运行,这个方法很长,不过核心的三个流程就是依次调用performMeasure.performLayout.performDraw三个方法,从而完成DecorView的绘

4.View绘制分析笔记之onDraw

上一篇文章我们了解了View的onLayout,那么今天我们来学习Android View绘制三部曲的最后一步,onDraw,绘制. ViewRootImpl#performDraw private void performDraw() { ··· final boolean fullRedrawNeeded = mFullRedrawNeeded; mFullRedrawNeeded = false; mIsDrawing = true; Trace.traceBegin(Trace.TRAC

Android面试,View绘制流程以及invalidate()等相关方法分析

整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为 根据之前设置的状态,判断是否需要重新计算视图大小(measure).是否重新需要安置视图的位置(layout).以及是否需要重绘 (draw),其框架过程如下: measure()过程 主要作用:为整个View树计算实际的大小,即设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性:mMeasureWidth),每个View的控件的实际宽高

Android应用自定义View绘制方法手册

背景 这篇迟迟难产的文章算是对2015前半年的一个交代吧,那时候有一哥们要求来一发Android Canvas相关总结,这哥们还打赏了,实在不好意思,可是这事一放就给放忘了,最近群里小伙伴催着说没更新博客,坐等更新啥的,随先有这么一篇Android应用开发超级基础的文章诞生了(因为这种文章最好写哈,就是用熟了就行).不得不说下这么久为何一直没更新博客的原因了,首先遇上了过年,我个人崇尚过节就该放下一切好好陪陪亲人,珍惜在一起的时光:其次今年开年很是蛋疼,不是不顺当就是深深的觉得被坑,所以心情也就

Android视图控件架构分析之View、ViewGroup

在Android中,视图控件大致被分为两类,即ViewGroup和View,ViewGroup控件作为父控件,包含并管理着子View,通过ViewGroup和View便形成了控件树,各个ViewGoup对象和View对象就是控件树中的节点.在控件树中,以树的深度来遍历查找对应的控件元素,同时,上层控件负责子控件的测量与绘制,并传递交互事件. Android控件树: AndroidUI界面架构图: 一.测量View的工具类:MeasureSpec 1.MeasureSpec包含了测量的模式和测量的

Android GUI之View绘制流程

在上篇文章中,我们通过跟踪源码,我们了解了Activity.Window.DecorView以及View之间的关系(查看文章:http://blog.csdn.net/jerehedu/article/details/47021541).那么整个Activity的界面到底是如何绘制出来的呢?既然DecorView作为Activity的顶层界面视图,那么整个界面的绘制工作应该从它开始,下面我们继续跟踪源码,看看是不是这样的. Activity在启动过程中会调用主线程ActivityThread中的

Android视图动画-View Animation

视图动画主要有两种: 一.Tween Animation译为"补间动画" 1.scale译为"规模.比例",是对View进行特定范围的缩放2.alpha通过改变View的透明度实现View隐现的效果3.translate译为"转移",是对View进行位置的移动4.rotate译为"旋转",是让View围绕特定的点进行旋转PS:所有View的移动.隐藏.旋转仅仅是看到的动画效果,实际View的位置/大小/比例并没有发生本质上的改

读android系统源码情景分析笔记(一)

android2.3系统驱动模块由内核模块,硬件抽象模块,用户调用模块(jni)组成,内核模块和linux一致,硬件抽象模块主要需要定义两个结构:hw_module_t和hw_device_t. hw_module_t包含模块id,版本号,署名和一个函数指针: int open (struct hw_module_t* module, const char* id, struct hw_device_t** device); hw_device_t包含版本,hw_module_t的引用和一个函数