public virtual void invalidateChild(android.view.View child, android.graphics.Rect dirty) { android.view.ViewParent parent = this; android.view.View.AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { // If the child is drawing an animation, we want to copy this flag onto // ourselves and the parent to make sure the invalidate request goes // through bool drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; if (dirty == null) { if (child.mLayerType != LAYER_TYPE_NONE) { mPrivateFlags |= INVALIDATED; mPrivateFlags &= ~DRAWING_CACHE_VALID; child.mLocalDirtyRect.setEmpty(); } do { android.view.View view = null; if (parent is android.view.View) { view = (android.view.View)parent; if (view.mLayerType != LAYER_TYPE_NONE) { view.mLocalDirtyRect.setEmpty(); if (view.getParent() is android.view.View) { android.view.View grandParent = (android.view.View)view.getParent(); grandParent.mPrivateFlags |= INVALIDATED; grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID; } } if ((view.mPrivateFlags & DIRTY_MASK) != 0) { // already marked dirty - we're done break; } } if (drawAnimation) { if (view != null) { view.mPrivateFlags |= DRAW_ANIMATION; } else { if (parent is android.view.ViewRootImpl) { ((android.view.ViewRootImpl)parent).mIsAnimating = true; } } } if (parent is android.view.ViewRootImpl) { ((android.view.ViewRootImpl)parent).invalidate(); parent = null; } else { if (view != null) { if ((view.mPrivateFlags & DRAWN) == DRAWN || (view.mPrivateFlags & DRAWING_CACHE_VALID ) == DRAWING_CACHE_VALID) { view.mPrivateFlags &= ~DRAWING_CACHE_VALID; view.mPrivateFlags |= DIRTY; parent = view.mParent; } else { parent = null; } } } } while (parent != null); } else { // Check whether the child that requests the invalidate is fully opaque bool isOpaque_1 = child.isOpaque() && !drawAnimation && child.getAnimation() == null; // Mark the child as dirty, using the appropriate flag // Make sure we do not set both flags at the same time int opaqueFlag = isOpaque_1 ? DIRTY_OPAQUE : DIRTY; if (child.mLayerType != LAYER_TYPE_NONE) { mPrivateFlags |= INVALIDATED; mPrivateFlags &= ~DRAWING_CACHE_VALID; child.mLocalDirtyRect.union(dirty); } int[] location = attachInfo.mInvalidateChildLocation; location[CHILD_LEFT_INDEX] = child.mLeft; location[CHILD_TOP_INDEX] = child.mTop; android.graphics.Matrix childMatrix = child.getMatrix(); if (!childMatrix.isIdentity()) { android.graphics.RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); //boundingRect.inset(-0.5f, -0.5f); childMatrix.mapRect(boundingRect); dirty.set((int)(boundingRect.left - 0.5f), (int)(boundingRect.top - 0.5f), (int)( boundingRect.right + 0.5f), (int)(boundingRect.bottom + 0.5f)); } do { android.view.View view = null; if (parent is android.view.View) { view = (android.view.View)parent; if (view.mLayerType != LAYER_TYPE_NONE && view.getParent() is android.view.View) { android.view.View grandParent = (android.view.View)view.getParent(); grandParent.mPrivateFlags |= INVALIDATED; grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID; } } if (drawAnimation) { if (view != null) { view.mPrivateFlags |= DRAW_ANIMATION; } else { if (parent is android.view.ViewRootImpl) { ((android.view.ViewRootImpl)parent).mIsAnimating = true; } } } // If the parent is dirty opaque or not dirty, mark it dirty with the opaque // flag coming from the child that initiated the invalidate if (view != null) { if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && view.getSolidColor() == 0) { opaqueFlag = DIRTY; } if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) { view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; } } parent = parent.invalidateChildInParent(location, dirty); if (view != null) { // Account for transform on current parent android.graphics.Matrix m = view.getMatrix(); if (!m.isIdentity()) { android.graphics.RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); m.mapRect(boundingRect); dirty.set((int)boundingRect.left, (int)boundingRect.top, (int)(boundingRect.right + 0.5f), (int)(boundingRect.bottom + 0.5f)); } } } while (parent != null); } } }
/// <summary>Finishes the removal of a detached view.</summary> /// <remarks> /// Finishes the removal of a detached view. This method will dispatch the detached from /// window event and notify the hierarchy change listener. /// </remarks> /// <param name="child">the child to be definitely removed from the view hierarchy</param> /// <param name="animate"> /// if true and the view has an animation, the view is placed in the /// disappearing views list, otherwise, it is detached from the window /// </param> /// <seealso cref="attachViewToParent(View, int, LayoutParams)">attachViewToParent(View, int, LayoutParams) /// </seealso> /// <seealso cref="detachAllViewsFromParent()">detachAllViewsFromParent()</seealso> /// <seealso cref="detachViewFromParent(View)">detachViewFromParent(View)</seealso> /// <seealso cref="detachViewFromParent(int)">detachViewFromParent(int)</seealso> protected internal virtual void removeDetachedView(android.view.View child, bool animate_1) { if (mTransition != null) { mTransition.removeChild(this, child); } if (child == mFocused) { child.clearFocus(); } if ((animate_1 && child.getAnimation() != null) || (mTransitioningViews != null && mTransitioningViews.contains(child))) { addDisappearingView(child); } else { if (child.mAttachInfo != null) { child.dispatchDetachedFromWindow(); } } onViewRemoved(child); }
/// <summary>Draw one child of this View Group.</summary> /// <remarks> /// Draw one child of this View Group. This method is responsible for getting /// the canvas in the right state. This includes clipping, translating so /// that the child's scrolled origin is at 0, 0, and applying any animation /// transformations. /// </remarks> /// <param name="canvas">The canvas on which to draw the child</param> /// <param name="child">Who to draw</param> /// <param name="drawingTime">The time at which draw is occuring</param> /// <returns>True if an invalidate() was issued</returns> protected internal virtual bool drawChild(android.graphics.Canvas canvas, android.view.View child, long drawingTime) { bool more = false; int cl = child.mLeft; int ct = child.mTop; int cr = child.mRight; int cb = child.mBottom; bool childHasIdentityMatrix = child.hasIdentityMatrix(); int flags = mGroupFlags; if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) { mChildTransformation.clear(); mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION; } android.view.animation.Transformation transformToApply = null; android.view.animation.Transformation invalidationTransform; android.view.animation.Animation a = child.getAnimation(); bool concatMatrix = false; bool scalingRequired = false; bool caching; int layerType = mDrawLayers ? child.getLayerType() : LAYER_TYPE_NONE; bool hardwareAccelerated = canvas.isHardwareAccelerated(); if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) { caching = true; if (mAttachInfo != null) { scalingRequired = mAttachInfo.mScalingRequired; } } else { caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated; } if (a != null) { bool initialized = a.isInitialized(); if (!initialized) { a.initialize(cr - cl, cb - ct, getWidth(), getHeight()); a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct); child.onAnimationStart(); } more = a.getTransformation(drawingTime, mChildTransformation, scalingRequired ? mAttachInfo .mApplicationScale : 1f); if (scalingRequired && mAttachInfo.mApplicationScale != 1f) { if (mInvalidationTransformation == null) { mInvalidationTransformation = new android.view.animation.Transformation(); } invalidationTransform = mInvalidationTransformation; a.getTransformation(drawingTime, invalidationTransform, 1f); } else { invalidationTransform = mChildTransformation; } transformToApply = mChildTransformation; concatMatrix = a.willChangeTransformationMatrix(); if (more) { if (!a.willChangeBounds()) { if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) == FLAG_OPTIMIZE_INVALIDATE) { mGroupFlags |= FLAG_INVALIDATE_REQUIRED; } else { if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) { // The child need to draw an animation, potentially offscreen, so // make sure we do not cancel invalidate requests mPrivateFlags |= DRAW_ANIMATION; invalidate(cl, ct, cr, cb); } } } else { if (mInvalidateRegion == null) { mInvalidateRegion = new android.graphics.RectF(); } android.graphics.RectF region = mInvalidateRegion; a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform); // The child need to draw an animation, potentially offscreen, so // make sure we do not cancel invalidate requests mPrivateFlags |= DRAW_ANIMATION; int left = cl + (int)region.left; int top = ct + (int)region.top; invalidate(left, top, left + (int)(region.width() + .5f), top + (int)(region.height () + .5f)); } } } else { if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) == FLAG_SUPPORT_STATIC_TRANSFORMATIONS) { bool hasTransform = getChildStaticTransformation(child, mChildTransformation); if (hasTransform) { int transformType = mChildTransformation.getTransformationType(); transformToApply = transformType != android.view.animation.Transformation.TYPE_IDENTITY ? mChildTransformation : null; concatMatrix = (transformType & android.view.animation.Transformation.TYPE_MATRIX ) != 0; } } } concatMatrix |= !childHasIdentityMatrix; // Sets the flag as early as possible to allow draw() implementations // to call invalidate() successfully when doing animations child.mPrivateFlags |= DRAWN; if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, android.graphics.Canvas.EdgeType .BW) && (child.mPrivateFlags & DRAW_ANIMATION) == 0) { return more; } float alpha = child.getAlpha(); // Bail out early if the view does not need to be drawn if (alpha <= android.view.ViewConfiguration.ALPHA_THRESHOLD && (child.mPrivateFlags & ALPHA_SET) == 0 && !(child is android.view.SurfaceView)) { return more; } if (hardwareAccelerated) { // Clear INVALIDATED flag to allow invalidation to occur during rendering, but // retain the flag's value temporarily in the mRecreateDisplayList flag child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED; child.mPrivateFlags &= ~INVALIDATED; } child.computeScroll(); int sx = child.mScrollX; int sy = child.mScrollY; android.view.DisplayList displayList = null; android.graphics.Bitmap cache = null; bool hasDisplayList = false; if (caching) { if (!hardwareAccelerated) { if (layerType != LAYER_TYPE_NONE) { layerType = LAYER_TYPE_SOFTWARE; child.buildDrawingCache(true); } cache = child.getDrawingCache(true); } else { switch (layerType) { case LAYER_TYPE_SOFTWARE: { child.buildDrawingCache(true); cache = child.getDrawingCache(true); break; } case LAYER_TYPE_NONE: { // Delay getting the display list until animation-driven alpha values are // set up and possibly passed on to the view hasDisplayList = child.canHaveDisplayList(); break; } } } } bool hasNoCache = cache == null || hasDisplayList; bool offsetForScroll = cache == null && !hasDisplayList && layerType != LAYER_TYPE_HARDWARE; int restoreTo = canvas.save(); if (offsetForScroll) { canvas.translate(cl - sx, ct - sy); } else { canvas.translate(cl, ct); if (scalingRequired) { // mAttachInfo cannot be null, otherwise scalingRequired == false float scale = 1.0f / mAttachInfo.mApplicationScale; canvas.scale(scale, scale); } } if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) { if (transformToApply != null || !childHasIdentityMatrix) { int transX = 0; int transY = 0; if (offsetForScroll) { transX = -sx; transY = -sy; } if (transformToApply != null) { if (concatMatrix) { // Undo the scroll translation, apply the transformation matrix, // then redo the scroll translate to get the correct result. canvas.translate(-transX, -transY); canvas.concat(transformToApply.getMatrix()); canvas.translate(transX, transY); mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; } float transformAlpha = transformToApply.getAlpha(); if (transformAlpha < 1.0f) { alpha *= transformToApply.getAlpha(); mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; } } if (!childHasIdentityMatrix) { canvas.translate(-transX, -transY); canvas.concat(child.getMatrix()); canvas.translate(transX, transY); } } if (alpha < 1.0f) { mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; if (hasNoCache) { int multipliedAlpha = (int)(255 * alpha); if (!child.onSetAlpha(multipliedAlpha)) { int layerFlags = android.graphics.Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN || layerType != LAYER_TYPE_NONE) { layerFlags |= android.graphics.Canvas.CLIP_TO_LAYER_SAVE_FLAG; } if (layerType == LAYER_TYPE_NONE) { int scrollX = hasDisplayList ? 0 : sx; int scrollY = hasDisplayList ? 0 : sy; canvas.saveLayerAlpha(scrollX, scrollY, scrollX + cr - cl, scrollY + cb - ct, multipliedAlpha , layerFlags); } } else { // Alpha is handled by the child directly, clobber the layer's alpha child.mPrivateFlags |= ALPHA_SET; } } } } else { if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) { child.onSetAlpha(255); child.mPrivateFlags &= ~ALPHA_SET; } } if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { if (offsetForScroll) { canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct)); } else { if (!scalingRequired || cache == null) { canvas.clipRect(0, 0, cr - cl, cb - ct); } else { canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight()); } } } if (hasDisplayList) { displayList = child.getDisplayList(); if (!displayList.isValid()) { // Uncommon, but possible. If a view is removed from the hierarchy during the call // to getDisplayList(), the display list will be marked invalid and we should not // try to use it again. displayList = null; hasDisplayList = false; } } if (hasNoCache) { bool layerRendered = false; if (layerType == LAYER_TYPE_HARDWARE) { android.view.HardwareLayer layer = child.getHardwareLayer(); if (layer != null && layer.isValid()) { child.mLayerPaint.setAlpha((int)(alpha * 255)); ((android.view.HardwareCanvas)canvas).drawHardwareLayer(layer, 0, 0, child.mLayerPaint ); layerRendered = true; } else { int scrollX = hasDisplayList ? 0 : sx; int scrollY = hasDisplayList ? 0 : sy; canvas.saveLayer(scrollX, scrollY, scrollX + cr - cl, scrollY + cb - ct, child.mLayerPaint , android.graphics.Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | android.graphics.Canvas.CLIP_TO_LAYER_SAVE_FLAG ); } } if (!layerRendered) { if (!hasDisplayList) { // Fast path for layouts with no backgrounds if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { child.mPrivateFlags &= ~DIRTY_MASK; child.dispatchDraw(canvas); } else { child.draw(canvas); } } else { child.mPrivateFlags &= ~DIRTY_MASK; ((android.view.HardwareCanvas)canvas).drawDisplayList(displayList, cr - cl, cb - ct, null); } } } else { if (cache != null) { child.mPrivateFlags &= ~DIRTY_MASK; android.graphics.Paint cachePaint; if (layerType == LAYER_TYPE_NONE) { cachePaint = mCachePaint; if (alpha < 1.0f) { cachePaint.setAlpha((int)(alpha * 255)); mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE; } else { if ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) { cachePaint.setAlpha(255); mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE; } } } else { cachePaint = child.mLayerPaint; cachePaint.setAlpha((int)(alpha * 255)); } canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint); } } canvas.restoreToCount(restoreTo); if (a != null && !more) { if (!hardwareAccelerated && !a.getFillAfter()) { child.onSetAlpha(255); } finishAnimatingView(child, a); } if (more && hardwareAccelerated) { // invalidation is the trigger to recreate display lists, so if we're using // display lists to render, force an invalidate to allow the animation to // continue drawing another frame invalidate(true); if (a.hasAlpha() && (child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) { // alpha animations should cause the child to recreate its display list child.invalidate(true); } } child.mRecreateDisplayList = false; return more; }
private void removeViewInternal(int index, android.view.View view) { if (mTransition != null) { mTransition.removeChild(this, view); } bool clearChildFocus_1 = false; if (view == mFocused) { view.clearFocusForRemoval(); clearChildFocus_1 = true; } if (view.getAnimation() != null || (mTransitioningViews != null && mTransitioningViews .contains(view))) { addDisappearingView(view); } else { if (view.mAttachInfo != null) { view.dispatchDetachedFromWindow(); } } onViewRemoved(view); needGlobalAttributesUpdate(false); removeFromArray(index); if (clearChildFocus_1) { clearChildFocus(view); } }
/// <summary>Returns true if a child view can receive pointer events.</summary> /// <remarks>Returns true if a child view can receive pointer events.</remarks> /// <hide></hide> private static bool canViewReceivePointerEvents(android.view.View child) { return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null; }