/// <summary> /// Scrolls the specified widget to the specified coordinates, except /// constrains the X scrolling position to the horizontal regions of /// the text that will be visible after scrolling to the specified /// Y position. /// </summary> /// <remarks> /// Scrolls the specified widget to the specified coordinates, except /// constrains the X scrolling position to the horizontal regions of /// the text that will be visible after scrolling to the specified /// Y position. /// </remarks> public static void scrollTo(android.widget.TextView widget, android.text.Layout layout , int x, int y) { int verticalPadding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom( ); int top = layout.getLineForVertical(y); int bottom = layout.getLineForVertical(y + widget.getHeight() - verticalPadding); int left = int.MaxValue; int right = 0; android.text.Layout.Alignment? a = layout.getParagraphAlignment(top); bool ltr = layout.getParagraphDirection(top) > 0; { for (int i = top; i <= bottom; i++) { left = (int)System.Math.Min(left, layout.getLineLeft(i)); right = (int)System.Math.Max(right, layout.getLineRight(i)); } } int hoizontalPadding = widget.getTotalPaddingLeft() + widget.getTotalPaddingRight (); int availableWidth = widget.getWidth() - hoizontalPadding; int actualWidth = right - left; if (actualWidth < availableWidth) { if (a == android.text.Layout.Alignment.ALIGN_CENTER) { x = left - ((availableWidth - actualWidth) / 2); } else { if ((ltr && (a == android.text.Layout.Alignment.ALIGN_OPPOSITE)) || (a == android.text.Layout.Alignment .ALIGN_RIGHT)) { // align_opposite does NOT mean align_right, we need the paragraph // direction to resolve it to left or right x = left - (availableWidth - actualWidth); } else { x = left; } } } else { x = System.Math.Min(x, right - availableWidth); x = System.Math.Max(x, left); } widget.scrollTo(x, y); }
/// <summary> /// Returns an immutable bitmap from subset of the source bitmap, /// transformed by the optional matrix. /// </summary> /// <remarks> /// Returns an immutable bitmap from subset of the source bitmap, /// transformed by the optional matrix. It is /// initialized with the same density as the original bitmap. /// </remarks> /// <param name="source">The bitmap we are subsetting</param> /// <param name="x">The x coordinate of the first pixel in source</param> /// <param name="y">The y coordinate of the first pixel in source</param> /// <param name="width">The number of pixels in each row</param> /// <param name="height">The number of rows</param> /// <param name="m">Optional matrix to be applied to the pixels</param> /// <param name="filter"> /// true if the source should be filtered. /// Only applies if the matrix contains more than just /// translation. /// </param> /// <returns>A bitmap that represents the specified subset of source</returns> /// <exception cref="System.ArgumentException"> /// if the x, y, width, height values are /// outside of the dimensions of the source bitmap. /// </exception> public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap source , int x, int y, int width, int height, android.graphics.Matrix m, bool filter) { checkXYSign(x, y); checkWidthHeight(width, height); if (x + width > source.getWidth()) { throw new System.ArgumentException("x + width must be <= bitmap.width()"); } if (y + height > source.getHeight()) { throw new System.ArgumentException("y + height must be <= bitmap.height()"); } // check if we can just return our argument unchanged if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() && height == source.getHeight() && (m == null || m.isIdentity())) { return source; } int neww = width; int newh = height; android.graphics.Canvas canvas = new android.graphics.Canvas(); android.graphics.Bitmap bitmap; android.graphics.Paint paint; android.graphics.Rect srcR = new android.graphics.Rect(x, y, x + width, y + height ); android.graphics.RectF dstR = new android.graphics.RectF(0, 0, width, height); android.graphics.Bitmap.Config newConfig = android.graphics.Bitmap.Config.ARGB_8888; android.graphics.Bitmap.Config config = source.getConfig(); // GIF files generate null configs, assume ARGB_8888 if (config != null) { switch (config) { case android.graphics.Bitmap.Config.RGB_565: { newConfig = android.graphics.Bitmap.Config.RGB_565; break; } case android.graphics.Bitmap.Config.ALPHA_8: { newConfig = android.graphics.Bitmap.Config.ALPHA_8; break; } case android.graphics.Bitmap.Config.ARGB_4444: case android.graphics.Bitmap.Config.ARGB_8888: default: { //noinspection deprecation newConfig = android.graphics.Bitmap.Config.ARGB_8888; break; } } } if (m == null || m.isIdentity()) { bitmap = createBitmap(neww, newh, newConfig, source.hasAlpha()); paint = null; } else { // not needed bool transformed = !m.rectStaysRect(); android.graphics.RectF deviceR = new android.graphics.RectF(); m.mapRect(deviceR, dstR); neww = Sharpen.Util.Round(deviceR.width()); newh = Sharpen.Util.Round(deviceR.height()); bitmap = createBitmap(neww, newh, transformed ? android.graphics.Bitmap.Config.ARGB_8888 : newConfig, transformed || source.hasAlpha()); canvas.translate(-deviceR.left, -deviceR.top); canvas.concat(m); paint = new android.graphics.Paint(); paint.setFilterBitmap(filter); if (transformed) { paint.setAntiAlias(true); } } // The new bitmap was created from a known bitmap source so assume that // they use the same density bitmap.mDensity = source.mDensity; canvas.setBitmap(bitmap); canvas.drawBitmap(source, srcR, dstR, paint); canvas.setBitmap(null); return bitmap; }
/// <summary>Returns an immutable bitmap from the source bitmap.</summary> /// <remarks> /// Returns an immutable bitmap from the source bitmap. The new bitmap may /// be the same object as source, or a copy may have been made. It is /// initialized with the same density as the original bitmap. /// </remarks> public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap src) { return createBitmap(src, 0, 0, src.getWidth(), src.getHeight()); }
/// <summary>Creates a new bitmap, scaled from an existing bitmap.</summary> /// <remarks>Creates a new bitmap, scaled from an existing bitmap.</remarks> /// <param name="src">The source bitmap.</param> /// <param name="dstWidth">The new bitmap's desired width.</param> /// <param name="dstHeight">The new bitmap's desired height.</param> /// <param name="filter">true if the source should be filtered.</param> /// <returns>the new scaled bitmap.</returns> public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap src, int dstWidth, int dstHeight, bool filter) { android.graphics.Matrix m; lock (typeof(android.graphics.Bitmap)) { // small pool of just 1 matrix m = sScaleMatrix; sScaleMatrix = null; } if (m == null) { m = new android.graphics.Matrix(); } int width = src.getWidth(); int height = src.getHeight(); float sx = dstWidth / (float)width; float sy = dstHeight / (float)height; m.setScale(sx, sy); android.graphics.Bitmap b = android.graphics.Bitmap.createBitmap(src, 0, 0, width , height, m, filter); lock (typeof(android.graphics.Bitmap)) { // do we need to check for null? why not just assign everytime? if (sScaleMatrix == null) { sScaleMatrix = m; } } return b; }
private static android.graphics.Bitmap finishDecode(android.graphics.Bitmap bm, android.graphics.Rect outPadding, android.graphics.BitmapFactory.Options opts) { if (bm == null || opts == null) { return bm; } int density = opts.inDensity; if (density == 0) { return bm; } bm.setDensity(density); int targetDensity = opts.inTargetDensity; if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) { return bm; } byte[] np = bm.getNinePatchChunk(); bool isNinePatch = np != null && android.graphics.NinePatch.isNinePatchChunk(np); if (opts.inScaled || isNinePatch) { float scale = targetDensity / (float)density; // TODO: This is very inefficient and should be done in native by Skia android.graphics.Bitmap oldBitmap = bm; bm = android.graphics.Bitmap.createScaledBitmap(oldBitmap, (int)(bm.getWidth() * scale + 0.5f), (int)(bm.getHeight() * scale + 0.5f), true); oldBitmap.recycle(); if (isNinePatch) { np = nativeScaleNinePatch(np, scale, outPadding); bm.setNinePatchChunk(np); } bm.setDensity(targetDensity); } return bm; }
/// <summary> /// Returns the maximum height that is available for the popup to be /// completely shown, optionally ignoring any bottom decorations such as /// the input method. /// </summary> /// <remarks> /// Returns the maximum height that is available for the popup to be /// completely shown, optionally ignoring any bottom decorations such as /// the input method. It is recommended that this height be the maximum for /// the popup's height, otherwise it is possible that the popup will be /// clipped. /// </remarks> /// <param name="anchor">The view on which the popup window must be anchored.</param> /// <param name="yOffset">y offset from the view's bottom edge</param> /// <param name="ignoreBottomDecorations"> /// if true, the height returned will be /// all the way to the bottom of the display, ignoring any /// bottom decorations /// </param> /// <returns> /// The maximum available height for the popup to be completely /// shown. /// </returns> /// <hide>Pending API council approval.</hide> public virtual int getMaxAvailableHeight(android.view.View anchor, int yOffset, bool ignoreBottomDecorations) { android.graphics.Rect displayFrame = new android.graphics.Rect(); anchor.getWindowVisibleDisplayFrame(displayFrame); int[] anchorPos = mDrawingLocation; anchor.getLocationOnScreen(anchorPos); int bottomEdge = displayFrame.bottom; if (ignoreBottomDecorations) { android.content.res.Resources res = anchor.getContext().getResources(); bottomEdge = res.getDisplayMetrics().heightPixels; } int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset; int distanceToTop = anchorPos[1] - displayFrame.top + yOffset; // anchorPos[1] is distance from anchor to top of screen int returnedHeight = System.Math.Max(distanceToBottom, distanceToTop); if (mBackground != null) { mBackground.getPadding(mTempRect); returnedHeight -= mTempRect.top + mTempRect.bottom; } return returnedHeight; }
/// <summary><p>Positions the popup window on screen.</summary> /// <remarks> /// <p>Positions the popup window on screen. When the popup window is too /// tall to fit under the anchor, a parent scroll view is seeked and scrolled /// up to reclaim space. If scrolling is not possible or not enough, the /// popup window gets moved on top of the anchor.</p> /// <p>The height must have been set on the layout parameters prior to /// calling this method.</p> /// </remarks> /// <param name="anchor">the view on which the popup window must be anchored</param> /// <param name="p">the layout parameters used to display the drop down</param> /// <returns>true if the popup is translated upwards to fit on screen</returns> private bool findDropDownPosition(android.view.View anchor, android.view.WindowManagerClass .LayoutParams p, int xoff, int yoff) { int anchorHeight = anchor.getHeight(); anchor.getLocationInWindow(mDrawingLocation); p.x = mDrawingLocation[0] + xoff; p.y = mDrawingLocation[1] + anchorHeight + yoff; bool onTop = false; p.gravity = android.view.Gravity.LEFT | android.view.Gravity.TOP; anchor.getLocationOnScreen(mScreenLocation); android.graphics.Rect displayFrame = new android.graphics.Rect(); anchor.getWindowVisibleDisplayFrame(displayFrame); int screenY = mScreenLocation[1] + anchorHeight + yoff; android.view.View root = anchor.getRootView(); if (screenY + mPopupHeight > displayFrame.bottom || p.x + mPopupWidth - root.getWidth () > 0) { // if the drop down disappears at the bottom of the screen. we try to // scroll a parent scrollview or move the drop down back up on top of // the edit box if (mAllowScrollingAnchorParent) { int scrollX = anchor.getScrollX(); int scrollY = anchor.getScrollY(); android.graphics.Rect r = new android.graphics.Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff, scrollY + mPopupHeight + anchor.getHeight() + yoff); anchor.requestRectangleOnScreen(r, true); } // now we re-evaluate the space available, and decide from that // whether the pop-up will go above or below the anchor. anchor.getLocationInWindow(mDrawingLocation); p.x = mDrawingLocation[0] + xoff; p.y = mDrawingLocation[1] + anchor.getHeight() + yoff; // determine whether there is more space above or below the anchor anchor.getLocationOnScreen(mScreenLocation); onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getHeight() - yoff) < (mScreenLocation[1] - yoff - displayFrame.top); if (onTop) { p.gravity = android.view.Gravity.LEFT | android.view.Gravity.BOTTOM; p.y = root.getHeight() - mDrawingLocation[1] + yoff; } else { p.y = mDrawingLocation[1] + anchor.getHeight() + yoff; } } if (mClipToScreen) { int displayFrameWidth = displayFrame.right - displayFrame.left; int right = p.x + p.width; if (right > displayFrameWidth) { p.x -= right - displayFrameWidth; } if (p.x < displayFrame.left) { p.x = displayFrame.left; p.width = System.Math.Min(p.width, displayFrameWidth); } if (onTop) { int popupTop = mScreenLocation[1] + yoff - mPopupHeight; if (popupTop < 0) { p.y += popupTop; } } else { p.y = System.Math.Max(p.y, displayFrame.top); } } p.gravity |= android.view.Gravity.DISPLAY_CLIP_VERTICAL; return onTop; }
/// <summary>Draw the picture, stretched to fit into the dst rectangle.</summary> /// <remarks>Draw the picture, stretched to fit into the dst rectangle.</remarks> public virtual void drawPicture(android.graphics.Picture picture, android.graphics.Rect dst) { save(); translate(dst.left, dst.top); if (picture.getWidth() > 0 && picture.getHeight() > 0) { scale((float)dst.width() / picture.getWidth(), (float)dst.height() / picture.getHeight ()); } drawPicture(picture); restore(); }
/// <summary>Figure out vertical placement based on mGravity</summary> /// <param name="child">Child to place</param> /// <returns>Where the top of the child should be</returns> private int calculateTop(android.view.View child, bool duringLayout) { int myHeight = duringLayout ? getMeasuredHeight() : getHeight(); int childHeight = duringLayout ? child.getMeasuredHeight() : child.getHeight(); int childTop = 0; switch (mGravity) { case android.view.Gravity.TOP: { childTop = mSpinnerPadding.top; break; } case android.view.Gravity.CENTER_VERTICAL: { int availableSpace = myHeight - mSpinnerPadding.bottom - mSpinnerPadding.top - childHeight; childTop = mSpinnerPadding.top + (availableSpace / 2); break; } case android.view.Gravity.BOTTOM: { childTop = myHeight - mSpinnerPadding.bottom - childHeight; break; } } return childTop; }
private int getInnerHeight(android.widget.TextView widget) { return widget.getHeight() - widget.getTotalPaddingTop() - widget.getTotalPaddingBottom (); }