Exemplo n.º 1
0
		/// <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;
		}
Exemplo n.º 2
0
		/// <summary>
		/// Returns true if a child view contains the specified point when transformed
		/// into its coordinate space.
		/// </summary>
		/// <remarks>
		/// Returns true if a child view contains the specified point when transformed
		/// into its coordinate space.
		/// Child must not be null.
		/// </remarks>
		/// <hide></hide>
		protected internal virtual bool isTransformedTouchPointInView(float x, float y, android.view.View
			 child, android.graphics.PointF outLocalPoint)
		{
			float localX = x + mScrollX - child.mLeft;
			float localY = y + mScrollY - child.mTop;
			if (!child.hasIdentityMatrix() && mAttachInfo != null)
			{
				float[] localXY = mAttachInfo.mTmpTransformLocation;
				localXY[0] = localX;
				localXY[1] = localY;
				child.getInverseMatrix().mapPoints(localXY);
				localX = localXY[0];
				localY = localXY[1];
			}
			bool isInView = child.pointInView(localX, localY);
			if (isInView && outLocalPoint != null)
			{
				outLocalPoint.set(localX, localY);
			}
			return isInView;
		}
Exemplo n.º 3
0
		/// <summary>
		/// Transforms a motion event into the coordinate space of a particular child view,
		/// filters out irrelevant pointer ids, and overrides its action if necessary.
		/// </summary>
		/// <remarks>
		/// Transforms a motion event into the coordinate space of a particular child view,
		/// filters out irrelevant pointer ids, and overrides its action if necessary.
		/// If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
		/// </remarks>
		private bool dispatchTransformedTouchEvent(android.view.MotionEvent @event, bool 
			cancel, android.view.View child, int desiredPointerIdBits)
		{
			bool handled;
			// Canceling motions is a special case.  We don't need to perform any transformations
			// or filtering.  The important part is the action, not the contents.
			int oldAction = @event.getAction();
			if (cancel || oldAction == android.view.MotionEvent.ACTION_CANCEL)
			{
				@event.setAction(android.view.MotionEvent.ACTION_CANCEL);
				if (child == null)
				{
					handled = base.dispatchTouchEvent(@event);
				}
				else
				{
					handled = child.dispatchTouchEvent(@event);
				}
				@event.setAction(oldAction);
				return handled;
			}
			// Calculate the number of pointers to deliver.
			int oldPointerIdBits = @event.getPointerIdBits();
			int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
			// If for some reason we ended up in an inconsistent state where it looks like we
			// might produce a motion event with no pointers in it, then drop the event.
			if (newPointerIdBits == 0)
			{
				return false;
			}
			// If the number of pointers is the same and we don't need to perform any fancy
			// irreversible transformations, then we can reuse the motion event for this
			// dispatch as long as we are careful to revert any changes we make.
			// Otherwise we need to make a copy.
			android.view.MotionEvent transformedEvent;
			if (newPointerIdBits == oldPointerIdBits)
			{
				if (child == null || child.hasIdentityMatrix())
				{
					if (child == null)
					{
						handled = base.dispatchTouchEvent(@event);
					}
					else
					{
						float offsetX = mScrollX - child.mLeft;
						float offsetY = mScrollY - child.mTop;
						@event.offsetLocation(offsetX, offsetY);
						handled = child.dispatchTouchEvent(@event);
						@event.offsetLocation(-offsetX, -offsetY);
					}
					return handled;
				}
				transformedEvent = android.view.MotionEvent.obtain(@event);
			}
			else
			{
				transformedEvent = @event.split(newPointerIdBits);
			}
			// Perform any necessary transformations and dispatch.
			if (child == null)
			{
				handled = base.dispatchTouchEvent(transformedEvent);
			}
			else
			{
				float offsetX = mScrollX - child.mLeft;
				float offsetY = mScrollY - child.mTop;
				transformedEvent.offsetLocation(offsetX, offsetY);
				if (!child.hasIdentityMatrix())
				{
					transformedEvent.transform(child.getInverseMatrix());
				}
				handled = child.dispatchTouchEvent(transformedEvent);
			}
			// Done.
			transformedEvent.recycle();
			return handled;
		}
Exemplo n.º 4
0
		/// <summary>
		/// Dispatches a generic pointer event to a child, taking into account
		/// transformations that apply to the child.
		/// </summary>
		/// <remarks>
		/// Dispatches a generic pointer event to a child, taking into account
		/// transformations that apply to the child.
		/// </remarks>
		/// <param name="event">The event to send.</param>
		/// <param name="child">The view to send the event to.</param>
		/// <returns>
		/// 
		/// <code>true</code>
		/// if the child handled the event.
		/// </returns>
		private bool dispatchTransformedGenericPointerEvent(android.view.MotionEvent @event
			, android.view.View child)
		{
			float offsetX = mScrollX - child.mLeft;
			float offsetY = mScrollY - child.mTop;
			bool handled;
			if (!child.hasIdentityMatrix())
			{
				android.view.MotionEvent transformedEvent = android.view.MotionEvent.obtain(@event
					);
				transformedEvent.offsetLocation(offsetX, offsetY);
				transformedEvent.transform(child.getInverseMatrix());
				handled = child.dispatchGenericMotionEvent(transformedEvent);
				transformedEvent.recycle();
			}
			else
			{
				@event.offsetLocation(offsetX, offsetY);
				handled = child.dispatchGenericMotionEvent(@event);
				@event.offsetLocation(-offsetX, -offsetY);
			}
			return handled;
		}