ANDROID自己定义视图——onLayout源代码 流程 思路具体解释
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持!
简单介绍:
在自己定义view的时候。事实上非常easy。仅仅须要知道3步骤:
1.測量——onMeasure():决定View的大小
2.布局——onLayout():决定View在ViewGroup中的位置
3.绘制——onDraw():怎样绘制这个View。
而第3步的onDraw系统已经封装的非常好了,基本不用我们来担心,仅仅须要专注到1,2两个步骤就中好了。
第一步的測量,能够參考我之前的文章:(ANDROID自己定义视图——onMeasure流程。MeasureSpec具体解释)
而这篇文章就来谈谈第二步:“布局(Layout)”
知识点回想:
在谈怎样使用onLayout方法前,先简单回顾一下知识点:
View视图结构:
View视图能够是单一的一个如TextView,也能够是一个视图组(ViewGroup)如LinearLayout。
如图:对于多View的视图他的结构是树形结构,最顶层是ViewGroup。ViewGroup下可能有多个ViewGroup或View。
这个树的概念非常重要,由于不管我们是在測量大小或是调整布局的时候都是从树的顶端開始一层一层。一个分支一个分支的进行(树形递归)。
Measure简单回想:
measure的作用就是为整个View树计算实际的大小。而通过刚才对View树的介绍知道,想计算整个View树的大小,就须要递归的去计算每个子视图的大小(Layout同理)。
对每个视图通过onMeasure方法的一系列測量流程后计算出实际的高(mMeasuredHeight)和宽(mMeasureWidth)传入setMeasuredDimension()方法完毕单个View的測量,如果所測的视图是ViewGroup则能够通过measureChild方法递归的计算当中的每个子view。对于每个View的实际宽高都是由父视图和本身视图决定的。
Layout(源代码分析):
Layout的作用就是为整个View树计算实际的位置。而通过刚才对View树的介绍知道,想计算整个View树的位置,就须要递归的去计算每个子视图的位置(Measure同理)。
而确定这个位置非常easy,仅仅须要mLeft,mTop,mRight,mBottom四个值(注意:这4个值是子View相对于父View的值。以下会具体介绍)。
在代码中怎样设置这4个值呢?
首先,不管是系统提供的LinearLayout还是我们自己定义的View视图,他都需要继承自ViewGroup类。之后必需要做的就是重写onLayout方法(由于在onLayout在ViewGroup中被定义为抽象方法)。
ViewGroup-onlayout:
@Override
protected 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的位置。
以下我贴出我画的图看一下就明确了。
如图,黄色区域是我们的父view,而中间的深色的区域就是我们的子view。
所以对于这个View来说,我列出它相对于父view的各个值是怎样计算和相关函数:
mLeft,mTop,mRight,mBottom:
view.getLeft()——mLeft:子View左边界到父view左边界的距离
public final int getLeft() {
return mLeft;
}
view.getTop()——mTop:子View上边界到父view上边界的距离
view.getRight()——mRight:子View右边界到父view左边界的距离
view.getBottom()——mBottom:子View下边距到父View上边界的距离
视图宽高:
视图宽度 view.getWidth();子View的右边界 - 子view的左边界。
public final int getWidth() {
return mRight - mLeft;
}
视图高度 view.getHeight() ;子View的下边界 - 子view的上边界。
public final int getHeight() {
return mBottom - mTop;
}
測量宽高:
view.getMeasuredWidth();measure过程中返回的mMeasuredWidth
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
view.getMeasuredHeight();measure过程中返回的mMeasuredHeight
public final int getMeasuredHeight() {
return mMeasuredHeight & MEASURED_SIZE_MASK;
}
最后介绍一下getWidth/Height和getMeasuredWidth/Height的差别:
getWidth,和getLeft等这些函数都是View相对于其父View的位置。而getMeasuredWidth,getMeasuredHeight是測量后该View的实际值(有点绕,以下摘录一段jafsldkfj所写的Blog中的解释).
实际上在当屏幕能够包裹内容的时候,他们的值是相等的。仅仅有当view超出屏幕后,才干看出他们的差别:
getMeasuredHeight()是实际View的大小,与屏幕无关,而getHeight的大小此时则是屏幕的大小。
当超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外没有显示的大小
在计算子View在父View中的位置时,主要就是应用上面这几个函数。以下就来看看怎样去重写onLayout。
onLayout:
对于重写onLayout的思路和重写onMeasure同样:
假设仅仅须要測量单个View。则单独測量它自己即可。假设须要測量的View其下还有子View,则须要測量其全部的子View。
并传递到onlayout( l, t, r, b )中;
剩下的任务就仅仅须要知道它的mLeft值。mTop值。加上长、宽值即可了。
长宽值非常easy,使用getWidth/Height和getMeasuredWidth/Height都能够。
因为这个View须要居中显示。剩下的问题就是怎样计算该View的mLeft值和mTop值。我的思路例如以下:
r(父View的mRight) = mLeft + width + mLeft(由于左右间距一样)
b(父View的mBottom) = mTop + height + mTop(由于上下间距一样)
我的代码例如以下:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) { // 循环全部子View
for (int i=0; i<getChildCount(); i++) {
View child = getChildAt(i);
// 取出当前子View长宽
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight(); // 计算当前的mLeft和mTop值(r,b为传递进来的父View的mRight和mBottom值)
int mLeft = (r - width) / 2;
int mTop = (b - height) / 2; // 调用layout并传递计算过的參数为子view布局
child.layout(mLeft, mTop, mLeft + width, mTop + height);
}
}
布局文件例如以下:
<com.gxy.text.CostomViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#eee999" > <Button
android:text="ChildView"
android:layout_width="200dip"
android:layout_height="200dip"
android:background="#333444"
android:id="@+id/textView2" />
</com.gxy.text.CostomViewGroup>
效果图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYTM5NjkwMTk5MA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="400" height="600" alt="">
总结:
onMeasure和onLayout的大致总结完了,在自己定义View的时候最关键的是onLayout,由于不管你怎样Measure这个View的大小,最后的决定权永远在onLayout手中,onLayout会决定详细View的大小和位置。当然onMeasure也非常重要,有的情况控件的宽高不确定或者须要自己定义,这时候须要我们人工Measure它。
而在复杂的自己定义View时。非常多计算也须要在onMeasure中完毕。而且些值会记录下来在onLayout中又一次使用(个人理解,欢迎指正)。
写onMeasure和onLayout的时候仅仅是想自己总结一下。整理一下思路。由于网上有太多写的好了。这里推荐一下qinjuning这位大神的blog。关于View的内容他总结的相当全面和深入。
尽管有非常好的了。但我还会坚持自己总结一遍,接下来的计划是写一个略微复杂点的小样例。将onMeasure和onLayout结合起来(已经写完了,链接例如以下:ANDROID自己定义视图——仿瀑布布局)。
之后深入的研究一下View和ViewGroup的源代码,总结一下LayoutParams,LayoutInflater等简单经常使用的知识点。了解一下View的绘制刷新流程等等等。
。。
太多不会的了,慢慢来吧。
ANDROID自己定义视图——onLayout源代码 流程 思路具体解释的更多相关文章
- Android自己定义视图(一):带下划线的TextView
package com.francis.underlinetextviewtest; import android.content.Context; import android.content.re ...
- ANDROID定义自己的看法——onMeasure,MeasureSpec源代码 过程 思考具体解释
一个简短的引论: 在他们的定义view什么时候,其实很easy,只需要知道3: 1.測量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGrou ...
- 【转】ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解
原文地址:http://blog.csdn.net/a396901990/article/details/36475213 简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量—— ...
- ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解
简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3. ...
- android自己定义控件系列教程----视图
理解android视图 对于android设备我们所示区域事实上和它在底层的绘制有着非常大的关系,非常多时候我们都仅仅关心我们所示,那么在底层一点它究竟是怎么样的一个东西呢?让我们先来看看这个图. w ...
- android 自己定义ViewGroup实现可记载并呈现选择的ListView
转载请注明出处:王亟亟的大牛之路 之前也做过一些用TextView之类的记录ListView选项的东西.可是总认为好难看.发现个不错的实现就贴给大家. 项目文件夹 执行效果: 自己定义视图: @Tar ...
- Android自己定义控件:进度条的四种实现方式
前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源代码下载)http://down ...
- Android自己定义控件系列五:自己定义绚丽水波纹效果
尊重原创!转载请注明出处:http://blog.csdn.net/cyp331203/article/details/41114551 今天我们来利用Android自己定义控件实现一个比較有趣的效果 ...
- Android 4.4 Kitkat Phone工作流程浅析(八)__Phone状态分析
本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象.与Google原生AOSP有些许差异.请读者知悉. ...
随机推荐
- php 常用经验
1.用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量, 单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的”函数”(译注:PHP手 ...
- php的系统常量
认识一下系统常量 系统常量是PHP已经定义好的常量,我们可以直接拿来使用,常见的系统常量有: (1)__FILE__ :php程序文件名.它可以帮助我们获取当前文件在服务器的物理位置. (2)__LI ...
- C/C++招聘的一些感受和经验【转】
找工作本人认为最重要的就是前期准备了. 首先.简历一定要写的切合主题.招聘单位要的是你的技能,这个只要大概能符合就可以,关键他们需要的是你的开发经验,一定要在简历中完美的体现出你之前所参与的项 ...
- 贴片方式COB COF COG
英文简称: COB英文全称: Chip On Board中文全称: 通过邦定将IC裸片固定于印刷线路板上 英文简称: COF 英文全称: Chip On FPC 中文全称: 将IC固定于柔性线路板上 ...
- 解决Qt程序发布时中文乱码问题(通过QApplication.addLibraryPath加载QTextCodec插件)
Qt程序的文字编码,是通过插件来解决的,所以我们发布的时候需要把相应的插件也发布出去,在开发者电脑上程序会自动从插件目录加载到插件,但是如果发布给别的电脑使用,需要手动指定插件路径,如下所示: int ...
- 编程珠玑I算法总结
主要是根据编程珠玑后面的Algorithm附录总结了一下这本书里面的经典算法. 1 辗转相减求最大公约数 思想:最大公约数能整除i和j,则其一定也能整除i-j(if i>j) int gcd(i ...
- libcurl get post http
一. 概念 1. 为什么要使用libcurl 1) 作为http的客户端,可以直接用socket连接服务器,然后对到的数据进行http解析,但要 ...
- [Leetcode][Python]28: Implement strStr()
# -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 28: Implement strStr()https://oj.leetco ...
- poj1663---纯粹找规律
#include <stdio.h> #include <stdlib.h> int main() { int n; scanf("%d",&n); ...
- linux内核源码阅读之facebook硬盘加速flashcache之八
前面我们的分析中重点关注正常的数据流程,这一小节关注如果有异常,那么流程是怎么走完的呢? 1)创建新任务时kcached_job申请不到 2)读写命中时cache块为忙 3)系统关机时处理,系统开机时 ...