onLayout源码 流程 思路详解

转载请注明本文出自大苞米的博客(),谢谢支持!

简介:

在自定义view的时候,其实很简单,只需要知道3步骤:

1.测量——onMeasure():决定View的大小

2.布局——onLayout():决定View在ViewGroup中的位置

3.绘制——onDraw():如何绘制这个View。

而第3步的onDraw系统已经封装的很好了,基本不用我们来操心,只需要专注到1,2两个步骤就中好了。

而这篇文章就来谈谈第二步:“布局(Layout)”

知识点回顾:

在谈如何使用onLayout方法前,先简单回忆一下知识点:

View视图结构:

View视图可以是单一的一个如TextView,也可以是一个视图组(ViewGroup)如LinearLayout。

如图:对于多View的视图他的结构是树形结构,最顶层是ViewGroup,ViewGroup下可能有多个ViewGroup或View。

这个树的概念很重要,因为无论我们是在测量大小或是调整布局的时候都是从树的顶端开始一层一层,一个分支一个分支的进行(树形递归)。

Measure简单回顾:

measure的作用就是为整个View树计算实际的大小,而通过刚才对View树的介绍知道,想计算整个View树的大小,就需要递归的去计算每一个子视图的大小(Layout同理)。

。对于每个View的实际宽高都是由父视图和本身视图决定的。

Layout(源码分析):

在代码中如何设置这4个值呢?

首先,无论是系统提供的LinearLayout还是我们自定义的View视图,他都需要继承自ViewGroup类,之后必须要做的就是重写onLayout方法(因为在onLayout在ViewGroup中被定义为抽象方法)。

ViewGroup-onlayout:

@Overrideprotected abstract void onLayout(boolean changed, int l, int t, int r, int b);onLayout被定义为抽象方法,所以在继承ViewGroup时必须要重写该方法(onMeasure不需要)。另外这个方法也被override标注,所以也是重写的方法,他重写的是其父类view中的onLayout方法。

View-onlayout:

/*** 当这个view和其子view被分配一个大小和位置时,被layout调用。* @param changed 当前View的大小和位置改变了* @param left 左部位置(相对于父视图)* @param top 顶部位置(相对于父视图)* @param right 右部位置(相对于父视图)* @param bottom 底部位置(相对于父视图)*/protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}注解说:当这个view和其子view被分配一个大小和位置时,被layout调用。所以我们去看看layout中做了什么。(注解没有完全按照英文翻译,并且有省略)

View-layout:

/*** 给View和其所有子View分配大小和位置** 这是布局的第二个阶段(第一个阶段是测量)。在这个阶段中,每个父视图需要去调用layout去为他所有的子视图确定位置* 派生的子类不应该重写layout方法,应该重写onLayout方法,在onlayout方法中应该去调用每一个view的layout*/public void layout(int l, int t, int r, int b) {// 将当前视图的左上右下记录为old值(参数中传入的为新的l,t,r,b值)int oldL = mLeft;int oldT = mTop;int oldB = mBottom;int oldR = mRight;// setFrame方法的作用就是将新传入的ltrb属性赋值给View,然后判断当前View大小和位置是否发生了变化并返回boolean changed = setFrame(l, t, r, b);if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {// 调用onLayout回调方法,具体实现由重写了onLayout方法的ViewGroup的子类去实现(后面详细说明)onLayout(changed, l, t, r, b);mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;// 调用所有重写了onLayoutChange监听的方法,通知View大小和位置发生了改变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;}

在这段代码中我们只要知道:如果视图的大小和位置发生变化后,会调用我们前面分析过的onLayout方法。

对于onLayout方法的最终实现全部依靠我们在自定义ViewGroup类中重写的onLayout去实现。

计算View位置:

在重写的onLayout方法中,唯一的目的就是:

对当前视图和其所有子View设置它们在父视图中具体位置(确定这个位置就依靠mLeft,mTop,mRight,mBottom这四个值)

之前介绍过,mLeft,mTop,mRight,mBottom这四个值表示的是子view相对于父view的位置。下面我贴出我画的图看一下就明白了。

而有的旅行是释放负面情绪,换个心情,轻装上阵。

onLayout源码 流程 思路详解

相关文章:

你感兴趣的文章:

标签云: