Example usage for android.graphics.drawable Drawable draw

List of usage examples for android.graphics.drawable Drawable draw

Introduction

In this page you can find the example usage for android.graphics.drawable Drawable draw.

Prototype

public abstract void draw(@NonNull Canvas canvas);

Source Link

Document

Draw in its bounds (set via setBounds) respecting optional effects such as alpha (set via setAlpha) and color filter (set via setColorFilter).

Usage

From source file:com.appunite.list.HorizontalListView.java

void drawOverscrollHeader(Canvas canvas, Drawable drawable, Rect bounds) {
    final int width = drawable.getMinimumWidth();

    canvas.save();/*from   w w w .j  av a2 s .  c o  m*/
    canvas.clipRect(bounds);

    final int span = bounds.right - bounds.left;
    if (span < width) {
        bounds.left = bounds.right - width;
    }

    drawable.setBounds(bounds);
    drawable.draw(canvas);

    canvas.restore();
}

From source file:com.appunite.list.HorizontalListView.java

void drawOverscrollFooter(Canvas canvas, Drawable drawable, Rect bounds) {
    final int width = drawable.getMinimumWidth();

    canvas.save();/*w  w  w  . j av a 2  s.  co m*/
    canvas.clipRect(bounds);

    final int span = bounds.right - bounds.left;
    if (span < width) {
        bounds.right = bounds.left + width;
    }

    drawable.setBounds(bounds);
    drawable.draw(canvas);

    canvas.restore();
}

From source file:com.android.launcher3.Utilities.java

/**
 * @param scale the scale to apply before drawing {@param icon} on the canvas
 *///from  ww w  .ja v a2 s.  c om
public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
    synchronized (sCanvas) {
        final int iconBitmapSize = getIconBitmapSize();

        int width = iconBitmapSize;
        int height = iconBitmapSize;

        if (icon instanceof PaintDrawable) {
            PaintDrawable painter = (PaintDrawable) icon;
            painter.setIntrinsicWidth(width);
            painter.setIntrinsicHeight(height);
        } else if (icon instanceof BitmapDrawable) {
            // Ensure the bitmap has a density.
            BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
            Bitmap bitmap = bitmapDrawable.getBitmap();
            if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) {
                bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
            }
        }
        int sourceWidth = icon.getIntrinsicWidth();
        int sourceHeight = icon.getIntrinsicHeight();
        if (sourceWidth > 0 && sourceHeight > 0) {
            // Scale the icon proportionally to the icon dimensions
            final float ratio = (float) sourceWidth / sourceHeight;
            if (sourceWidth > sourceHeight) {
                height = (int) (width / ratio);
            } else if (sourceHeight > sourceWidth) {
                width = (int) (height * ratio);
            }
        }

        // no intrinsic size --> use default size
        int textureWidth = iconBitmapSize;
        int textureHeight = iconBitmapSize;

        final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, Bitmap.Config.ARGB_8888);
        final Canvas canvas = sCanvas;
        canvas.setBitmap(bitmap);

        final int left = (textureWidth - width) / 2;
        final int top = (textureHeight - height) / 2;

        @SuppressWarnings("all") // suppress dead code warning
        final boolean debug = false;
        if (debug) {
            // draw a big box for the icon for debugging
            canvas.drawColor(sColors[sColorIndex]);
            if (++sColorIndex >= sColors.length)
                sColorIndex = 0;
            Paint debugPaint = new Paint();
            debugPaint.setColor(0xffcccc00);
            canvas.drawRect(left, top, left + width, top + height, debugPaint);
        }

        sOldBounds.set(icon.getBounds());
        icon.setBounds(left, top, left + width, top + height);
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
        icon.draw(canvas);
        canvas.restore();
        icon.setBounds(sOldBounds);
        canvas.setBitmap(null);

        return bitmap;
    }
}

From source file:com.appunite.list.ListView.java

void drawOverscrollHeader(Canvas canvas, Drawable drawable, Rect bounds) {
    final int height = drawable.getMinimumHeight();

    canvas.save();//from www  . j  a  v  a 2 s  . co m
    canvas.clipRect(bounds);

    final int span = bounds.bottom - bounds.top;
    if (span < height) {
        bounds.top = bounds.bottom - height;
    }

    drawable.setBounds(bounds);
    drawable.draw(canvas);

    canvas.restore();
}

