• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >Android > 深入理解 Android 之 View 的绘制流程,androidview

深入理解 Android 之 View 的绘制流程,androidview

作者:网友 字体:[增加 减小] 来源:互联网 时间:2017-05-26

网友通过本文主要向大家介绍了android view绘制流程,android recyclerview,android scrollview,android surfaceview,android photoview等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

深入理解 Android 之 View 的绘制流程,androidview


概述

本篇文章会从源码(基于Android 6.0)角度分析Android中View的绘制流程,侧重于对整体流程的分析,对一些难以理解的点加以重点阐述,目的是把View绘制的整个流程把握好,而对于特定实现细节则可以日后再对相应源码进行研读。
在进行实际的分析之前,我们先来看下面这张图:



我们来对上图做出简单解释:DecorView是一个应用窗口的根容器,它本质上是一个FrameLayout。DecorView有唯一一个子View,它是一个垂直LinearLayout,包含两个子元素,一个是TitleView(ActionBar的容器),另一个是ContentView(窗口内容的容器)。关于ContentView,它是一个FrameLayout(android.R.id.content),我们平常用的setContentView就是设置它的子View。上图还表达了每个Activity都与一个Window(具体来说是PhoneWindow)相关联,用户界面则由Window所承载。

Window

Window即窗口,这个概念在Android Framework中的实现为android.view.Window这个抽象类,这个抽象类是对Android系统中的窗口的抽象。在介绍这个类之前,我们先来看看究竟什么是窗口呢?

实际上,窗口是一个宏观的思想,它是屏幕上用于绘制各种UI元素及响应用户输入事件的一个矩形区域。通常具备以下两个特点:

  • 独立绘制,不与其它界面相互影响;
  • 不会触发其它界面的输入事件;

在Android系统中,窗口是独占一个Surface实例的显示区域,每个窗口的Surface由WindowManagerService分配。我们可以把Surface看作一块画布,应用可以通过Canvas或OpenGL在其上面作画。画好之后,通过SurfaceFlinger将多块Surface按照特定的顺序(即Z-order)进行混合,而后输出到FrameBuffer中,这样用户界面就得以显示。

android.view.Window这个抽象类可以看做Android中对窗口这一宏观概念所做的约定,而PhoneWindow这个类是Framework为我们提供的Android窗口概念的具体实现。接下来我们先来介绍一下android.view.Window这个抽象类。

这个抽象类包含了三个核心组件:

  • WindowManager.LayoutParams: 窗口的布局参数;
  • Callback: 窗口的回调接口,通常由Activity实现;
  • ViewTree: 窗口所承载的控件树。

下面我们来看一下Android中Window的具体实现(也是唯一实现)——PhoneWindow。

PhoneWindow

前面我们提到了,PhoneWindow这个类是Framework为我们提供的Android窗口的具体实现。我们平时调用setContentView()方法设置Activity的用户界面时,实际上就完成了对所关联的PhoneWindow的ViewTree的设置。我们还可以通过Activity类的requestWindowFeature()方法来定制Activity关联PhoneWindow的外观,这个方法实际上做的是把我们所请求的窗口外观特性存储到了PhoneWindow的mFeatures成员中,在窗口绘制阶段生成外观模板时,会根据mFeatures的值绘制特定外观。

从setContentView()说开去

在分析setContentView()方法前,我们需要明确:这个方法只是完成了Activity的ContentView的创建,而并没有执行View的绘制流程。
当我们自定义Activity继承自android.app.Activity时候,调用的setContentView()方法是Activity类的,源码如下:

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

getWindow()方法会返回Activity所关联的PhoneWindow,也就是说,实际上调用到了PhoneWindow的setContentView()方法,源码如下:

@Override
public void setContentView(int layoutResID) {
  if (mContentParent == null) {
    // mContentParent即为上面提到的ContentView的父容器,若为空则调用installDecor()生成
    installDecor();
  } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    // 具有FEATURE_CONTENT_TRANSITIONS特性表示开启了Transition
    // mContentParent不为null,则移除decorView的所有子View
    mContentParent.removeAllViews();
  }
  if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    // 开启了Transition,做相应的处理,我们不讨论这种情况
    // 感兴趣的同学可以参考源码
    . . .
  } else {
    // 一般情况会来到这里,调用mLayoutInflater.inflate()方法来填充布局
    // 填充布局也就是把我们设置的ContentView加入到mContentParent中
    mLayoutInflater.inflate(layoutResID, mContentParent);
  }
  . . .
  // cb即为该Window所关联的Activity
  final Callback cb = getCallback();
  if (cb != null && !isDestroyed()) {
    // 调用onContentChanged()回调方法通知Activity窗口内容发生了改变
    cb.onContentChanged();
  }

  . . .
} 

