深入理解Android View
2016-01-15 14:22
609 查看
![](http://img.imooc.com/56946a680001566b10000625.jpg)
layout(确定View的位置)
draw(画出View)通常我们的View都是以这样的树结构呈现的,如下图
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } } } protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }这里我们可以看到在ViewGroup中的measureChildren的方法,首先获取到了所有的子View,然后调用View的measure方法进行子View的测量,这样就完成了一次测量过程,然后子View会重复父View的操作,如此返回就完成了整个View树的测量过程。通过源码我们发现一个View的大小,主要由父View的MeasureSpec和自身的LayoutParams来共同决定,LayoutParams我们应都比较熟悉了,接下来我们就来深入分析MeasureSpec,这是在View类中的一个静态内部类,看名字就能猜到它代表它的作用是一种测量标准或者说测量规格,我们先来看下它的源码
public static class MeasureSpec { private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; public static final int UNSPECIFIED = 0 << MODE_SHIFT; public static final int EXACTLY = 1 << MODE_SHIFT; public static final int AT_MOST = 2 << MODE_SHIFT; public static int makeMeasureSpec(int size, int mode) { if (sUseBrokenMakeMeasureSpec) { // 老版api return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); } } public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); } public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); } }MeasureSpec代表一个32位的int值,高两位代表SpecMode,低30位代表SpecSize,Mode指测量模式,Size则指测量的大小,通过看MeasureSpec的源码也能看到,其实这里谷歌工程师非常巧妙的把Mode和Size封装到了一个int值中,非常巧妙的办法(这种设计方式可以借鉴),如果你对Java运算符不太熟的话,你可以这样理解,谷歌工程师把一个32位的int分成了两节,一节用来表示Mode,一节用来表示Size。可以看到Mode分为三种模式如下所示:UNSPECIFIED父容器不对子View做任何限制,要多大给多大,一般用于系统内部,这里不用过多考虑
EXACTLY精准模式,一般View指定了具体的大小(dp/px)或者设置为match_parent则就是这个模式
AT_MOST父容器制定了一个可用的大小,子View不能大于这个值,对于wrap_content
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //分别获取宽高的Mode 和size super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); // 如果是精准模式则直接使用获取到的宽高,如果是AT_MOST,则使用我 们自己测量的宽高 if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(mWidth, mHeight); } else if (widthSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(mWidth, heightSpecSize); } else if (heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSpecSize, mHeight); } }如果当前是View则直接完成测量,如果当前是ViewGruop除了完成自身绘制外,还需要遍历调用子View的measure方法。View的layout方法和Draw方法就比较简单了,layout用于确定View的位置,原理和Measure过程类似,当一个ViewGroup的位置被确定之后,会遍历其所有子View并调用其layout方法。Draw的过程也类似,通过dispatchDraw(Canvas canvas)方法,遍历子View,通过canvas进行Draw过程,接下来我通过一个小Demo流式布局,来运用一下三个过程
import java.util.ArrayList; import java.util.List; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * */ public class FlowLayout extends ViewGroup { /** * 存储所有的子View */ private List<List<View>> mAllChildViews = new ArrayList<List<View>>(); /** * 存储每一行的高度 */ private List<Integer> mLineHeight = new ArrayList<Integer>(); public FlowLayout(Context context) { this(context, null); }
相关文章推荐
- Android 第三方开源库收集整理(转)
- Android沉浸式状态栏实现
- Android TextWatcher使用详解
- Android Studio开发工具的设置
- Android LruCache 缓存 类 源码 注解 分析
- Android退出应用最优雅的方式
- android studio 开启genymotion 出现"failed to create framebuffer image"
- android 顶部状态栏遮盖
- Android开发之EditText属性详解
- <安卓那点事>简单短信发送器
- android自定义控件学习(一)单行横向标签并做溢出处理
- 解决支付宝WEB支付界面模块在Android上自动滑动到登录模块的问题
- Android开发中使用软引用和弱引用
- Android缓存文件的存放
- Android如何实现音频输出切换
- Android linearlayout常用布局
- android intent flag的相关介绍
- android打印调用栈
- android SDK23 一些api无法使用的解决方案
- Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框