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有些许差异.请读者知悉. ...
随机推荐
- UEFI引导系统
UEFI引导系统 1 2 3 4 5 6 7 分步阅读 现在的电脑大多数使用了UEFI引导系统(原来都是使用BIOS),从而加快启动速度,但同时也给用惯BIOS的用户带来很多困惑!为啥电脑不能识别制作 ...
- 当你还在争夺移动支付的时候,我已经统一了IC卡市场
摘要:虽然说今年移动支付行业的发展很快:苹果.Twitter和Facebook等巨头都开始进军这个市场,再加上PayPal.Coin和Square几个“老玩家”的存在,使得今年的移动支付市场热闹非凡. ...
- [置顶] C# WINCE调节屏幕亮度
在wince里面保存屏幕亮度的值保存在注册表HKEY_CURRENT_USER\ControlPanel\\Backlight\Brightness里面,值的范围是0-100,所以要改变屏幕的亮度,只 ...
- C++面试题一大波
//打印1到最大的n位数. //题目:输入数字n.按顺序打印出从1到最大的n位十进制数.比方: //输入3.则打印出1.2.3一直到最大的3位数999. //[陷阱]:这个题目非常easy想到的办法就 ...
- Objective-C中的block块语法
#import <Foundation/Foundation.h> /* OC不同于Java C++ 它没有字符串对象 只有NS对象 NS就是乔布斯第一个公司NeXt的类集合 被收购之后才 ...
- Cocos2d-x 3.1.1 学习日志6--30分钟了解C++11新特性
新的keyword auto C++11中引入auto第一种作用是为了自己主动类型推导 auto的自己主动类型推导,用于从初始化表达式中判断出变量的数据类型.通过auto的自己主动类型推导.能够大大简 ...
- Unity 3d 刚体
1.起始的设置如下图: 这是我们运行游戏,方块并不会往下掉. 2.选中CUBE,然后添加刚体 此时再运行,会发现场景的方块会自动掉在地上. 3.我们来看一下刚体的属性 ...
- 制作U盘启动盘(以CentOS6.3为例)
借助UltraISO(软碟通),自己百度下载一个即可(同样适用于制作Windows启动盘). 选择文件→打开,选择ISO镜像所在目录,如下两幅图所示:
- 转场动画2-Pop动画
上一篇试讲push动画,这篇分解pop动画 里面关于矩阵有不懂得,参考CATransform3D 特效详解 上图(虚拟机下,图是渣渣 ) 代码直接上 // // PopTransition.h // ...
- jjjjQuery选择器
此文为作者自用复习文章 jQuery选择器: 它不仅继承了CSS选择器简洁的语法, 还继承了其获取页面便捷高效的特点, 它还拥有更加完善的处理机制: 但jQuery选择器获取元素后,为该元素添加的是行 ...