From source file:com.appunite.list.ListView.java

void drawOverscrollFooter(Canvas canvas, Drawable drawable, Rect bounds) {
    final int height = drawable.getMinimumHeight();

    canvas.save();//from  w  w w.  j a  va2s  . c  o m
    canvas.clipRect(bounds);

    final int span = bounds.bottom - bounds.top;
    if (span < height) {
        bounds.bottom = bounds.top + height;
    }

    drawable.setBounds(bounds);
    drawable.draw(canvas);

    canvas.restore();
}

From source file:com.android.launcher3.CellLayout.java

@Override
protected void onDraw(Canvas canvas) {
    if (!mIsDragTarget) {
        return;/*from  w  w  w . j  av a  2s  .  com*/
    }

    // When we're large, we are either drawn in a "hover" state (ie when dragging an item to
    // a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f)
    // When we're small, we are either drawn normally or in the "accepts drops" state (during
    // a drag). However, we also drag the mini hover background *over* one of those two
    // backgrounds
    if (mBackgroundAlpha > 0.0f) {
        mBackground.draw(canvas);
    }

    final Paint paint = mDragOutlinePaint;
    for (int i = 0; i < mDragOutlines.length; i++) {
        final float alpha = mDragOutlineAlphas[i];
        if (alpha > 0) {
            final Rect r = mDragOutlines[i];
            mTempRect.set(r);
            Utilities.scaleRectAboutCenter(mTempRect, getChildrenScale());
            final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag();
            paint.setAlpha((int) (alpha + .5f));
            canvas.drawBitmap(b, null, mTempRect, paint);
        }
    }

    if (DEBUG_VISUALIZE_OCCUPIED) {
        int[] pt = new int[2];
        ColorDrawable cd = new ColorDrawable(Color.RED);
        cd.setBounds(0, 0, mCellWidth, mCellHeight);
        for (int i = 0; i < mCountX; i++) {
            for (int j = 0; j < mCountY; j++) {
                if (mOccupied[i][j]) {
                    cellToPoint(i, j, pt);
                    canvas.save();
                    canvas.translate(pt[0], pt[1]);
                    cd.draw(canvas);
                    canvas.restore();
                }
            }
        }
    }

    int previewOffset = FolderRingAnimator.sPreviewSize;

    // The folder outer / inner ring image(s)
    DeviceProfile grid = mLauncher.getDeviceProfile();
    for (int i = 0; i < mFolderOuterRings.size(); i++) {
        FolderRingAnimator fra = mFolderOuterRings.get(i);

        Drawable d;
        int width, height;
        cellToPoint(fra.mCellX, fra.mCellY, mTempLocation);
        View child = getChildAt(fra.mCellX, fra.mCellY);

        if (child != null) {
            int centerX = mTempLocation[0] + mCellWidth / 2;
            int centerY = mTempLocation[1] + previewOffset / 2 + child.getPaddingTop()
                    + grid.folderBackgroundOffset;

            // Draw outer ring, if it exists
            if (FolderIcon.HAS_OUTER_RING) {
                d = FolderRingAnimator.sSharedOuterRingDrawable;
                width = (int) (fra.getOuterRingSize() * getChildrenScale());
                height = width;
                canvas.save();
                canvas.translate(centerX - width / 2, centerY - height / 2);
                d.setBounds(0, 0, width, height);
                d.draw(canvas);
                canvas.restore();
            }

            // Draw inner ring
            d = FolderRingAnimator.sSharedInnerRingDrawable;
            width = (int) (fra.getInnerRingSize() * getChildrenScale());
            height = width;
            canvas.save();
            canvas.translate(centerX - width / 2, centerY - width / 2);
            d.setBounds(0, 0, width, height);
            d.draw(canvas);
            canvas.restore();
        }
    }

    if (mFolderLeaveBehindCell[0] >= 0 && mFolderLeaveBehindCell[1] >= 0) {
        Drawable d = FolderIcon.sSharedFolderLeaveBehind;
        int width = d.getIntrinsicWidth();
        int height = d.getIntrinsicHeight();

        cellToPoint(mFolderLeaveBehindCell[0], mFolderLeaveBehindCell[1], mTempLocation);
        View child = getChildAt(mFolderLeaveBehindCell[0], mFolderLeaveBehindCell[1]);
        if (child != null) {
            int centerX = mTempLocation[0] + mCellWidth / 2;
            int centerY = mTempLocation[1] + previewOffset / 2 + child.getPaddingTop()
                    + grid.folderBackgroundOffset;

            canvas.save();
            canvas.translate(centerX - width / 2, centerY - width / 2);
            d.setBounds(0, 0, width, height);
            d.draw(canvas);
            canvas.restore();
        }
    }
}

