/**
* ViewRoot中的draw()函数主要处理一些根视图中的特有属性,并且处理完毕后同样要调用View类
* 中 的draw()进行具体的绘制。
* Surface按照底层的驱动模式可以分为两种,一种是使用图形加速支持的Surface,俗称显卡,另一
* 种是使用CPU及内存模拟的Surface。因此,根视图中将针对不同的Surface采用不同的方式从该Surface
* 中获取一个Canvas对象,并 将 该Canvas对象派发到整个视图中,对于非根视图而言,它并不区分底层
* 是使用显卡模式,还是使用CPU模式。
* @param fullRedrawNeeded
*/
private void draw(boolean fullRedrawNeeded) {
/**
* 检 查 Surface是否无效。在正常情况下, Surface都是有效的,除 非WmS发生异常不能为该客
* 户端分配有效的Surface, isValide()才会返回false。如 果 Surface无效,则终止绘制过程
*/
Surface surface = mSurface;
if (surface == null || !surface.isValid()) {
return;
}
/**
* 执行注册过的Runnable对象。 ViewRoot中使用一个静态列表,可以向该静态列表中添加一些
* Runnable对象,本步骤则把这些Runnable对象调用post()发送 到Handler队列中,以便下次消息循环时
* 处理这些Runnable对象。这个变量的名称是sFirstDrawComplete,有些读者可能觉得变量名称有点奇怪,
* 为什么是Complete呢,明明还没有开始绘制?因为该变量中保存的Runnable对象会在下个消息循环中
* 执行,而执行前,接下来的绘制过程必须先被执行。
*/
if (!sFirstDrawComplete) {
synchronized (sFirstDrawHandlers) {
sFirstDrawComplete = true;
for (int i=0; i<sFirstDrawHandlers.size(); i++) {
post(sFirstDrawHandlers.get(i));
}
}
}
/**
* 调 用 scrollToRectOrFocusO。几乎在所有的情况下,该函数内部都不会执行什么,所以其内部
* 执行流程忽略。该函数本来的设计目的是对mScmllY变量进行调整,调整的依据是调整到第一个Focus
* 视图中。
*/
scrollToRectOrFocus(null, false);
/**
* 如果根视图内部包含Scroller对象,则调用该对象的computeScrollOffset()获取新的滚动值,并
* 赋值给局部变量yoff,关 于Scroller的详细意义将在后面小节中单独介绍。该对象的computeScrollOffset()
* 的意义是计算是否发生滚动,该函数返回值类型为boolean,比如,当用户在一个ListView上滑动手指
* 后 ,会在一小段时间内发生滚动,ListView内部有一个Scroller对象,在这个期间调用computeScrollOffsetO
* 将 返 回true。
*/
if (mAttachInfo.mViewScrollChanged) {
mAttachInfo.mViewScrollChanged = false;
mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
}
/**
* 判 断 该Surface是 否 有SurfaceHolder对象。如果有则意味着该Sb*face是应用程序创建的,因
* 此所有的绘制操作应该由应用程序自身去负责,于 是 View系统退出绘制。如果不是,才 开 始View绘
* 制的内部流程。
*/
int yoff;
final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
if (scrolling) {
yoff = mScroller.getCurrY();
} else {
yoff = mScrollY;
}
if (mCurScrollY != yoff) {
mCurScrollY = yoff;
fullRedrawNeeded = true;
}
float appScale = mAttachInfo.mApplicationScale;
boolean scalingRequired = mAttachInfo.mScalingRequired;
Rect dirty = mDirty;
if (mSurfaceHolder != null) {
// The app owns the surface, we won't draw.
dirty.setEmpty();
return;
}
/**
* 如 果 Surface是 由 OpenGL实现的,则开始按照G L 的处理方式进行处理。
*/
if (mUseGL) {
if (!dirty.isEmpty()) {
/**
* (1 )以全局变量mGlCanvas作 为 canvas的值,并 调 用canvas.save()保 存 该Canvas内部的各种属性
* 及状态,因为接下来View树内部在绘制的过程中会修改Canvas对象的相关属性。
*/
Canvas canvas = mGlCanvas;
if (mGL != null && canvas != null) {
mGL.glDisable(GL_SCISSOR_TEST);
mGL.glClearColor(0, 0, 0, 0);
mGL.glClear(GL_COLOR_BUFFER_BIT);
mGL.glEnable(GL_SCISSOR_TEST);
mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
mAttachInfo.mIgnoreDirtyState = true;
mView.mPrivateFlags |= View.DRAWN;
/**
* (2 )如果根视图内部包含Translator对象,则需要先经过Translator对象对该canvas对象进行一定
* 的调整。 Translator的作用主要是根据设备的硬件参数对Canvas的相关绘制属性进行一定的调整,该函
* 数的内部一般由驱动设计者支持实现。调整的过程由三个函数调用组成,
*/
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
/**
* 调 用canvas的 setScreenDensity()设置屏幕密度。这里需要区分不同语境中density的含义的不同。
* 首先,定义屏幕密度(density) 概念的作用是为了让不同分辨率的屏幕显示的视图能够看起来大小
* 相同。在传统的编程方式中,如果用像素指定视图的大小,对于分辨率高的显示器而言,像素密度高,
* 所以相同像素的实际尺寸会变小,于是引入density的概念。 Android中 160dpi的屏幕的density值定义
* 为 1,如果屏幕分辨率增加,比如240dpi,那 么 density的值也就越高,为 240/160=1.5,而分辨率低的
* 屏幕,比 如 120dpi,其密度就低,为 120/160=0.75。有了 density后,应用程序可以设置视图的大小单
* 位 为 dip,即密度无关像素(density independent pixel),从而在绘制视图时, View系统会根据不同的屏
* 幕分辨率将其换算成不同的像素。
* 而本步设置的screenDensity,却是指屏幕的分辨率值。当 参 数scalingRequired为 true时,该值为
* DisplayMetric.DENSITY_DEVICE,该常量是在系统启动时调用getProp()函数获取的设备参数,比如240、
* 160、 120等;如 果 scalingRequired为 false,那 么 screenDensity将被赋值为0, 0 是一个特殊值,并不是
* 说屏幕分辨率为0 , 而是指视图绘制没有指定具体的分辨率,从而在绘制时一个dpi将对应一个真实的
* 物理像素。
*/
canvas.setScreenDensity(scalingRequired
? DisplayMetrics.DENSITY_DEVICE : 0);
/**
*(4) 调 用 mView.draw(canvas)。该步骤才真正启动视图树的绘制过程,注意这里是将canvas作为
*参数,这也就是为什么应用程序中不能保存这个Canvas的原因,因为它是一个临时变量。 mView.draw()
* 实际调用的是View类 的 dmw()函数,关于其内部流程将在后面小节中介绍。
*/
mView.draw(canvas);
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
}
} finally {
/**
* ( 5 ) 完成视图树的绘制后,绘制工作就算结束了,因为调用Canvas的 restoreToCount()将 Canvas
* 的内部状态恢复到绘制之前,该步骤与前面的canvas.saveO函数是对称调用的。
*/
canvas.restoreToCount(saveCount);
}
mAttachInfo.mIgnoreDirtyState = false;
mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
checkEglErrors();
/**
* 如 果 是 Debug模式,并且模式中要求显示FPS,即CPU的使用率,则调用一个native函数
* nativeShowFPSO给屏幕上方绘制一个条状的统计图。
*/
if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
int now = (int)SystemClock.elapsedRealtime();
if (sDrawTime != 0) {
nativeShowFPS(canvas, now - sDrawTime);
}
sDrawTime = now;
}
}
}
/**
* 最后,如果屏幕正在滚动,则需要再次发起一个重绘命令scheduleTravasals(),以便接着绘制,
* 直到滚动结束,滚动的标志scrolling来 源 于Scroller对 象 的computeScrollOffset()函数返回值。
*/
if (scrolling) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
return;
}
/**
* 如 果 Surface不 是 OpenGL实现的,则开始按照非G L 的处理方式进行处理。该步骤内部与上
* 一步基本上是相同的,唯一的区别在于如何获得Canvas对象。 G L方式中,内部使用mGlCanvas全局
* 变 量 保 存canvas对象,该变量是在G L 的初始化时进行赋值的;而 非 G L 方式中, Canvas对象需要调
* 用 surface对 象 的lockCanvas()获取,其他过程完全相同,此处不再赘述。
* 至此, ViewRoot中 的 draw()函数就执行完毕,一次绘制过程也就结束了,下一次的绘制将在下一
* 个消息循环中执行。
*/
if (fullRedrawNeeded) {
mAttachInfo.mIgnoreDirtyState = true;
dirty.union(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(TAG, "Draw " + mView + "/"
+ mWindowAttributes.getTitle()
+ ": dirty={" + dirty.left + "," + dirty.top
+ "," + dirty.right + "," + dirty.bottom + "} surface="
+ surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
appScale + ", width=" + mWidth + ", height=" + mHeight);
}
if (!dirty.isEmpty() || mIsAnimating) {
Canvas canvas;
try {
int left = dirty.left;
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
canvas = surface.lockCanvas(dirty);
if (left != dirty.left || top != dirty.top || right != dirty.right ||
bottom != dirty.bottom) {
mAttachInfo.mIgnoreDirtyState = true;
}
// TODO: Do this in native
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
Log.e(TAG, "OutOfResourcesException locking surface", e);
// TODO: we should ask the window manager to do something!
// for now we just do nothing
return;
} catch (IllegalArgumentException e) {
Log.e(TAG, "IllegalArgumentException locking surface", e);
// TODO: we should ask the window manager to do something!
// for now we just do nothing
return;
}
try {
if (!dirty.isEmpty() || mIsAnimating) {
long startTime = 0L;
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
+ canvas.getWidth() + ", h=" + canvas.getHeight());
//canvas.drawARGB(255, 255, 0, 0);
}
if (Config.DEBUG && ViewDebug.profileDrawing) {
startTime = SystemClock.elapsedRealtime();
}
// If this bitmap's format includes an alpha channel, we
// need to clear it before drawing so that the child will
// properly re-composite its drawing on a transparent
// background. This automatically respects the clip/dirty region
// or
// If we are applying an offset, we need to clear the area
// where the offset doesn't appear to avoid having garbage
// left in the blank areas.
if (!canvas.isOpaque() || yoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
mView.mPrivateFlags |= View.DRAWN;
if (DEBUG_DRAW) {
Context cxt = mView.getContext();
Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
", metrics=" + cxt.getResources().getDisplayMetrics() +
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired
? DisplayMetrics.DENSITY_DEVICE : 0);
mView.draw(canvas);
} finally {
mAttachInfo.mIgnoreDirtyState = false;
canvas.restoreToCount(saveCount);
}
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
}
if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
int now = (int)SystemClock.elapsedRealtime();
if (sDrawTime != 0) {
nativeShowFPS(canvas, now - sDrawTime);
}
sDrawTime = now;
}
if (Config.DEBUG && ViewDebug.profileDrawing) {
EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
}
}
} finally {
surface.unlockCanvasAndPost(canvas);
}
}
if (LOCAL_LOGV) {
Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
}
if (scrolling) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
ViewRoot的draw()分析
最新推荐文章于 2022-07-12 17:12:52 发布