LayoutInflater.inflate()

在上面我们看到了,PhoneWindow的setContentView()方法中调用了LayoutInflater的inflate()方法来填充布局,这个方法的源码如下:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
  return inflate(resource, root, root != null);
}

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
  final Resources res = getContext().getResources();
  . . .
  final XmlResourceParser parser = res.getLayout(resource);
  try {
    return inflate(parser, root, attachToRoot);
  } finally {
    parser.close();
  }
}

在PhoneWindow的setContentView()方法中传入了decorView作为LayoutInflater.inflate()的root参数,我们可以看到,通过层层调用,最终调用的是inflate(XmlPullParser, ViewGroup, boolean)方法来填充布局。这个方法的源码如下:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
  synchronized (mConstructorArgs) {
    . . .
    final Context inflaterContext = mContext;
    final AttributeSet attrs = Xml.asAttributeSet(parser);
    Context lastContext = (Context) mConstructorArgs[0];
    mConstructorArgs[0] = inflaterContext;

    View result = root;

    try {
      // Look for the root node.
      int type;
      // 一直读取xml文件,直到遇到开始标记
      while ((type = parser.next()) != XmlPullParser.START_TAG &&
          type != XmlPullParser.END_DOCUMENT) {
        // Empty
       }
      // 最先遇到的不是开始标记,报错
      if (type != XmlPullParser.START_TAG) {
        throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
      }

      final String name = parser.getName();
      . . .
      // 单独处理<merge>标签,不熟悉的同学请参考官方文档的说明
      if (TAG_MERGE.equals(name)) {
        // 若包含<merge>标签,父容器(即root参数)不可为空且attachRoot须为true,否则报错
        if (root == null || !attachToRoot) {
          throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
        }

        // 递归地填充布局
        rInflate(parser, root, inflaterContext, attrs, false);
     } else {
        // temp为xml布局文件的根View
        final View temp = createViewFromTag(root, name, inflaterContext, attrs); 
        ViewGroup.LayoutParams params = null;
        if (root != null) {
          . . .
          // 获取父容器的布局参数(LayoutParams)
          params = root.generateLayoutParams(attrs);
          if (!attachToRoot) {
            // 若attachToRoot参数为false,则我们只会将父容器的布局参数设置给根View
            temp.setLayoutParams(params);
          }

        }

        // 递归加载根View的所有子View
        rInflateChildren(parser, temp, attrs, true);
        . . .

        if (root != null && attachToRoot) {
          // 若父容器不为空且attachToRoot为true,则将父容器作为根View的父View包裹上来
          root.addView(temp, params);
        }

        // 若root为空或是attachToRoot为false,则以根View作为返回值
        if (root == null || !attachToRoot) {
           result = temp;
        }
      }

    } catch (XmlPullParserException e) {
      . . . 
    } catch (Exception e) {
      . . . 
    } finally {

      . . .
    }
    return result;




 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 订单流程view,订单view
  • 深入理解 Android 之 View 的绘制流程,androidview
  • Android View的绘制流程,androidview绘制
  • android View绘制源码分析

相关文章

  • 2017-05-26谷歌IAP:skusBundle array associated with key ITEM_ID_LIST cannot contain more than 20 items.,jsarraycontain
  • 2017-05-26RecyclerView再封装,RecyclerView封装
  • 2017-05-26Android开发笔记(6)——类的设定与继承,android笔记
  • 2017-05-26Xamarin.Forms+Prism(2)—— 基本使用 NavigationService,xamarin.forms
  • 2017-05-26Android开发技术周报 Issue#67
  • 2017-05-26AppManager,androidappmanager
  • 2017-05-26Android应用项目中BaseAdapter、SimpleAdapter和ArrayAdapter中的三种适配器,simplearrayadapter
  • 2017-05-26Android 6.0 Permission权限与安全机制,androidpermission
  • 2017-05-26利用zabbix监控国际线路的延迟情况
  • 2017-05-26第一节 搭建开发环境,第一节搭建开发

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 简单的数据类型转换的工具类,数据类型工具类
    • Android 手机卫士--设置界面&amp;功能列表界面跳转逻辑处理,android卫士--界面
    • 自定义控件添加自定义属性问题,控件添加自定义属性
    • 微信支付,信支付
    • ListView 添加 HeaderView常见错误,listviewheaderview
    • Android面试准备 第二天 第五例 数据存储
    • 菜鸟新闻3--ViewDragHelper详解(侧滑栏),viewdraghelper侧滑
    • Android 手机卫士--导航界面2,android卫士
    • android开发学习——小细节注意,android开发细节
    • Android GreenDAO3.0——介绍,androidgreendao3.0

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有