Android UI 绘制过程浅析(三)layout过程

前言

  上一篇blog中,了解到measure过程对View进行了测量,得到measuredWidth/measuredHeight。对于ViewGroup,则计算出全部children的宽高进行求和。本篇来分析一下layout过程。

layout综述

  layout方法对一个View及它的后代分配size与position,是View绘制过程的第二步(the second phase of layout mechanism),其中用到了上一步measure出的宽高。与measure-onMeasure相似,layout中也是回调了onLayout方法。但有一点不一样,layout并没有被声明为final。

  子View在继承后,不应当重写onLayout;当子View是一个ViewGroup时,需要重写onLayout,对该ViewGroup下面的每一个View调用layout。

  layout四个参数l, t, r, b分别表示相对于ParentView的距离,最常见的传参就是(0, 0, width, height)。

    public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }

        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;

        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);
            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnLayoutChangeListeners != null) {
                ArrayList<OnLayoutChangeListener> listenersCopy =
                        (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
                int numListeners = listenersCopy.size();
                for (int i = 0; i < numListeners; ++i) {
                    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
                }
            }
        }

        mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    }

  layout方法中,首先根据标志位判断是否需要重新measure,随后对是否为optical进行判断,这个参数用来控制投影等效果,通常情况下直接进入setFrame,setFrame判断视图大小是否发生了变化,以确定有没有必要进行重绘。

  随后回调onLayout,在View中,onLayout是个空壳,什么都没有做;在ViewGroup中则依次对子View调用layout。

  如果layout有变化,则通知所有注册的Listener变化前后的layout数值。

setFrame

  当size和position变化时,返回true。如果发生了变化,会在setFrame方法内部调用invalidate。

onLayout

  View中onLayout什么都没有做,在ViewGroup中,根据各自实际规则(Linear、Relative 等)对内部Views进行布局安排。

getMeasuredWidth与getWidth

  • 可以调用的时机不同:getMeasuredWidth在measure后即可调用,getWidth要在layout后才可以调用。(在发生时机之前调用的话均返回0)
  • 含义不同:getMeasuredWidth是View计算出自己的实际大小,getWidth是在布局后的大小。最简单的,在ScrollLayout中,getHeight返回屏幕内的高度,getMeasuredHeight返回屏幕内+屏幕外的总高度。

小结

  这一篇比较简单,因为大部分情况下,都不需要重写onLayout方法;一旦重写该方法(ViewGroup),就会发现要做的事情还是很多的,参见LinearLayout.onLayout、RelativeLayout.onLayout。

  下一篇着手onDraw()。

时间: 2024-12-09 16:43:41

Android UI 绘制过程浅析(三)layout过程的相关文章

Android UI 绘制过程浅析(五)自定义View

前言 这已经是Android UI 绘制过程浅析系列文章的第五篇了,不出意外的话也是最后一篇.再次声明一下,这一系列文章,是我在拜读了csdn大牛郭霖的博客文章<带你一步步深入了解View>后进行的实践. 前面依次了解了inflate的过程,以及绘制View的三个步骤:measure, layout, draw.这一次来亲身实践一下,通过自定义View来加深对这几个过程的理解. 自定义View的分类 根据实现方式,自定义View可以分为以下3种类型. 自绘控件.View的绘制代码(onDraw

Android UI 绘制过程浅析(四)draw过程

前言 draw是绘制View三个步骤中的最后一步.同measure.layout一样,通常不对draw本身进行重写,draw内部会调用onDraw方法,子类View需要重写onDraw(Canvas),以完成最终的绘制. 如果一定要重写draw(Canvas)的话,需要在方法的开始处调用super.draw(canvas). draw过程 draw内部具体做了什么事情,在View.java的源码注释中已经做了非常详细的介绍 /* * Draw traversal performs several

Android UI设计中的三种特效

一.背景色渐变 背景色渐变可以通过在res/drawable中定义一个XML文件实现,gradient.xml的代码如下: <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:startColor="#FFFFFF

【android】绘制圆环的三种方式

在android中要绘制圆环,暂时知道有三种方式.分别是: 一.设置画笔的style为stoke,绘制圆 这里是先绘制内圆,然后绘制圆环(圆环的宽度就是paint设置的paint.setStrokeWidth的宽度),最后绘制外圆. 请看核心源码: [java] view plaincopyprint? <span xmlns="http://www.w3.org/1999/xhtml" style="">package yan.guoqi.rectph

【Android - View】之View的工作过程简介

View的工作过程分为三个过程: View的measure过程: View的layout过程: View的draw过程. 我们知道,一个Activity就是一个窗口,这个窗口中包含一个Window.一个DecorView和一个ViewRootImpl对象,而应用中的所有Window都由一个WindowManager对象管理.ViewRootImpl是连接WindowManager和DecorView的纽带,它可以接受WindowManager传过来的消息,将消息传递给DecorView,Deco

View工作原理(四)view的layout过程

转载请说明出处:http://blog.csdn.net/ff20081528/article/details/17784911刚过完自己的本命年,新的一年希望自己有个新的开始,祝自己在新的一年里一帆风顺,同时也祝广大的朋友们新年新气象,收获多多! 一.android中view的layout过程总概 Layout过程其实就是父视图按照子视图的大小及布局参数将子视图放在窗口的合适的位置上. 视图的布局过程是从ViewRoot对象调调用根视图的layout()方法开始,接着layout()方法调用根

Android UI(一)Layout 背景局部Shape圆角设计

Jeff Lee blog:   http://www.cnblogs.com/Alandre/  (泥沙砖瓦浆木匠),retain the url when reproduced ! Thanks 今天我们来Android UI第一讲:实现Layout 背景局部Shape圆角设计 效果图: 第一步:定义一个shape res/drawable/shape_to_corner_no_bottom_line.xml <?xml version="1.0" encoding=&quo

简单研究Android View绘制一

2015-07-27 16:52:58 一.如何通过继承ViewGroup来实现自定义View?首先得搞清楚Android时如何绘制View的,参考Android官方文档:How Android Draws Views 以下翻译摘自:http://blog.csdn.net/linghu_java/article/details/23882681,这也是一片好文章,推荐大家看看- When an Activity receives focus, it will be requested to d

Android UI测量、布局、绘制过程探究

在上一篇博客<Android中Activity启动过程探究>中,已经从ActivityThread.main()开始,一路摸索到ViewRootImpl.performTraversals()了.本篇就来探究UI的绘制过程. performTraversals()方法非常长,其中关键性的三个步骤是依次调用了performMeasure(), performLayout(), performDraw().分别来看这三个步骤吧! Measure过程(测量过程) 直接来看performMeasure