View的简介 自定义view的原理
来源:互联网 发布:js 淘宝左侧 分类导航 编辑:程序博客网 时间:2024/06/05 08:41
认识一个新的事物,首先我们从概念上讲,我们需要知道,这个事物 是什么,这个事物有什么用途?
对应到自定义View 上,首先我们要搞明白 View 的定义以及工作原理。
1.View是什么?
View是屏幕上的一块矩形区域,它负责用来显示一个区域,并且响应这个区域内的事件。可以说,手机屏幕上的任意一部分看的见得地方都是View,它很常见,比如 TextView 、ImageView 、Button以及LinearLayout、RelativeLayout都是继承子View的。
对于Activity来说,我们通过setContentView(view)添加的布局到Activity上,实际上都是添加到了Activity 内部的DecorView上面,这个DecorView,其实就是一个FrameLayout,因此实际上,我们的布局实际上添加到了FrameLayout里面。
2.View 是怎么工作的?
View的工作流程分为两部分,第一部分 显示在屏幕上的过程, 第二部分 响应屏幕上的触摸事件的过程。
对于显示在屏幕上的过程:是View 从无到有,经过测量大小(Measure)、确定在屏幕中的位置(Layout)、以及最终绘制在屏幕上(Draw) 这一系列的过程。
对于响应屏幕上的触摸事件的过程:则是Touch事件的分发过程(这一部分,在这里先不涉及)。
Measure() Layout()方法是final修饰的,无法重写 ,Draw()虽然不是final的,但是也不建议重写该方法。
3.如何自定义View?
View 为我们提供了 onMeasure() onLayout() onDraw() 这样的方法,其实所谓的自定义View,也就是对onMeasure() onLayout() onDraw() 三个方法的重写的过程。
所谓的自定义View 实际上,就是仿照View的工作流程,去自己实现的过程。根据继承对象的不同自定义View又分为继承View 与ViewGroup两种。
measure():
View的onMeasure()方法如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
内部调用了 setMeasuredDimension() 方法,这个方法看名字就知道是设置测量的View的宽/高,传递的参数就是宽度和高度。
onMeasure通过父View传递过来的大小和模式,以及自身的背景图片的大小得出自身最终的大小,通过setMeasuredDimension()方法设置给mMeasuredWidth和mMeasuredHeight。
普通View的onMeasure逻辑大同小异,基本都是测量自身内容和背景,然后根据父View传递过来的MeasureSpec进行最终的大小判定,例如TextView会根据文字的长度,文字的大小,文字行高,文字的行宽,显示方式,背景图片,以及父View传递过来的模式和大小最终确定自身的大小。
ViewGroup本身没有实现onMeasure,但是他的子类都有各自的实现,通常他们都是通过measureChildWithMargins函数或者其他类似于measureChild的函数来遍历测量子View,被GONE的子View将不参与测量,当所有的子View都测量完毕后,才根据父View传递过来的模式和大小来最终决定自身的大小。
ViewGroup一般都在测量完所有子View后才会调用setMeasuredDimension()设置自身大小。
这里我们就要介绍一下Android中自己定义的测量规格:MeasureSpec。
因为测量过程中,系统会把我们代码或者布局中设置的View的LayoutParams 转换成MeasureSpec,然后通过MeasureSpec来测量确定View的宽高。
MeasureSpec由32位 int值组成,高2位表示的是测量模式(specMode),后面的30位代表的是测量的大小(specSize)。
public static class MeasureSpec { private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; /** 测量模式:父View不对子View的大小做任何限制,可以是子布局需要的任意大小 * Measure specification mode: The parent has not imposed any constraint * on the child. It can be whatever size it wants. */ public static final int UNSPECIFIED = 0 << MODE_SHIFT; /** 已经为子View给出了确切的值 相当于给View的LayoutParams指定了具体数值,或者match_parent. * Measure specification mode: The parent has determined an exact size * for the child. The child is going to be given those bounds regardless * of how big it wants to be. */ public static final int EXACTLY = 1 << MODE_SHIFT; /**子View可以跟自己的测量大小一样大。对应于View的LayoutParams的wrap_content * Measure specification mode: The child can be as large as it wants up * to the specified size. */ public static final int AT_MOST = 2 << MODE_SHIFT; /**根据 size和mode 去生成一个MeasureSpec值 * Creates a measure specification based on the supplied size and mode. * * The mode must always be one of the following: * <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li> * <li>{@link android.view.View.MeasureSpec#EXACTLY}</li> * <li>{@link android.view.View.MeasureSpec#AT_MOST}</li> * @return the measure specification based on size and mode */ public static int makeMeasureSpec(int size, int mode) { if (sUseBrokenMakeMeasureSpec) { return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); } } /**获取测量模式 * Extracts the mode from the supplied measure specification. * * @param measureSpec the measure specification to extract the mode from * @return {@link android.view.View.MeasureSpec#UNSPECIFIED}, * {@link android.view.View.MeasureSpec#AT_MOST} or * {@link android.view.View.MeasureSpec#EXACTLY} */ public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); } /**获取测量值大小 * Extracts the size from the supplied measure specification. * * @param measureSpec the measure specification to extract the size from * @return the size in pixels defined in the supplied measure specification */ public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); } static int adjust(int measureSpec, int delta) { return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec)); } }
MeasureSpec是由父布局与View 自身的LayoutParams来决定的
经过measure 完成后,我们就可以通过getMeasuredWidth/Height 获取View 的宽高。
layout()
Layout() 方法如果是ViewGroup,则循环遍历所有子View,普通View则空实现,因此如果我们继承ViewGroup 我们需要遍历执行所有的child.layout()。
Layout方法中接受四个参数,是由父View提供,指定了子View在父View中的左、上、右、下的位置。父View在指定子View的位置时通常会根据子View在measure中测量的大小来决定。
子View的位置通常还受有其他属性左右,例如父View的orientation,gravity,自身的margin等等,影响布局的因素非常多。
onLayout是ViewGroup用来决定子View摆放位置的,各种布局的差异都在该方法中得到了体现。
onLayout比layout多一个参数,changed,该参数是在setFrame通过比对上次的位置得出是否发生了变化,通常该参数没有被使用的意义,因为父View位置和大小不变,并不能代表子View的位置和大小没有发生改变。
draw()
draw()的过程就是绘制View到屏幕上的过程,draw()的执行遵循如下步骤:
/* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background(绘制背景) * 2. If necessary, save the canvas' layers to prepare for fading(保存画布的图层来准备色变) * 3. Draw view's content(绘制内容) * 4. Draw children(绘制children) * 5. If necessary, draw the fading edges and restore layers(画出褪色的边缘和恢复层) * 6. Draw decorations (scrollbars for instance)(绘制装饰 比如scollbar) */
一般2和5 是可以跳过的。
在TextView中在该方法中绘制文字、光标和CompoundDrawable 、ImageView中相对简单,只是绘制了图片。
View 的绘制主要通过dispatchDraw()
先根据自身的padding剪裁画布,所有的子View都将在画布剪裁后的区域绘制。
遍历所有子View,调用子View的computeScroll对子View的滚动值进行计算。
根据滚动值和子View在父View中的坐标进行画布原点坐标的移动,根据子在父View中的坐标计算出子View的视图大小,然后对画布进行剪裁,请看下面的示意图。
dispatchDraw的逻辑其实比较复杂,但是幸运的是对子View流程都采用该方式,而ViewGroup已经处理好了,我们不必要重载该方法对子View进行绘制事件的派遣分发。
理解了以上工作流程,就可以试着去自定义View了。
自定义View一般就分为 继承View/VieGroup两种,在我们自定义View的时候要让自定义的View的功能尽可能向
系统提供的考虑,比如支持wrap_content,padding 等。
View 的几个比较重要的方法:
requestLayout:
方法会执行,区别就是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:
Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
鉴于此,如果要使用invalidate的刷新,那我们就得配合handler的使用,
使异步非ui线程转到ui线程中调用,如果要在非ui线程中直接使用就调用postInvalidate方法即可,
这样就省去使用handler的烦恼。
感谢:
原文链接:http://blog.csdn.net/u011733020/article/details/50849475 仅供个人学习使用。如侵犯了您的利益,请联系作者删除:Q337081267
- View的简介 自定义view的原理
- 自定义view(二)view的绘制原理简介
- View的工作原理(自定义View)
- 自定义View学习笔记03—View的工作原理简介
- 【自定义View】一个完整的自定义View简介
- 自定义View第一篇(view生命周期的简介)
- Android<View的工作原理以及自定义View的方法>
- 自定义View的实现方法,View工作原理(四)
- extends View的自定义view
- android自定义View的实现原理
- Android 自定义View的原理与基础
- android自定义View的绘制原理
- 自定义View的绘制原理图解
- 自定义控件(一):View的测量及绘制流程、原理简介
- 自定义View(一)自定义View的分类
- View的简介
- View的简介
- 自定义ScollerView,理解滑动动画与自定义view的原理
- 颈源性头痛患者日常生活中的康复护理方法有哪些?
- 8、ssm整合quartz实现定时调度
- HDU 1008 Elevator(最简代码)
- SQL的四种连接--内连接,左外连接,右外连接,完全连接
- InterlliJ Debug方式启动:method breakpoints may dramatically show down debugging
- View的简介 自定义view的原理
- Facebook开源LogDevice:一种用于日志的分布式数据存储系统
- hdu1521排列组合 (指数型母函数)
- 高程笔记-关于typeof和instanceof的那些事儿
- leetcode_491. Increasing Subsequences ? 待解决
- day5_常用模块
- 设置闹钟
- 亿图流程图连接线怎么画?三个步骤轻松搞定
- [学习笔记][Java编程思想]第6章:访问权限控制