View视图安卓应用中非常重要的组成部分,它不仅是构成应用界面的基本单元,还是与用户交互的最直接对象。视图View作为界面的基本元素,是由View System进行管理的。
在Android中,视图控件主要被分成两大类,一类是单一控件View,另外一类是可以包含并管理子控件的ViewGroup,在一个界面布局中,ViewGroup与View的嵌套与组合,就构成了一棵控件树,经过一系列流程绘制在屏幕上,形成完整的用户界面。
View是最基本的UI类,几乎所有的控件都继承于View,广义的View指的是除了ViewGroup外的如TextView,CheckBox、Button、EditText、RadioButton等的单一控件,它们管理自身的绘制以及对事件的处理。而ViewGroup是View 的子类,同时作为View控件的容器而存在,ViewGroup可以控制各个子View 的显示层次与位置,同时可以对焦点获取、触摸等交互事件进行处理、拦截或分发。
视图的绘制主要分为三个步骤,分别为Measure,Layout跟Draw。
在Measure操作中,测量的操作的分发由ViewGroup来完成,ViewGroup通过对子节点进行遍历并分发测量操作,在具体的测量过程中,根据父节点的MeasureSpec以及子节点的LayoutParams等信息计算子节点的宽高,最终整理成父容器的宽高,具体的测量方式,我们也可以通过重写onMeasure方法来设定。
紧接着,通过Layout过程,来确定每一个View在父容器中的具体位置,同样的,我们也可以通过onLayout方法来自定义具体的布局流程。
最后进入Draw的绘制流程,根据前两步所获得的具体布局参数,在draw函数中对各控件进行绘制,绘制的顺序为背景-控件内容-子控件绘制-绘制边缘以及滚动条等装饰物。
总体来说,控件的绘制都是在控件树上进行的,由ViewGroup分发给子View各自完成自身的测量与布局操作,最后由根节点开始进行绘制,最终形成完整的界面。
最后选择一个具体的例子来观察View的绘制流程吧。
首先我们看一下TextView的具体绘制流程:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
......
setMeasuredDimension(width, height);
}
传入的MeasureSpec是一个32位的整数,高两位表示测量模式,低30位表示规格大小,这个整数指定了控件将如何进行具体的测量。最后利用更新后的width跟height设置测量结果的宽高。这个函数的主要作用是计算是否有margin/padding等造成额外的宽高,在必要时预留空间,并确定view的大小。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mDeferScroll >= 0) {
int curs = mDeferScroll;
mDeferScroll = -1;
bringPointIntoView(Math.min(curs, mText.length()));
}
}
在这里调用了父类的onLayout函数
/**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout
(boolean changed, int left, int top, int right, int bottom) {
}
可以看到View中该函数的注释,可以知道这个函数的主要作用是对view的位置与大小进行赋值,以便下一步进行绘制。
@Override
protected void onDraw(Canvas canvas) {
restartMarqueeIfNeeded();
// Draw the background for this view
super.onDraw(canvas);
......
mTextPaint.setColor(color);
mTextPaint.drawableState = getDrawableState();
......
layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
......
}
onDraw函数在该view需要进行渲染时调用,设置了具体的画笔等参数,并调用draw进行具体绘制。
/**
* Draw this Layout on the specified canvas, with the highlight path drawn
* between the background and the text.
*
* @param canvas the canvas
* @param highlight the path of the highlight or cursor; can be null
* @param highlightPaint the paint for the highlight
* @param cursorOffsetVertical the amount to temporarily translate the
* canvas while rendering the highlight
*/
public void draw(Canvas canvas, Path highlight, Paint highlightPaint,
int cursorOffsetVertical) {
final long lineRange = getLineRangeForDraw(canvas);
int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical,
firstLine, lastLine);
drawText(canvas, firstLine, lastLine);
}
在draw方法中分别调用对背景以及具体文字的绘制函数。
总而言之,我们可以重写这些方法,来完成我们的控件自定义操作。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。