From source file:com.lovely3x.eavlibrary.EasyAdapterView.java

void drawDivider(Canvas canvas, Rect bounds) {
    // This widget draws the same divider for all children
    final Drawable divider = mDivider;
    divider.setBounds(bounds);/*from   w  w w  .  ja  v a 2  s.c o  m*/
    divider.draw(canvas);
}

From source file:com.anysoftkeyboard.keyboards.views.AnyKeyboardViewBase.java

@CallSuper
protected void onBufferDraw(Canvas canvas, final Paint paint) {
    if (mKeyboardChanged) {
        invalidateAllKeys();// www.j  a v  a  2 s . co m
        mKeyboardChanged = false;
    }

    canvas.getClipBounds(mDirtyRect);

    if (mKeyboard == null)
        return;

    final boolean drawKeyboardNameText = (mKeyboardNameTextSize > 1f)
            && AnyApplication.getConfig().getShowKeyboardNameText();

    final boolean drawHintText = (mHintTextSize > 1) && AnyApplication.getConfig().getShowHintTextOnKeys();

    final boolean useCustomKeyTextColor = false;
    // TODO: final boolean useCustomKeyTextColor =
    // AnyApplication.getConfig().getUseCustomTextColorOnKeys();
    final ColorStateList keyTextColor = useCustomKeyTextColor
            ? new ColorStateList(new int[][] { { 0 } }, new int[] { 0xFF6666FF })
            : mKeyTextColor;

    final boolean useCustomHintColor = drawHintText && false;
    // TODO: final boolean useCustomHintColor = drawHintText &&
    final ColorStateList hintColor = useCustomHintColor
            ? new ColorStateList(new int[][] { { 0 } }, new int[] { 0xFFFF6666 })
            : mHintTextColor;

    // allow preferences to override theme settings for hint text position
    final boolean useCustomHintAlign = drawHintText && AnyApplication.getConfig().getUseCustomHintAlign();
    final int hintAlign = useCustomHintAlign ? AnyApplication.getConfig().getCustomHintAlign()
            : mHintLabelAlign;
    final int hintVAlign = useCustomHintAlign ? AnyApplication.getConfig().getCustomHintVAlign()
            : mHintLabelVAlign;

    final Drawable keyBackground = mKeyBackground;
    final Rect clipRegion = mClipRegion;
    final int kbdPaddingLeft = getPaddingLeft();
    final int kbdPaddingTop = getPaddingTop();
    final Key[] keys = mKeys;
    final Key invalidKey = mInvalidatedKey;

    boolean drawSingleKey = false;
    if (invalidKey != null && canvas.getClipBounds(clipRegion)) {
        // TODO we should use Rect.inset and Rect.contains here.
        // Is clipRegion completely contained within the invalidated key?
        if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left
                && invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top
                && invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right
                && invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) {
            drawSingleKey = true;
        }
    }

    for (Key keyBase : keys) {
        final AnyKey key = (AnyKey) keyBase;
        final boolean keyIsSpace = isSpaceKey(key);

        if (drawSingleKey && (invalidKey != key)) {
            continue;
        }
        if (!mDirtyRect.intersects(key.x + kbdPaddingLeft, key.y + kbdPaddingTop,
                key.x + key.width + kbdPaddingLeft, key.y + key.height + kbdPaddingTop)) {
            continue;
        }
        int[] drawableState = key.getCurrentDrawableState(mDrawableStatesProvider);

        if (keyIsSpace)
            paint.setColor(mKeyboardNameTextColor);
        else
            paint.setColor(keyTextColor.getColorForState(drawableState, 0xFF000000));
        keyBackground.setState(drawableState);

        // Switch the character to uppercase if shift is pressed
        CharSequence label = key.label == null ? null : adjustLabelToShiftState(key);

        final Rect bounds = keyBackground.getBounds();
        if ((key.width != bounds.right) || (key.height != bounds.bottom)) {
            keyBackground.setBounds(0, 0, key.width, key.height);
        }
        canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop);
        keyBackground.draw(canvas);

        if (TextUtils.isEmpty(label)) {
            Drawable iconToDraw = getIconToDrawForKey(key, false);
            if (iconToDraw != null/* && shouldDrawIcon */) {
                //http://developer.android.com/reference/android/graphics/drawable/Drawable.html#getCurrent()
                //http://stackoverflow.com/a/103600/1324235
                final boolean is9Patch = iconToDraw.getCurrent() instanceof NinePatchDrawable;

                // Special handing for the upper-right number hint icons
                final int drawableWidth;
                final int drawableHeight;
                final int drawableX;
                final int drawableY;

                drawableWidth = is9Patch ? key.width : iconToDraw.getIntrinsicWidth();
                drawableHeight = is9Patch ? key.height : iconToDraw.getIntrinsicHeight();
                drawableX = (key.width + mKeyBackgroundPadding.left - mKeyBackgroundPadding.right
                        - drawableWidth) / 2;
                drawableY = (key.height + mKeyBackgroundPadding.top - mKeyBackgroundPadding.bottom
                        - drawableHeight) / 2;

                canvas.translate(drawableX, drawableY);
                iconToDraw.setBounds(0, 0, drawableWidth, drawableHeight);
                iconToDraw.draw(canvas);
                canvas.translate(-drawableX, -drawableY);
                if (keyIsSpace && drawKeyboardNameText) {
                    // now a little hack, I'll set the label now, so it get
                    // drawn.
                    label = mKeyboardName;
                }
            } else {
                // ho... no icon.
                // I'll try to guess the text
                label = guessLabelForKey(key.getPrimaryCode());
            }
        }

        if (label != null) {
            // For characters, use large font. For labels like "Done", use
            // small font.
            final FontMetrics fm;
            if (keyIsSpace) {
                paint.setTextSize(mKeyboardNameTextSize);
                paint.setTypeface(Typeface.DEFAULT_BOLD);
                if (mKeyboardNameFontMetrics == null)
                    mKeyboardNameFontMetrics = paint.getFontMetrics();
                fm = mKeyboardNameFontMetrics;
            } else if (label.length() > 1 && key.getCodesCount() < 2) {
                setPaintForLabelText(paint);
                if (mLabelFontMetrics == null)
                    mLabelFontMetrics = paint.getFontMetrics();
                fm = mLabelFontMetrics;
            } else {
                setPaintToKeyText(paint);
                if (mTextFontMetrics == null)
                    mTextFontMetrics = paint.getFontMetrics();
                fm = mTextFontMetrics;
            }

            if (isLabelOfPictographic(label)) {
                paint.setTextSize(2f * paint.getTextSize());
            }

            final float labelHeight = -fm.top;
            // Draw a drop shadow for the text
            paint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mShadowColor);

            final float textWidth = adjustTextSizeForLabel(paint, label, key.width);

            // the center of the drawable space, which is value used
            // previously for vertically
            // positioning the key label
            final float centerY = mKeyBackgroundPadding.top
                    + ((key.height - mKeyBackgroundPadding.top - mKeyBackgroundPadding.bottom)
                            / (keyIsSpace ? 3 : 2));// the label on the space is a bit higher

            // the X coordinate for the center of the main label text is
            // unaffected by the hints
            final float textX = mKeyBackgroundPadding.left
                    + (key.width - mKeyBackgroundPadding.left - mKeyBackgroundPadding.right) / 2;
            final float textY;
            // Some devices (mostly pre-Honeycomb, have issues with RTL text
            // drawing.
            // Of course, there is no issue with a single character :)
            // so, we'll use the RTL secured drawing (via StaticLayout) for
            // labels.
            if (label.length() > 1 && !AnyApplication.getConfig().workaround_alwaysUseDrawText()) {
                // calculate Y coordinate of top of text based on center
                // location
                textY = centerY - ((labelHeight - paint.descent()) / 2);
                canvas.translate(textX, textY);
                // RTL fix. But it costs, let do it when in need (more than
                // 1 character)
                StaticLayout labelText = new StaticLayout(label, new TextPaint(paint), (int) textWidth,
                        Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
                labelText.draw(canvas);
            } else {
                // to get Y coordinate of baseline from center of text,
                // first add half the height (to get to
                // bottom of text), then subtract the part below the
                // baseline. Note that fm.top is negative.
                textY = centerY + ((labelHeight - paint.descent()) / 2);
                canvas.translate(textX, textY);
                canvas.drawText(label, 0, label.length(), 0, 0, paint);
            }
            canvas.translate(-textX, -textY);
            // (-)

            // Turn off drop shadow
            paint.setShadowLayer(0, 0, 0, 0);
        }

        if (drawHintText) {
            if ((key.popupCharacters != null && key.popupCharacters.length() > 0) || (key.popupResId != 0)
                    || (key.longPressCode != 0)) {
                Align oldAlign = paint.getTextAlign();

                String hintText = null;

                if (key.hintLabel != null && key.hintLabel.length() > 0) {
                    hintText = key.hintLabel.toString();
                    // it is the responsibility of the keyboard layout
                    // designer to ensure that they do
                    // not put too many characters in the hint label...
                } else if (key.longPressCode != 0) {
                    if (Character.isLetterOrDigit(key.longPressCode))
                        hintText = Character.toString((char) key.longPressCode);
                } else if (key.popupCharacters != null) {
                    final String hintString = key.popupCharacters.toString();
                    final int hintLength = hintString.length();
                    if (hintLength <= 3)
                        hintText = hintString;
                }

                // if hintText is still null, it means it didn't fit one of
                // the above
                // cases, so we should provide the hint using the default
                if (hintText == null) {
                    if (mHintOverflowLabel != null)
                        hintText = mHintOverflowLabel;
                    else {
                        // theme does not provide a defaultHintLabel
                        // use  if hints are above, ... if hints are
                        // below
                        // (to avoid being too close to main label/icon)
                        if (hintVAlign == Gravity.TOP)
                            hintText = "";
                        else
                            hintText = "...";
                    }
                }

                if (mKeyboard.isShifted())
                    hintText = hintText.toUpperCase(getKeyboard().getLocale());

                // now draw hint
                paint.setTypeface(Typeface.DEFAULT);
                paint.setColor(hintColor.getColorForState(drawableState, 0xFF000000));
                paint.setTextSize(mHintTextSize);
                // get the hint text font metrics so that we know the size
                // of the hint when
                // we try to position the main label (to try to make sure
                // they don't overlap)
                if (mHintTextFontMetrics == null) {
                    mHintTextFontMetrics = paint.getFontMetrics();
                }

                final float hintX;
                final float hintY;

                // the (float) 0.5 value is added or subtracted to just give
                // a little more room
                // in case the theme designer didn't account for the hint
                // label location
                if (hintAlign == Gravity.START) {
                    // left
                    paint.setTextAlign(Align.LEFT);
                    hintX = mKeyBackgroundPadding.left + 0.5f;
                } else if (hintAlign == Gravity.CENTER) {
                    // center
                    paint.setTextAlign(Align.CENTER);
                    hintX = mKeyBackgroundPadding.left
                            + (key.width - mKeyBackgroundPadding.left - mKeyBackgroundPadding.right) / 2;
                } else {
                    // right
                    paint.setTextAlign(Align.RIGHT);
                    hintX = key.width - mKeyBackgroundPadding.right - 0.5f;
                }

                if (hintVAlign == Gravity.TOP) {
                    // above
                    hintY = mKeyBackgroundPadding.top - mHintTextFontMetrics.top + 0.5f;
                } else {
                    // below
                    hintY = key.height - mKeyBackgroundPadding.bottom - mHintTextFontMetrics.bottom - 0.5f;
                }

                canvas.drawText(hintText, hintX, hintY, paint);
                paint.setTextAlign(oldAlign);
            }
        }

        canvas.translate(-key.x - kbdPaddingLeft, -key.y - kbdPaddingTop);
    }
    mInvalidatedKey = null;

    mDirtyRect.setEmpty();
}

