Step 7. FrameLayout.draw
- public class FrameLayout extends ViewGroup {
- ......
- private Drawable mForeground;
- ......
- public void draw(Canvas canvas) {
- super.draw(canvas);
- if (mForeground != null) {
- final Drawable foreground = mForeground;
- if (mForegroundBoundsChanged) {
- mForegroundBoundsChanged = false;
- final Rect selfBounds = mSelfBounds;
- final Rect overlayBounds = mOverlayBounds;
- final int w = mRight-mLeft;
- final int h = mBottom-mTop;
- if (mForegroundInPadding) {
- selfBounds.set(0, 0, w, h);
- } else {
- selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
- }
- Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
- foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
- foreground.setBounds(overlayBounds);
- }
- foreground.draw(canvas);
- }
- }
- ......
- }
这个函数定义在文件frameworks/base/core/java/android/widget/FrameLayout.java中。
FrameLayout类的成员函数draw首先调用父类View的成员函数draw来绘制它的UI内容,然后再检查它是否设置了一个前景图,即成员变量mForeground的值是否等于null。如果不等于null的话,那么就会先设置这个前景图的大小和位置,然后再调用用成员变量mForeground所指向的一个Drawable对象的成员函数draw来在画布canvas上绘制这个前景图。
接下来,我们就继续分析View类的成员函数draw的实现,以便可以了解应用程序窗口UI的绘制过程。
Step 8. View.draw
这个函数定义在文件frameworks/base/core/java/android/view/View.java中,它主要是完成以下六个操作:
1. 绘制当前视图的背景。
2. 保存当前画布的堆栈状态,并且在在当前画布上创建额外的图层,以便接下来可以用来绘制当前视图在滑动时的边框渐变效果。
3. 绘制当前视图的内容。
4. 绘制当前视图的子视图的内容。
5. 绘制当前视图在滑动时的边框渐变效果。
6. 绘制当前视图的滚动条。
在上面六个操作中,有些是可以优化的。例如,如果当前视图的某一个子视图是不透明的,并且覆盖了当前视图的内容,那么当前视图的背景以及内容就不会绘制了,即不用执行第1和第3个操作。又如,如果当前视图不是处于滑动的状态,那么第2和第5个操作也是不用执行的。
接下来我们就分段来阅读View类的成员函数draw的代码:
- public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
- ......
- public void draw(Canvas canvas) {
- ......
- final int privateFlags = mPrivateFlags;
- final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
- (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
- mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN;
View类的成员函数在继续往下执行之前,还会将成员变量mPrivateFlags的DIRTY_MASK位重置为0,以及将DRAWN位设置为1,因为接下来就要开始绘制当前视图的UI了。
我们继续往下阅读代码:
- // Step 1, draw the background, if needed
- int saveCount;
- if (!dirtyOpaque) {
- final Drawable background = mBGDrawable;
- if (background != null) {
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
- if (mBackgroundSizeChanged) {
- background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
- mBackgroundSizeChanged = false;
- }
- if ((scrollX | scrollY) == 0) {
- background.draw(canvas);
- } else {
- canvas.translate(scrollX, scrollY);
- background.draw(canvas);
- canvas.translate(-scrollX, -scrollY);
- }
- }
- }
我们继续往下阅读代码:
- // skip step 2 & 5 if possible (common case)
- final int viewFlags = mViewFlags;
- boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
- boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
- if (!verticalEdges && !horizontalEdges) {
- // Step 3, draw the content
- if (!dirtyOpaque) onDraw(canvas);
- // Step 4, draw the children
- dispatchDraw(canvas);
- // Step 6, draw decorations (scrollbars)
- onDrawScrollBars(canvas);
- // we're done...
- return;
- }
这段代码检查是否可以跳过上述的第2和第5个操作。当View类的成员变量mViewFlags的FADING_EDGE_HORIZONTAL位等于1的时候,就说明当前视图正在处于水平滑动状态,并且需要绘制水平边框的渐变效果。同样,当View类的成员变量mViewFlags的FADING_EDGE_VERTICAL位等于1的时候,就说明当前视图正在处于垂直滑动状态,并且需要绘制垂直边框的渐变效果。但是,如果当前视图不是处于滑动状态,即变量horizontalEdges和verticalEdges的值均等于false的时候,那么就不需要执行上述的第2和第5个操作了,而只需要执行第3、第4和第6个操作。注意,当变量dirtyOpaque的值等于true的时候,第3个操作也是不需要执行的。
我们继续往下分析代码:
- boolean drawTop = false;
- boolean drawBottom = false;
- boolean drawLeft = false;
- boolean drawRight = false;
- float topFadeStrength = 0.0f;
- float bottomFadeStrength = 0.0f;
- float leftFadeStrength = 0.0f;
- float rightFadeStrength = 0.0f;
- // Step 2, save the canvas' layers
- int paddingLeft = mPaddingLeft;
- int paddingTop = mPaddingTop;
- final boolean offsetRequired = isPaddingOffsetRequired();
- if (offsetRequired) {
- paddingLeft += getLeftPaddingOffset();
- paddingTop += getTopPaddingOffset();
- }
- int left = mScrollX + paddingLeft;
- int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
- int top = mScrollY + paddingTop;
- int bottom = top + mBottom - mTop - mPaddingBottom - paddingTop;
- if (offsetRequired) {
- right += getRightPaddingOffset();
- bottom += getBottomPaddingOffset();
- }
- final ScrollabilityCache scrollabilityCache = mScrollCache;
- int length = scrollabilityCache.fadingEdgeLength;
- // clip the fade length if top and bottom fades overlap
- // overlapping fades produce odd-looking artifacts
- if (verticalEdges && (top + length > bottom - length)) {
- length = (bottom - top) / 2;
- }
- // also clip horizontal fades if necessary
- if (horizontalEdges && (left + length > right - length)) {
- length = (right - left) / 2;
- }
- if (verticalEdges) {
- topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
- drawTop = topFadeStrength >= 0.0f;
- bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
- drawBottom = bottomFadeStrength >= 0.0f;
- }
- if (horizontalEdges) {
- leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
- drawLeft = leftFadeStrength >= 0.0f;
- rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
- drawRight = rightFadeStrength >= 0.0f;
- }
- saveCount = canvas.getSaveCount();
- int solidColor = getSolidColor();
- if (solidColor == 0) {
- final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
- if (drawTop) {
- canvas.saveLayer(left, top, right, top + length, null, flags);
- }
- if (drawBottom) {
- canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
- }
- if (drawLeft) {
- canvas.saveLayer(left, top, left + length, bottom, null, flags);
- }
- if (drawRight) {
- canvas.saveLayer(right - length, top, right, bottom, null, flags);
- }
- } else {
- scrollabilityCache.setFadeColor(solidColor);
- }
本来通过View类的成员变量mPaddingLeft、mPaddingRight、mPaddingTop和mPaddingBottom就可以得到当视图的左、右、上以及下内边距的大小的,但是有时候我们在定制一个视图的时候,可能会需要在视图的内边距上绘制其它额外的东西,这时候就有扩展视图的内边距的需求。如果有扩展视图的内边距的需求,那么就需要重写View类的成员函数isPaddingOffsetRequired,即将它的返回值设置为true,并且重载另外四个成员函数getLeftPaddingOffset、getRightPaddingOffset、getTopPaddingOffset和getBottomPaddingOffset来提供额外的左、右、上以及下内边距。
这段代码经过计算后,就得到四个值left、right、top和bottom,它们分别表示当前视图可以用来绘制的内容区域,这个区域已经将内置的和扩展的内边距排除之外。
计算好left、right、top和bottom这四个值之后,就相当于得到左、右、上以及下边框的起始位置了,但是我还需要知道边框的长度,才能确定左、右、上以及下边框所要绘制的区域。
边框的长度length设置在View类的成员变量mScrollCache所指向的一个ScrollabilityCache对象的成员变量fadingEdgeLength中。但是,这个预先设置的边框长度length不一定适合当前视图使用。这是因为视图的大小是可以随时改变的,一旦发生了改变之后,原先设置的边框长度length可能就会显得过长。具体来说,就是当上下两个边框或者左右两个边框发生重叠时,就说明原先设置的边框长度过长了。在这种情况下,就要将边框长度length修改为当前视图的内容区域的高度和宽度的较小者的一半,以便可以保证上下两个边框或者左右两个边框不会发生重叠。
左、右、上以及下边框都对应有一个强度值,强度值的取值范围为[0.0, 1.0]。如果一个边框的强度值等于0.0,那么它就是不可见的,这时候就不需要绘制它的渐变效果。另一方面,如果一个边框的强度值等于1.0,那么它的长度等于原来设置的长度。我们可以将这个强度值理解为一个缩放因子。左、右、上以及下边框的强度值可以分别通过调用View类的成员函数getLeftFadingEdgeStrength、getRightFadingEdgeStrength、getTopFadingEdgeStrength以及getBottomFadingEdgeStrength来获得。注意,只有在变量verticalEdges的值等于true的时候,这段代码才会计算上下两个边框的强度值topFadeStrength和bottomFadeStrength;同样,只有变量horizontalEdges的值等于true的时候,这代码才会计算左右两个边框的强度值leftFadeStrength和rightFadeStrength。
计算好左、右、上以及下边框的强度值leftFadeStrength、rightFadeStrength、topFadeStrength以及bottomFadeStrength之后,这段代码就会判断它们的值是否大于0。如果大于0,那么与它们所对应的四个变量drawLeft、drawRight、drawTop以及drawBottom的值就会等于true,表示需要绘制左、右、上以及下四个边框的渐变效果。
View类的成员函数getSolidColor返回的是当前视图的背景颜色。如果当前视图的背景颜色是纯色的,即变量solidColor的值不等于0,那么这时候就会使用这个背景颜色来绘制边框的渐变效果,即调用变量scrollabilityCache所指向的一个ScrollabilityCache对象的成员函数setFadeColor来将将边框的渐变效果颜色设置为solidColor,这种情况是比较简单的。如果当前视图的背景颜色不是纯色的,即变量solidColor的值等于0,这种情况就比较复杂了,我们需要创建在参数canvas所描述的一块画布上来创建额外的图层来绘制边框的渐变效果,这样做是为了能够使用背景颜色来绘制边框的渐变效果。
参数canvas所描述的一块画布上来创建额外的图层是通过调用它的成员函数saveLayer来实现的。我们注意到在调用参数canvas所指向的一个Canvas对象的成员函数saveLayer的时候,最后一个参数指定为Canvas.HAS_ALPHA_LAYER_SAVE_FLAG,这表示在将额外创建的图层合成到参数canvas所描述的一块画布上去,要给额外创建的图层设置一个透明度值。同时,我们还可以看出,当前视图的左、右、上和下边框所占据的范围分别为(left, top, left + length, bottom)、(right - length, top, right, bottom)、(left, top, right, top + length)和(left, bottom - length, right, bottom)。还有另外一个地方需要注意的是,在参数canvas所描述的一块画布上来创建额外的图层之前,这段代码首先会获得画布的当前堆栈状态,这是通过一个整数saveCount来描述的,并且这个整数是通过调用参数canvas所指向的一个Canvas对象的成员函数getSaveCount来获得的。这样,后面在额外创建的图层上绘制了边框的渐变效果之后,就可以通过前面得到的整数saveCount将恢复画布的堆栈状态,也就是将前面额外创建的图层合成到画布上来。
我们接着往下阅读代码:
- // Step 3, draw the content
- if (!dirtyOpaque) onDraw(canvas);
- // Step 4, draw the children
- dispatchDraw(canvas);
这段代码用来执行上述的第3和第4个操作,即绘制当前视图的内容,以及当前视图的子视图的内容,它们分别是通过调用View类的成员函数onDraw和dispatchDraw来实现的。本文主要关注当前视图的子视图的绘制过程,因此,在接下来的Step 9中,我们再详细分析View类的成员函数dispatchDraw的实现。
我们接着往下阅读代码:
- // Step 5, draw the fade effect and restore layers
- final Paint p = scrollabilityCache.paint;
- final Matrix matrix = scrollabilityCache.matrix;
- final Shader fade = scrollabilityCache.shader;
- final float fadeHeight = scrollabilityCache.fadingEdgeLength;
- if (drawTop) {
- matrix.setScale(1, fadeHeight * topFadeStrength);
- matrix.postTranslate(left, top);
- fade.setLocalMatrix(matrix);
- canvas.drawRect(left, top, right, top + length, p);
- }
- if (drawBottom) {
- matrix.setScale(1, fadeHeight * bottomFadeStrength);
- matrix.postRotate(180);
- matrix.postTranslate(left, bottom);
- fade.setLocalMatrix(matrix);
- canvas.drawRect(left, bottom - length, right, bottom, p);
- }
- if (drawLeft) {
- matrix.setScale(1, fadeHeight * leftFadeStrength);
- matrix.postRotate(-90);
- matrix.postTranslate(left, top);
- fade.setLocalMatrix(matrix);
- canvas.drawRect(left, top, left + length, bottom, p);
- }
- if (drawRight) {
- matrix.setScale(1, fadeHeight * rightFadeStrength);
- matrix.postRotate(90);
- matrix.postTranslate(right, top);
- fade.setLocalMatrix(matrix);
- canvas.drawRect(right - length, top, right, bottom, p);
- }
- canvas.restoreToCount(saveCount);
由于当前视图的左、右、上以及下边框的渐变效果是在参数canvas所描述的一块画布的额外创建的图层上绘制的,因此,在绘制完之后,这段代码需要调用参数canvas所指向的一个Canvas对象来恢复参数canvas所描述的一块画布在创建额外图层时的堆栈状态,即相当于是将前面所绘制的边框渐变效果合成到参数canvas所描述的一块画布来。
我们继续往下阅读最后一段代码:
- // Step 6, draw decorations (scrollbars)
- onDrawScrollBars(canvas);
- }
- ......
- }
接下来,我们就主要关注当前视图的子视图的绘制过程,即View类的成员函数dispatchDraw的实现。注意,View类的成员函数dispatchDraw是一个空实现,它是由子类ViewGroup来重写的,也就是说,只有当一个视图描述的是一个视图容器时,它才会重写父类View的成员函数dispatchDraw。
前面我们已经假设当前正在处理的视图是应用程序窗口的顶层视图,即它是一个类型为DecorView视图。DecorView类是从ViewGroup类继承下来的,并且在ViewGroup类中重写了父类View类的成员函数dispatchDraw。因此,接下来我们就继续分析ViewGroup的成员函数dispatchDraw的实现。
Step 9. ViewGroup.dispatchDraw
这个函数定义在文件frameworks/base/core/java/android/view/ViewGroup.java中,它的实现比较长,我们分段来阅读:
- public abstract class ViewGroup extends View implements ViewParent, ViewManager {
- ......
- @Override
- protected void dispatchDraw(Canvas canvas) {
- final int count = mChildrenCount;
- final View[] children = mChildren;
- int flags = mGroupFlags;
我们继续往下阅读代码:
- if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
- ......
- for (int i = 0; i < count; i++) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
- ......
- bindLayoutAnimation(child);
- ......
- }
- }
- final LayoutAnimationController controller = mLayoutAnimationController;
- ......
- controller.start();
- ......
- if (mAnimationListener != null) {
- mAnimationListener.onAnimationStart(controller.getAnimation());
- }
- }
这段代码用来检查当前视图组的子视图是否需要显示动画。如果变量flags的FLAG_RUN_ANIMATION位等于1,并且ViewGroup类的成员函数canAnimate的返回值等于true,即当前当前视图组允许其子视图显示动画,那么这段代码接下来就要开始显示动画了。
这段代码首先检查当前视图组的每一个子视图child,如果它是可见的,那么就会调用ViewGroup类的另外一个成员函数bindLayoutAnimation来设置它的动画。设置完成子视图的动画之后,这段代码接下来再调用ViewGroup类的成员变量mLayoutAnimationController所指向的一个LayoutAnimationController对象的成员函数start来启动动画,并且调用ViewGroup类的成员变量mAnimationListener所指向的一个AnimationListener对象的成员函数onAnimationStart来通知那些注册到当前视图组的动画监听者,当前视图组开始显示动画了。
我们继续往下阅读代码:
- int saveCount = 0;
- final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
- if (clipToPadding) {
- saveCount = canvas.save();
- canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
- mScrollX + mRight - mLeft - mPaddingRight,
- mScrollY + mBottom - mTop - mPaddingBottom);
- }
在设置参数canvas所描述的一块画布的剪裁区域之前,这段代码会先调用参数canvas所指向的一个Canvas对象的成员函数save来保存它的堆栈状态,以便在绘制完成当前视图组的UI之后,可以恢复canvas所描述的一块画布的堆栈状态。
我们继续往下阅读代码:
- boolean more = false;
- final long drawingTime = getDrawingTime();
- if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
- for (int i = 0; i < count; i++) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
- more |= drawChild(canvas, child, drawingTime);
- }
- }
- } else {
- for (int i = 0; i < count; i++) {
- final View child = children[getChildDrawingOrder(count, i)];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
- more |= drawChild(canvas, child, drawingTime);
- }
- }
- }
注意,当变量flags的FLAG_USE_CHILD_DRAWING_ORDER位等于0的时候,就表示当前视图组的子视图按照它们在数组children中的位置从小到在三类绘制,否则的话,就需要通过ViewGroup类的成员函数getChildDrawingOrder来决定这些子视图的绘制顺序。
我们接着往下阅读代码:
- // Draw any disappearing views that have animations
- if (mDisappearingChildren != null) {
- final ArrayList disappearingChildren = mDisappearingChildren;
- final int disappearingCount = disappearingChildren.size() - 1;
- // Go backwards -- we may delete as animations finish
- for (int i = disappearingCount; i >= 0; i--) {
- final View child = disappearingChildren.get(i);
- more |= drawChild(canvas, child, drawingTime);
- }
- }
我们继续往下阅读最后一段代码:
- if (clipToPadding) {
- canvas.restoreToCount(saveCount);
- }
- // mGroupFlags might have been updated by drawChild()
- flags = mGroupFlags;
- if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
- invalidate();
- }
- if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
- mLayoutAnimationController.isDone() && !more) {
- // We want to erase the drawing cache and notify the listener after the
- // next frame is drawn because one extra invalidate() is caused by
- // drawChild() after the animation is over
- mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
- final Runnable end = new Runnable() {
- public void run() {
- notifyAnimationListener();
- }
- };
- post(end);
- }
- }
- ......
1. 检查变量clipToPadding的值是否等于true。如果是的话,那么就说明前面设置过参数canvas所描述的一块画布的裁剪区域。由于现在已经在这块画布上绘制完成当前视图组的UI了,因此,就需要恢复参数canvas所描述的一块画布堆栈状态。这是通过调用参数canvas所指向的一个Canvas对象的成员函数restoreToCount来实现的。
2. 前面在绘制当前视图组的子视图的UI的时候,有可能会需要修改当前视图组的标志位,即修改ViewGroup类的成员变量mGroupFlags的值。如果修改后的mGroupFlags的FLAG_INVALIDATE_REQUIRED位等于1,那么就说明当前视图组需要重新发起一个绘制UI的请求。这是通过调用ViewGroup类的另外一个成员函数invalidate来实现的。
3. 如果当前视图组的动画已经显示完成,并且当前视图组的子视图的动画也已经显示完成,再并且当前视图组注册有动画监听者,那么就是会调用ViewGroup类的另外一个成员函数notifyAnimationListener来通知这些动画监听者,当前视图组的动画已经显示结束。注意,ViewGroup类的成员函数notifyAnimationListener是以消息的形式来调用的,即ViewGroup类的成员函数dispatchDraw不是在动画一显示结束,就马上通知那些动画监听者。
接下来,我们就继续分析ViewGroup类的成员函数drawChild的实现,以便可以了解一个视图组的子视图的绘制过程。
Step 10. ViewGroup.drawChild
这个函数定义在文件frameworks/base/core/java/android/view/ViewGroup.java中,它的实现比较长,我们分段来阅读:
- public abstract class ViewGroup extends View implements ViewParent, ViewManager {
- ......
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- boolean more = false;
- final int cl = child.mLeft;
- final int ct = child.mTop;
- final int cr = child.mRight;
- final int cb = child.mBottom;
- final int flags = mGroupFlags;
这段代码首先获得子视图child的区域(cl, ct, cr, cb),以及当前视图组的标志位flags,以便接下来可以使用。另外,变量more的值用来表示子视图child是否还在显示动画。
我们接着往下阅读代码:
- Transformation transformToApply = null;
- final Animation a = child.getAnimation();
- ......
- if (a != null) {
- ......
- if (mChildTransformation == null) {
- mChildTransformation = new Transformation();
- }
- more = a.getTransformation(drawingTime, mChildTransformation);
- transformToApply = mChildTransformation;
- ......
- } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
- FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
- if (mChildTransformation == null) {
- mChildTransformation = new Transformation();
- }
- final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
- if (hasTransform) {
- final int transformType = mChildTransformation.getTransformationType();
- transformToApply = transformType != Transformation.TYPE_IDENTITY ?
- mChildTransformation : null;
- ......
- }
- }
对于第一种情况,子视图child的成员函数getAnimation的返回值a不等于null,并且它所指向的一个Animation对象就是用来描述子视图child的动画的。获得了子视图的动画对象a之后,我们就可以调用它的成员函数getTransformation来继续执行它的动画了。如果该动画还需要继续执行,那么调用Animation对象a的成员函数getTransformation的返回值more就会等于true,并且会返回子视图child的接下来需要使用的变换矩阵,保存在ViewGroup类的成员变量mChildTransformation中。ViewGroup类的成员变量mChildTransformation最后又会保存在变量transformToApply中。
对于第二种情况,变量flags的FLAG_SUPPORT_STATIC_TRANSFORMATIONS位等于1,这时候调用ViewGroup类的成员函数getChildStaticTransformation就可以知道子视图child是否被设置了一个变换矩阵。如果设置了的话,那么ViewGroup类的成员函数getChildStaticTransformation的返回值hasTransform就会等于true。在这种情况下,ViewGroup类的成员变量mChildTransformation所描述的变换矩阵就是要应用在子视图child中的。不过有一个前提,即ViewGroup类的成员变量mChildTransformation所描述的变换矩阵不是一个单位矩阵,这是因为单位矩阵是没有变换效果的。如果ViewGroup类的成员变量mChildTransformation所描述的变换矩阵不是一个单位矩阵,那么它同样会被保存在变量transformToApply中。
未完。。。
- 相关文章
- Android应用程序窗口(Activity)的测量(Measure)、布局(L (4人浏览)
- Android应用程序窗口(Activity)的测量(Measure)、布局(L (1人浏览)
- Android应用程序窗口(Activity)的视图对象(View)的创建过程分 (2人浏览)
- Android应用程序窗口(Activity)的窗口对象(Window)的创建过 (0人浏览)
- Android应用程序窗口(Activity)的运行上下文环境(Context) (0人浏览)
- Android应用程序窗口(Activity)实现框架简要介绍和学习计划 (0人浏览)
- Android系统Surface机制的SurfaceFlinger服务的线程模型 (3人浏览)