Android View总结

来源:互联网 发布:带端口的域名解析 编辑:程序博客网 时间:2024/06/07 01:10

Android 中包括两类基类:View 和 ViewGroup

View 是 Android 中重要的类,它有众多的重要的方法,比如 onMeasure,onLayout,onTouchEvent等 ViewGroup 是容器,可以包含多个View,比如典型的 LinearLayout , Framelayout等

Android 窗口组成

  1. Activity
  2. └── PhoneWindow
  3. └── DecorView (FrameLayout)
  4. └── LinearLayout
  5. ├── TitleView (FrameLayout)
  6. └── ContentView (FrameLayout)

DecorView 是每一个可见Activity窗口的根 View,它本身是继承自 FrameLayout,是拓展的 FrameLayout 类,实现了窗口的一些操作,其对象内部有两个对象,分别是 TitleView 和 ContentView,也就是我们在 onCreate() 方法中用到的setContentView()。我们在activity_main.xml 中定义的布局都是 addView 到了 ContentView 中。ActionBar 属于 TitleView,如果要去除 ActionBar 需要在 onCreate() 方法中调用 requestWindowFeature(Windwo.FEATURE_NO_TITLE)。 DecorView 将在调用 setContentView() 时被添加到 PhoneWindow 中,并显示出来,这也就是为什么 requestWindowFeature 需要在 setContentView 之前调用才有效的原因了,一旦执行了 setContentView, DecorView 已经被添加到了 PhoneWindow 中,requestWindowFeature 对其不发生作用。

  1. setContentView() -> {
  2. getWindow().setContentView(){
  3. installDecor() -> {
  4. generateDecor() -> { mDecor = new DecorView() }
  5. mContentParent = generateLayout(mDecor)
  6. }
  7. mContentParent.addView()
  8. }
  9. initWindowDecorActionBar() -> {
  10. getWindow().getDecorView() -> {
  11. mActionBar = new WindowDecorActionBar(this)
  12. }
  13. }
  14. }

注:此处是简化过的调用过程,并非具体过程。-> 表示调用或者实现

可以看到,Activity 中的setContentView(v) 实际是将 v 添加到了 PhoneWindow 的 mContentParent 中。

View 树

也就是所说的控件树。由多个 ViewGroup 和 View 组成。

View 的绘制要经过三个流程,measure、layout、draw。这三个方法分别调用onMeasure、onLayout、onDraw

关于 View 的 measure 流程,下面的文章总结的太好了,就不再赘述了。 Android View体系(七)从源码解析View的measure流程

流程

  1. measure -> onMeasure(widthSpec, heightSpec)
  1. // 测量规则
  2. 1. MeasureSpec.EXACTLY // 精确
  3. 2. MeasureSpec.AT_MOST // 最大值
  4. 3. MeasureSpec.UNSPECIFIED //不确定
  5. 对应关系:
  6. match_parent "固定值" -> EXACTLY -> 直接赋值父控件传递的值
  7. / 给定了新值 -> min( 新值,父控件传递的值 )
  8. wrap_content -> AT_MOST
  9. \ 没有给定新值 -> 直接赋值父控件传递的值

对于系统自带控件,内部实现了 测量新值的逻辑,自定义控件,需要自己写。下面以 TextView 为例,源码中这样写道:

  1. onMeasure(widthSpec, heightSpec){
  2. match_parent -> EXACTLY -> 直接赋值 width = widthSpec.getSize
  3. else {
  4. des = desired(mLayout) { max( TextView 中每一行文字的个数*cell )}
  5. width = des
  6. width = max(
  7. width,
  8. getSuggestMinimumWidth(){ min(
  9. mMinWidth, // layout 中的 minWidth 属性,默认 0
  10. mBackgroud.getMinimumWidth() // layout 中的 background 属性
  11. )}
  12. )
  13. if (wrap_content -> AT_MOST) {
  14. width = min( width, widthSpec.getSize )
  15. }
  16. }
  17. ... // 此处省略了 height 的测量
  18. setMeasuredDimension(width, height);
  19. }

而对于我们简单的使用 TextView, 不设置 minWidth,不设置背景(这也是最常用的设置),一下为简化流程

  1. onMeasure(widthSpec, heightSpec){
  2. int specSzie = widthSpec.getSize()
  3. if( match_parent || "?dp" -> EXACTLY ) {
  4. width = specSzie
  5. }else {
  6. width = max( TextView 每一行的字数 * 字宽) // 系统测量关键步骤
  7. if( wrap_content -> AT_MOST ) {
  8. width = min(width, specSzie)
  9. }
  10. }
  11. setMeasuredDimension(width, height);
  12. }
0 0
原创粉丝点击