android初级学习之深入了解View绘制流程(一)
2016-11-09 13:44
645 查看
View是android开发中必不可少的,之前也只是停留在简单使用状态上,如使用setContentView()设置布局呀,但背后原理还没深入了解。知其然而不知其所以然乃技术学习之大忌。趁有空,稍微整理一下,也稍微深究一下,并记录下来,聊以不时之需。
老样子,先应用再深究。
LayoutInflater的用法比较简单,先获取LayoutInflater实例:
或:
其实第一种写法就是第二种的写法的一个简单写法,这里android为我们做了一个简单的封装。
获取实例后就是通过inflate(resourceId, root)方法来加载布局了。
接着是创建一个TextView的简单布局layout_tv:
最后就是写相关逻辑代码了:
运行结果:
这是一个比较简单的实例,但也比较好的诠释了LayoutInflate的使用。接下来,我们一起来探究一下其背后的“阴谋”。
翻看源码发现,无论你调用哪个inflate()方法,最终都将调用
接着查看最终调用的:
原来LayoutInflate是使用xmlpullparser来解析xml文件的。这里,我们主要看一下482这行。这行调用了createViewFromTag(root, name, attrs, false)方法,根据方法名大概可以猜出该方法用于根据节点名来创建View对象的。确实如此,在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回。到此,已经创建了一个根实例。
接下来看504行。这里调用了rInflate()方法。该方法是用来循环遍历这个根布局下的子元素。
在第806行同样是createViewFromTag()方法来创建View的实例,然后还会在第809行递归调用rInflate()方法来查找这个View下的子元素,每次递归完成后则将这个View添加到父布局当中,最终完成将控件加载的任务。
1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。
2. 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。
3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。
4. 在不设置attachToRoot参数的情况下,如果root不null,attachToRoot参数默认为true。
这里对我们的TextView稍微做一下改变,使其变高或变小,为方便区分,将其背景颜色设置一下。
layout_tv.xml:
运行结果:
哎哎哎,我明明改变了TextView中的layout_width和layout_height,可是为什么显示却没任何反应?先别急。其实主要是因为layout_width和layout_height失去其作用了。layout_width和layout_height这两个属性是用来表示控件在布局中的大小的,这和表示控件的大小有质的区别!!!这由于我们的TextView只是一个控件布局文件,并没有在任何布局中,自然layout_width和layout_height这两个属性就失去其作用了。大家是不是又有疑问了。我们明明指定控件文件也是可以显示的呀,不信你看:
运行结果:
这不就发挥其作用了吗?
其实,调用setContentView()时,系统就默认为我们嵌套了一个Framelayout。故而,TextView中的layout_width和layout_height能发挥作用,有兴趣的可以获取当前布局的父布局并在locat中打印出来就可以看到是一个Framelayout了。这其中涉及到android的控件架构问题。
每个Activity都包含一个Window对象,在android中通常由PhoneWindow来实现。而PhoneWindow将DecorView设置为整个应用窗口的根View。DecorView作为窗口界面的顶层视图,并封装了一些窗口操作的通用方法。可以说,DecorView将要显示的内容呈现在PhoneWindow上,这里面的View监听事件,都通过WindowMannagerService来进行接收。在显示上,它将屏幕分为两部分,TitleView和ContenView。ContenView就是一个ID为content的Framelayout,每当代用setContentView时,就将布局文件加载在一个这样的Framelayout中。据此,我们可以建立一颗View视图树:
这也可以解释为什么requestWindowFeature()为什么要在setContenView()之前调用了。
其实,其中还封装了一个方法onReasume(),每当我们在onCreate()中调用setContenView()时,ActivityManagerService会回调onReasume()方法,这样系统才会把整个DecorView添加到PhoneWindow中,并让其显示出来,从而完成界面的绘制。
LayoutInflater
View的绘制首先得从LayoutInflater说起:老样子,先应用再深究。
LayoutInflater的用法比较简单,先获取LayoutInflater实例:
LayoutInflater mInflater = LayoutInflater.from(context);
或:
LayoutInflater mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
其实第一种写法就是第二种的写法的一个简单写法,这里android为我们做了一个简单的封装。
获取实例后就是通过inflate(resourceId, root)方法来加载布局了。
mInflater.inflate(resourceId, root);
第一个参数就是要加载的布局id,第二个参数是指给该布局的外部再嵌套一层父布局,如果不需要就直接传null。这样就成功创建了一个布局的实例,之后再将它添加到指定的位置就可以显示出来了。 下面是一个简单的实例,通过LayoutInflater来在一个空布局上加载一个TextView并显示出来: 首先创建一个空布局文件activity_inflate.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > </LinearLayout>
接着是创建一个TextView的简单布局layout_tv:
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="通过LayoutInflate加载控件布局" android:textSize="50sp" > </TextView>
最后就是写相关逻辑代码了:
package com.yixingu.demo.view; /* * 演示LayoutInflater加载控件布局 * */ import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import com.yixingu.demo.R; public class LayoutInflaterDemo extends Activity { private LinearLayout mLayout; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_inflater); mLayout = (LinearLayout)findViewById(R.id.main_layout); LayoutInflater mInflate = LayoutInflater.from(this); View mTextViewLayout = mInflate.inflate(R.layout.layout_tv, null); mLayout.addView(mTextViewLayout); } }
运行结果:
这是一个比较简单的实例,但也比较好的诠释了LayoutInflate的使用。接下来,我们一起来探究一下其背后的“阴谋”。
翻看源码发现,无论你调用哪个inflate()方法,最终都将调用
inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)这个方法,请看:
接着查看最终调用的:
原来LayoutInflate是使用xmlpullparser来解析xml文件的。这里,我们主要看一下482这行。这行调用了createViewFromTag(root, name, attrs, false)方法,根据方法名大概可以猜出该方法用于根据节点名来创建View对象的。确实如此,在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回。到此,已经创建了一个根实例。
接下来看504行。这里调用了rInflate()方法。该方法是用来循环遍历这个根布局下的子元素。
在第806行同样是createViewFromTag()方法来创建View的实例,然后还会在第809行递归调用rInflate()方法来查找这个View下的子元素,每次递归完成后则将这个View添加到父布局当中,最终完成将控件加载的任务。
inflate()的参数含义
这里需要注意的是inflate(int resource, ViewGroup root, boolean attachToRoot)的三个参数,根据字面意思大概可以猜出resource代表要加载的布局文件资源,root代表父布局,attachToRoot代表是否和父布局建立联系。那么他么之间有什么联系呢?
1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。
2. 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。
3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。
4. 在不设置attachToRoot参数的情况下,如果root不null,attachToRoot参数默认为true。
这里对我们的TextView稍微做一下改变,使其变高或变小,为方便区分,将其背景颜色设置一下。
layout_tv.xml:
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffff00" android:text="通过LayoutInflate加载控件布局" android:textSize="50sp" > </TextView>
运行结果:
哎哎哎,我明明改变了TextView中的layout_width和layout_height,可是为什么显示却没任何反应?先别急。其实主要是因为layout_width和layout_height失去其作用了。layout_width和layout_height这两个属性是用来表示控件在布局中的大小的,这和表示控件的大小有质的区别!!!这由于我们的TextView只是一个控件布局文件,并没有在任何布局中,自然layout_width和layout_height这两个属性就失去其作用了。大家是不是又有疑问了。我们明明指定控件文件也是可以显示的呀,不信你看:
package com.yixingu.demo.view; /* * 演示LayoutInflater加载控件布局 * */ import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import com.yixingu.demo.R; public class LayoutInflaterDemo extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.layout_tv); } }
运行结果:
这不就发挥其作用了吗?
其实,调用setContentView()时,系统就默认为我们嵌套了一个Framelayout。故而,TextView中的layout_width和layout_height能发挥作用,有兴趣的可以获取当前布局的父布局并在locat中打印出来就可以看到是一个Framelayout了。这其中涉及到android的控件架构问题。
android控件架构
我们先看一下android界面的架构图:每个Activity都包含一个Window对象,在android中通常由PhoneWindow来实现。而PhoneWindow将DecorView设置为整个应用窗口的根View。DecorView作为窗口界面的顶层视图,并封装了一些窗口操作的通用方法。可以说,DecorView将要显示的内容呈现在PhoneWindow上,这里面的View监听事件,都通过WindowMannagerService来进行接收。在显示上,它将屏幕分为两部分,TitleView和ContenView。ContenView就是一个ID为content的Framelayout,每当代用setContentView时,就将布局文件加载在一个这样的Framelayout中。据此,我们可以建立一颗View视图树:
这也可以解释为什么requestWindowFeature()为什么要在setContenView()之前调用了。
其实,其中还封装了一个方法onReasume(),每当我们在onCreate()中调用setContenView()时,ActivityManagerService会回调onReasume()方法,这样系统才会把整个DecorView添加到PhoneWindow中,并让其显示出来,从而完成界面的绘制。
相关文章推荐
- Android 进阶学习:Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android 进阶学习:Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二) ---站在巨人的肩膀上学习总结
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图自定义View绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Android视图绘制流程完全解析,带你一步步深入了解View(二)