From source file:com.android.launcher3.Workspace.java

/**
 * Draw the View v into the given Canvas.
 *
 * @param v the view to draw/*from  w  w  w . j  av  a  2  s .  c  om*/
 * @param destCanvas the canvas to draw on
 * @param padding the horizontal and vertical padding to use when drawing
 */
private static void drawDragView(View v, Canvas destCanvas, int padding) {
    final Rect clipRect = sTempRect;
    v.getDrawingRect(clipRect);

    boolean textVisible = false;

    destCanvas.save();
    if (v instanceof TextView) {
        Drawable d = ((TextView) v).getCompoundDrawables()[1];
        Rect bounds = getDrawableBounds(d);
        clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
        destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top);
        d.draw(destCanvas);
    } else {
        if (v instanceof FolderIcon) {
            // For FolderIcons the text can bleed into the icon area, and so we need to
            // hide the text completely (which can't be achieved by clipping).
            if (((FolderIcon) v).getTextVisible()) {
                ((FolderIcon) v).setTextVisible(false);
                textVisible = true;
            }
        }
        destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
        destCanvas.clipRect(clipRect, Op.REPLACE);
        v.draw(destCanvas);

        // Restore text visibility of FolderIcon if necessary
        if (textVisible) {
            ((FolderIcon) v).setTextVisible(true);
        }
    }
    destCanvas.restore();
}

