zoukankan      html  css  js  c++  java
  • 《android开发艺术探索》读书笔记(四)--View工作原理

    接上篇《android开发艺术探索》读书笔记(三)

    No1:

    View的三大流程:测量流程、布局流程、绘制流程

    No2:

    ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。

    在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。

    No3:

    View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measurelayoutdraw三个过程才能最终将一个View绘制出来,measure用来测量View的宽和高,layout用来确定View在父容器中的放置位置(View的最终宽/高和四个顶点的位置),draw负责View绘制在屏幕上。

    No4:

    DecorView其实是一个FrameLayout,View层的事件都先经过DecorView,然后才传递给我们的View。

    No5:

    performTraversals会依次调用performMeasureperformLayoutperformDraw三个方法,这三个方法分别完成顶级View的measure、layout和draw这三个流程。

    其中performMeasure会调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到子元素中了,这样就完成了依次measure过程。接着子元素会重复父容器的measure过程,如此反复就完成了整个View树的遍历。

    No6:

    子元素的MeasureSpec的创建与父容器的MeasureSpec和子元素本身的LayoutParams有关,此外还和View的margin及padding有关。

    getChildMeasureSpce的主要作用是根据父容器的MeasureSpec同时结合View本身的LayoutParams来确定子元素的MeasureSpec,参数中的padding是指父容器中已占用的空间大小,因此子元素可用的大小为父容器的尺寸减去padding

    No7:

    measure的两种情况:

    1.如果只是一个原始的View,那么通过measure方法就完成了其测量过程

    2.如果是一个ViewGroup,除了完成自己的测量过程外,还会遍历去调用所有子元素的measure方法,各个子元素再递归去执行这个流程。

    No8:

    View的measure方法是一个final类型的方法,这意味着子类不能重写此方法。

    No9:

    measureChild的思想就是取出子元素的LayoutParams,然后再通过getChildMeasureSpec来创建子元素的MeasureSpec,接着将MeasureSpec直接传递给View的measure方法来进行测量。

    No10:

    如何解决onCreate、onStart、onResume中无法得到某个View的宽、高信息?

    1.Activity、View的onWindowFocusChanged:View已经初始化完毕了,宽高已经准备好了

    public void onWindowFocusChanged(boolean hasFocus){
         super.onWindowFocusChanged(hasFocus);
         if(hasFocus){
                int width = view.getMeasuredWidth();
                int height = view.getMeasuredHeight();
         }
    }

    2.view.post(runnable):通过post可以将runnable投递到消息队列的尾部,然后等待Looper调用此runnable的时候,View也已经初始化好了

    protected void onStart(){
        super.onStart();
        view.post(new Runnable(){
            @Override
            public void run(){
                int width = view.getMeasuredWidth();
                int height = view.getMeasuredHeight();
            }
        });
    }

    3.ViewTreeObserver--当View树的状态发生改变或者View树内部的View的可见性发生改变时,onGlobalLayou方法将被回调,因此这是获取View的宽高一个很好的时机

    protected void onStart(){
        super.onStart();
        ViewTreeObserver observer = view.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout(){
                view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                int width = view.getMeasureWidth();
                int height = view.getMeasureHeight();
            }
        });
    }

    4.view.measure(int widthMeasureSpec,int heightMeasureSpec)

    根据View的LayoutParams来分情况处理

    match_parent直接放弃

    具体的数值(px)

    int widthMeasureSpec = MeasureSpec.makeMeasureSpec(100,MeasureSpec.EXACTLY);
    int heightMeasureSpec = MeasureSpec.makeMeasureSpec(100,MeasureSpec.EXACTLY);
    view.measure(widthMeasureSpec,heightMeasureSpec);

    wrap_content

    int widthMeasureSpec = MeasureSpec.makeMeasureSpec((1<<30)-1,MeasureSpec.AT_MOST);
    int heightMeasureSpec = MeasureSpec.makeMeasureSpec((1<<30)-1,MeasureSpec.AT_MOST);
    view.measure(widthMeasureSpec,heightMeasureSpec);

    No11:

    layout方法大致流程:首先通过setFrame方法来设定View的四个顶点的位置,即初始化mLeft、mRight、mTop和mBottom这四个值,View的四个顶点一旦确定,那么View在父容器中的位置也就确定了;接着会调用onLayout方法,这个方法的用途是父容器确定子元素的位置。

    No12:

    View的测量宽/高最终宽/高有什么区别(View的getMeasuredWidth/getMeasuredHeitght和getWidth/getHeight)

    在View的默认实现中,View的测量宽/高和最终宽/高是相等的,只不过测量宽/高形成于View的measure过程,而最终宽/高形成于View的layout过程,即两者的赋值时机不同,测量宽/高的赋值时机稍微早一些。

    如果重写View的layout方法,才会导致View的最终宽/高和测量宽/高不一致

    No13:

    View的绘制过程步骤:

    1.绘制背景background.draw(canvas)

    2.绘制自己onDraw

    3.绘制children(dispatchDraw)

    4.绘制装饰(onDrawScrollBars)

    No14:

    View绘制过程的传递是通过diapatchDraw来实现的,dispatchDraw会遍历调用所有子元素的draw方法,如此draw事件就一层层地传递了下去。

    No15:

    自定义View

    1.继承View重写onDraw方法:主要用于实现一些不规则的效果

    2.继承ViewGroup派生特殊的Layout:主要用于实现自定义的布局

    3.继承特定的View(比如TextView):用于扩展某种已有的View的功能

    4.继承特定的ViewGroup(比如LinearLayout):效果看起来像几种View组合在一起的时候

    No16:

    自定义View须知:

    1.让View支持warp_content--对于直接继承自View的控件,如果不对wrap_content做特殊处理,那么使用wrap_content就相当于使用match_parent

    2.如果有必要,让你的View支持padding--因为padding默认不生效

    3.尽量不要在View中使用Handler,没必要--因为View内部本身就提供了post方法

    4.View中如果有线程或者动画,需要及时停止,参考View#onDetchedFromWindow--防止内存泄露

    5.View带有滑动嵌套情形时,需要处理好滑动冲突

    No17:

    自定义属性:请看我的另一篇自定义属性

    No18:

    MeasureSpec代表一个32位int值,高2位代表SpecMode,低30位代表SpecSize,SpecMode是指测量模式,而SpecSize是指在某种测量模式下的规格大小。

    系统内部是通过MeasureSpec来进行View的测量,但是正常情况下我们使用View指定MeasureSpec,尽管如此,但是我们可以给View设置LayoutParams。在View测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据这个MeasureSpec来确定View测量猴的宽/高。

  • 相关阅读:
    关于PCA算法的一点学习总结
    c++大作业--学籍管理系统--
    AVC1与H264的差别
    Java多线程之wait(),notify(),notifyAll()
    jquery——zTree, 完美好用的树插件
    一位Erlang程序猿的自白
    SQL Server 中关于EXCEPT和INTERSECT的使用方法
    JAVA程序猿怎么才干高速查找到学习资料?
    CFileDialog的使用方法简单介绍
    XCode 打包问题巧遇
  • 原文地址:https://www.cnblogs.com/anni-qianqian/p/8243985.html
Copyright © 2011-2022 走看看