Android View总结
2015-12-25 14:12
645 查看
关于Android View控件
Android中控件大致被分为两类ViewGroup,View。ViewGroup作为容器管理View。Android视图,是类似于Dom树的架构。父视图负责测量定位绘制等操作。我们经常在用的findViewById方法代价昂贵的原因,就是因为他负责至上而下遍历整棵控件树,来寻找View实例,在重复操作中尽量少用。现在在用的很多控件都是直接或者间接继承自View的,如下图。
Android UI界面架构
每个Activity包含一个PhoneWindow对象,
PhoneWindow设置
DecorView为应用窗口的根视图。在里面就是熟悉的
TitleView和
ContentView,没错,平时使用的
setContentView()就是设置的
ContentView。
Android是如何绘制View的?
当一个Activity启动时,会被要求绘制出它的布局。Android框架会处理这个请求,当然前提是Activity提供了合理的布局。绘制从根视图开始,从上至下遍历整棵视图树,每一个ViewGroup负责让自己的子
View被绘制,每一个
View负责绘制自己,通过
draw()方法.绘制过程分三步走。
Measure
Layout
Draw
整个绘制流程是在
ViewRoot中的
performTraversals()方法展开的。部分源代码如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private void performTraversals() { ...... //最外层的根视图的widthMeasureSpec和heightMeasureSpec由来 //lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENT int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); ...... mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); ...... mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); ...... mView.draw(canvas); ...... } |
measu和
layout(测量和定位),如下图。
Measure过程
1 2 3 4 5 6 7 8 | public final void measure(int widthMeasureSpec, int heightMeasureSpec) { //.... //回调onMeasure()方法 onMeasure(widthMeasureSpec, heightMeasureSpec); //more } |
mMeasuredHeight和
mMeasureWidth,
measure(int, int)传入的两个参数。
MeasureSpec是一个32位int值,高2位为测量的模式,低30位为测量的大小。测量的模式可以分为以下三种。
EXACTLY
精确值模式,当
layout_width或
layout_height指定为具体数值,或者为
match_parent时,系统使用EXACTLY。
AT_MOST
最大值模式,指定为
wrap_content时,控件的尺寸不能超过父控件允许的最大尺寸。
UNSPECIFIED
不指定测量模式,View想多大就多大,一般不太使用。
根据上面的源码可知,measure方法不可被重写,自定义时需要重写的是
onMeasure方法
1 2 3 4 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } |
setMeasuredDimension()设定的,如果不重写,默认是直接调用
getDefaultSize获取尺寸的。
使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。
Layout过程
Layout方法就是用来确定view布局的位置,就好像你知道了一件东西的大小以后,总要知道位置才能画上去。mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
layout获取四个参数,左,上,右,下坐标,相对于父视图而言。这里可以看到,使用了刚刚测量的宽和高。
1 2 3 4 5 6 7 89 | public void layout(int l, int t, int r, int b) { int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; boolean changed = setFrame(l, t, r, b); if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) { ..... onLayout(changed, l, t, r, b); ..... } |
setFrame设置坐标。如果坐标改变过了,则重新进行定位。如果是View对象,那么onLayout是个空方法。因为定位是由ViewGroup确定的。
当layout结束以后
getWidth()与
getHeight()才会返回正确的值。
这里出现一个问题,
getWidth/Height()and
getMeasuredWidth/Height()有什么区别?
getWidth():View在設定好佈局後整個View的寬度。
getMeasuredWidth():對View上的內容進行測量後得到的View內容佔據的寬度
Draw过程
1 2 3 4 5 6 7 8 9 10 11 12 13 1415 | public void draw(Canvas canvas) { ...... /* * 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 * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */ // Step 1, draw the background, if needed ...... if (!dirtyOpaque) { drawBackground(canvas); } // skip step 2 & 5 if possible (common case) ...... // Step 2, save the canvas' layers ...... if (drawTop) { canvas.saveLayer(left, top, right, top + length, null, flags); } ...... // Step 3, draw the content if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); // Step 5, draw the fade effect and restore layers ...... if (drawTop) { matrix.setScale(1, fadeHeight * topFadeStrength); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); p.setShader(fade); canvas.drawRect(left, top, right, top + length, p); } ...... // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); ...... } |
dispatchDraw,是用来递归调用子View,如果没有则不需要。
onDraw方法是需要自己实现的,因为每个控件绘制的内容不同。主要用canvas对象进行绘制,这里就不说了。
参考资料
Android视图绘制流程完全解析,带你一步步深入了解View(二)Android应用层View绘制流程与源码分析
How
Android Draws Views
《Android群英传》
What
is the difference between getWidth/Height() and getMeasuredWidth/Height() in Android SDK?
点击打开链接
相关文章推荐
- Android HorizontalScrollView 水平滑动 里面放listView 解决滑动冲突
- Android 5.x新特性之elevation(阴影),tinting(着色)以及clipping(剪裁)
- Android 图片Exif信息相关的获取与修改
- Android 系统编译 1
- 转 Android 系统编译
- android 旋转动画
- RxJava与RxAndroid 接收消息通知
- android studio 的奇葩环境问题
- Android 中的自定义开关按钮
- 【Android】Android Studio 快速打开Github上的Demo
- Android Service在bindService不能触发onServiceConnected方法
- Debug的方式
- Android进程间通信之Messenger浅析
- 浅谈android 视频播放VideoView
- 如何调用Android隐藏API
- Android之查看外部依赖jar的源代码_android private libralies does not allow modifications to source
- android AsyncTask介绍
- android 手机拍照选择本地图片
- android 界面中的各种布局
- Android通过webservice连接SQLServer 详细教程(数据库+服务器+客户端)