From source file:com.yek.keyboard.keyboards.views.AnyKeyboardViewBase.java

@CallSuper
protected void onBufferDraw(Canvas canvas, final Paint paint) {
    if (mKeyboardChanged) {
        invalidateAllKeys();// ww  w.  ja v  a 2 s.c  o m
        mKeyboardChanged = false;
    }

    canvas.getClipBounds(mDirtyRect);

    if (mKeyboard == null)
        return;

    final boolean drawKeyboardNameText = (mKeyboardNameTextSize > 1f)
            && AnyApplication.getConfig().getShowKeyboardNameText();

    final boolean drawHintText = (mHintTextSize > 1) && AnyApplication.getConfig().getShowHintTextOnKeys();

    final boolean useCustomKeyTextColor = false;
    // TODO: final boolean useCustomKeyTextColor =
    // AnyApplication.getConfig().getUseCustomTextColorOnKeys();
    final ColorStateList keyTextColor = useCustomKeyTextColor
            ? new ColorStateList(new int[][] { { 0 } }, new int[] { 0xFF6666FF })
            : mKeyTextColor;

    final boolean useCustomHintColor = drawHintText && false;
    // TODO: final boolean useCustomHintColor = drawHintText &&
    final ColorStateList hintColor = useCustomHintColor
            ? new ColorStateList(new int[][] { { 0 } }, new int[] { 0xFFFF6666 })
            : mHintTextColor;

    // allow preferences to override theme settings for hint text position
    final boolean useCustomHintAlign = drawHintText && AnyApplication.getConfig().getUseCustomHintAlign();
    final int hintAlign = useCustomHintAlign ? AnyApplication.getConfig().getCustomHintAlign()
            : mHintLabelAlign;
    final int hintVAlign = useCustomHintAlign ? AnyApplication.getConfig().getCustomHintVAlign()
            : mHintLabelVAlign;

    final Drawable keyBackground = mKeyBackground;
    final Rect clipRegion = mClipRegion;
    final int kbdPaddingLeft = getPaddingLeft();
    final int kbdPaddingTop = getPaddingTop();
    final Keyboard.Key[] keys = mKeys;
    final Keyboard.Key invalidKey = mInvalidatedKey;

    boolean drawSingleKey = false;
    if (invalidKey != null && canvas.getClipBounds(clipRegion)) {
        // TODO we should use Rect.inset and Rect.contains here.
        // Is clipRegion completely contained within the invalidated key?
        if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left
                && invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top
                && invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right
                && invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) {
            drawSingleKey = true;
        }
    }

    for (Keyboard.Key keyBase : keys) {
        final AnyKeyboard.AnyKey key = (AnyKeyboard.AnyKey) keyBase;
        final boolean keyIsSpace = isSpaceKey(key);

        if (drawSingleKey && (invalidKey != key)) {
            continue;
        }
        if (!mDirtyRect.intersects(key.x + kbdPaddingLeft, key.y + kbdPaddingTop,
                key.x + key.width + kbdPaddingLeft, key.y + key.height + kbdPaddingTop)) {
            continue;
        }
        int[] drawableState = key.getCurrentDrawableState(mDrawableStatesProvider);

        if (keyIsSpace)
            paint.setColor(mKeyboardNameTextColor);
        else
            paint.setColor(keyTextColor.getColorForState(drawableState, 0xFF000000));
        keyBackground.setState(drawableState);

        // Switch the character to uppercase if shift is pressed
        CharSequence label = key.label == null ? null : adjustLabelToShiftState(key);

        final Rect bounds = keyBackground.getBounds();
        if ((key.width != bounds.right) || (key.height != bounds.bottom)) {
            keyBackground.setBounds(0, 0, key.width, key.height);
        }
        canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop);
        keyBackground.draw(canvas);

        if (TextUtils.isEmpty(label)) {
            Drawable iconToDraw = getIconToDrawForKey(key, false);
            if (iconToDraw != null/* && shouldDrawIcon */) {
                //http://developer.android.com/reference/android/graphics/drawable/Drawable.html#getCurrent()
                //http://stackoverflow.com/a/103600/1324235
                final boolean is9Patch = iconToDraw.getCurrent() instanceof NinePatchDrawable;

                // Special handing for the upper-right number hint icons
                final int drawableWidth;
                final int drawableHeight;
                final int drawableX;
                final int drawableY;

                drawableWidth = is9Patch ? key.width : iconToDraw.getIntrinsicWidth();
                drawableHeight = is9Patch ? key.height : iconToDraw.getIntrinsicHeight();
                drawableX = (key.width + mKeyBackgroundPadding.left - mKeyBackgroundPadding.right
                        - drawableWidth) / 2;
                drawableY = (key.height + mKeyBackgroundPadding.top - mKeyBackgroundPadding.bottom
                        - drawableHeight) / 2;

                canvas.translate(drawableX, drawableY);
                iconToDraw.setBounds(0, 0, drawableWidth, drawableHeight);
                iconToDraw.draw(canvas);
                canvas.translate(-drawableX, -drawableY);
                if (keyIsSpace && drawKeyboardNameText) {
                    // now a little hack, I'll set the label now, so it get
                    // drawn.
                    label = mKeyboardName;
                }
            } else {
                // ho... no icon.
                // I'll try to guess the text
                label = guessLabelForKey(key.getPrimaryCode());
            }
        }

        if (label != null) {
            // For characters, use large font. For labels like "Done", use
            // small font.
            final FontMetrics fm;
            if (keyIsSpace) {
                paint.setTextSize(mKeyboardNameTextSize);
                paint.setTypeface(Typeface.DEFAULT_BOLD);
                if (mKeyboardNameFM == null)
                    mKeyboardNameFM = paint.getFontMetrics();
                fm = mKeyboardNameFM;
            } else if (label.length() > 1 && key.getCodesCount() < 2) {
                setPaintForLabelText(paint);
                if (mLabelFM == null)
                    mLabelFM = paint.getFontMetrics();
                fm = mLabelFM;
            } else {
                setPaintToKeyText(paint);
                if (mTextFM == null)
                    mTextFM = paint.getFontMetrics();
                fm = mTextFM;
            }

            if (isLabelOfPictographic(label)) {
                paint.setTextSize(2f * paint.getTextSize());
            }

            final float labelHeight = -fm.top;
            // Draw a drop shadow for the text
            paint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mShadowColor);

            final float textWidth = adjustTextSizeForLabel(paint, label, key.width);

            // the center of the drawable space, which is value used
            // previously for vertically
            // positioning the key label
            final float centerY = mKeyBackgroundPadding.top
                    + ((key.height - mKeyBackgroundPadding.top - mKeyBackgroundPadding.bottom)
                            / (keyIsSpace ? 3 : 2));// the label on the space is a bit higher

            // the X coordinate for the center of the main label text is
            // unaffected by the hints
            final float textX = mKeyBackgroundPadding.left
                    + (key.width - mKeyBackgroundPadding.left - mKeyBackgroundPadding.right) / 2;
            final float textY;
            // Some devices (mostly pre-Honeycomb, have issues with RTL text
            // drawing.
            // Of course, there is no issue with a single character :)
            // so, we'll use the RTL secured drawing (via StaticLayout) for
            // labels.
            if (label.length() > 1 && !AnyApplication.getConfig().workaround_alwaysUseDrawText()) {
                // calculate Y coordinate of top of text based on center
                // location
                textY = centerY - ((labelHeight - paint.descent()) / 2);
                canvas.translate(textX, textY);
                // RTL fix. But it costs, let do it when in need (more than
                // 1 character)
                StaticLayout labelText = new StaticLayout(label, new TextPaint(paint), (int) textWidth,
                        Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
                labelText.draw(canvas);
            } else {
                // to get Y coordinate of baseline from center of text,
                // first add half the height (to get to
                // bottom of text), then subtract the part below the
                // baseline. Note that fm.top is negative.
                textY = centerY + ((labelHeight - paint.descent()) / 2);
                canvas.translate(textX, textY);
                canvas.drawText(label, 0, label.length(), 0, 0, paint);
            }
            canvas.translate(-textX, -textY);
            // (-)

            // Turn off drop shadow
            paint.setShadowLayer(0, 0, 0, 0);
        }

        if (drawHintText) {
            if ((key.popupCharacters != null && key.popupCharacters.length() > 0) || (key.popupResId != 0)
                    || (key.longPressCode != 0)) {
                Align oldAlign = paint.getTextAlign();

                String hintText = null;

                if (key.hintLabel != null && key.hintLabel.length() > 0) {
                    hintText = key.hintLabel.toString();
                    // it is the responsibility of the keyboard layout
                    // designer to ensure that they do
                    // not put too many characters in the hint label...
                } else if (key.longPressCode != 0) {
                    if (Character.isLetterOrDigit(key.longPressCode))
                        hintText = Character.toString((char) key.longPressCode);
                } else if (key.popupCharacters != null) {
                    final String hintString = key.popupCharacters.toString();
                    final int hintLength = hintString.length();
                    if (hintLength <= 3)
                        hintText = hintString;
                }

                // if hintText is still null, it means it didn't fit one of
                // the above
                // cases, so we should provide the hint using the default
                if (hintText == null) {
                    if (mHintOverflowLabel != null)
                        hintText = mHintOverflowLabel;
                    else {
                        // theme does not provide a defaultHintLabel
                        // use  if hints are above, ... if hints are
                        // below
                        // (to avoid being too close to main label/icon)
                        if (hintVAlign == Gravity.TOP)
                            hintText = "";
                        else
                            hintText = "...";
                    }
                }

                if (mKeyboard.isShifted())
                    hintText = hintText.toUpperCase(getKeyboard().getLocale());

                // now draw hint
                paint.setTypeface(Typeface.DEFAULT);
                paint.setColor(hintColor.getColorForState(drawableState, 0xFF000000));
                paint.setTextSize(mHintTextSize);
                // get the hint text font metrics so that we know the size
                // of the hint when
                // we try to position the main label (to try to make sure
                // they don't overlap)
                if (mHintTextFM == null) {
                    mHintTextFM = paint.getFontMetrics();
                }

                final float hintX;
                final float hintY;

                // the (float) 0.5 value is added or subtracted to just give
                // a little more room
                // in case the theme designer didn't account for the hint
                // label location
                if (hintAlign == Gravity.START) {
                    // left
                    paint.setTextAlign(Align.LEFT);
                    hintX = mKeyBackgroundPadding.left + 0.5f;
                } else if (hintAlign == Gravity.CENTER) {
                    // center
                    paint.setTextAlign(Align.CENTER);
                    hintX = mKeyBackgroundPadding.left
                            + (key.width - mKeyBackgroundPadding.left - mKeyBackgroundPadding.right) / 2;
                } else {
                    // right
                    paint.setTextAlign(Align.RIGHT);
                    hintX = key.width - mKeyBackgroundPadding.right - 0.5f;
                }

                if (hintVAlign == Gravity.TOP) {
                    // above
                    hintY = mKeyBackgroundPadding.top - mHintTextFM.top + 0.5f;
                } else {
                    // below
                    hintY = key.height - mKeyBackgroundPadding.bottom - mHintTextFM.bottom - 0.5f;
                }

                canvas.drawText(hintText, hintX, hintY, paint);
                paint.setTextAlign(oldAlign);
            }
        }

        canvas.translate(-key.x - kbdPaddingLeft, -key.y - kbdPaddingTop);
    }
    mInvalidatedKey = null;

    mDirtyRect.setEmpty();
}