Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f67a3881 authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka Committed by Android (Google) Code Review
Browse files

Merge "Add TypefaceUtils"

parents 6c7d008e 08ae0d5c
Loading
Loading
Loading
Loading
+55 −91
Original line number Diff line number Diff line
@@ -26,10 +26,8 @@ import android.graphics.Paint.Align;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;

import com.android.inputmethod.keyboard.internal.KeyDrawParams;
@@ -73,15 +71,15 @@ import java.util.HashSet;
 */
public class KeyboardView extends View {
    // XML attributes
    protected final KeyVisualAttributes mKeyVisualAttributes;
    private final KeyVisualAttributes mKeyVisualAttributes;
    private final int mKeyLabelHorizontalPadding;
    private final float mKeyHintLetterPadding;
    private final float mKeyPopupHintLetterPadding;
    private final float mKeyShiftedLetterHintPadding;
    private final float mKeyTextShadowRadius;
    protected final float mVerticalCorrection;
    protected final Drawable mKeyBackground;
    protected final Rect mKeyBackgroundPadding = new Rect();
    private final float mVerticalCorrection;
    private final Drawable mKeyBackground;
    private final Rect mKeyBackgroundPadding = new Rect();

    // HORIZONTAL ELLIPSIS "...", character for popup hint.
    private static final String POPUP_HINT_CHAR = "\u2026";
@@ -113,10 +111,6 @@ public class KeyboardView extends View {
    private final Canvas mOffscreenCanvas = new Canvas();
    private final Paint mPaint = new Paint();
    private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();
    // This sparse array caches key label text height in pixel indexed by key label text size.
    private static final SparseArray<Float> sTextHeightCache = CollectionUtils.newSparseArray();
    // This sparse array caches key label text width in pixel indexed by key label text size.
    private static final SparseArray<Float> sTextWidthCache = CollectionUtils.newSparseArray();
    private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
    private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };

@@ -134,15 +128,15 @@ public class KeyboardView extends View {
        mKeyLabelHorizontalPadding = keyboardViewAttr.getDimensionPixelOffset(
                R.styleable.KeyboardView_keyLabelHorizontalPadding, 0);
        mKeyHintLetterPadding = keyboardViewAttr.getDimension(
                R.styleable.KeyboardView_keyHintLetterPadding, 0);
                R.styleable.KeyboardView_keyHintLetterPadding, 0.0f);
        mKeyPopupHintLetterPadding = keyboardViewAttr.getDimension(
                R.styleable.KeyboardView_keyPopupHintLetterPadding, 0);
                R.styleable.KeyboardView_keyPopupHintLetterPadding, 0.0f);
        mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension(
                R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0);
                R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0.0f);
        mKeyTextShadowRadius = keyboardViewAttr.getFloat(
                R.styleable.KeyboardView_keyTextShadowRadius, 0.0f);
        mVerticalCorrection = keyboardViewAttr.getDimension(
                R.styleable.KeyboardView_verticalCorrection, 0);
                R.styleable.KeyboardView_verticalCorrection, 0.0f);
        keyboardViewAttr.recycle();

        final TypedArray keyAttr = context.obtainStyledAttributes(attrs,
@@ -185,6 +179,14 @@ public class KeyboardView extends View {
        return mKeyboard;
    }

    protected float getVerticalCorrection() {
        return mVerticalCorrection;
    }

    protected void updateKeyDrawParams(final int keyHeight) {
        mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        if (mKeyboard != null) {
@@ -213,7 +215,7 @@ public class KeyboardView extends View {
            }
            onDrawKeyboard(mOffscreenCanvas);
        }
        canvas.drawBitmap(mOffscreenBuffer, 0, 0, null);
        canvas.drawBitmap(mOffscreenBuffer, 0.0f, 0.0f, null);
    }

    private boolean maybeAllocateOffscreenBuffer() {
@@ -333,7 +335,7 @@ public class KeyboardView extends View {
        canvas.translate(bgX, bgY);
        background.draw(canvas);
        if (LatinImeLogger.sVISUALDEBUG) {
            drawRectangle(canvas, 0, 0, bgWidth, bgHeight, 0x80c00000, new Paint());
            drawRectangle(canvas, 0.0f, 0.0f, bgWidth, bgHeight, 0x80c00000, new Paint());
        }
        canvas.translate(-bgX, -bgY);
    }
@@ -347,7 +349,7 @@ public class KeyboardView extends View {
        final float centerY = keyHeight * 0.5f;

        if (LatinImeLogger.sVISUALDEBUG) {
            drawRectangle(canvas, 0, 0, keyWidth, keyHeight, 0x800000c0, new Paint());
            drawRectangle(canvas, 0.0f, 0.0f, keyWidth, keyHeight, 0x800000c0, new Paint());
        }

        // Draw key label.
@@ -357,14 +359,16 @@ public class KeyboardView extends View {
            final String label = key.mLabel;
            paint.setTypeface(key.selectTypeface(params));
            paint.setTextSize(key.selectTextSize(params));
            final float labelCharHeight = getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint);
            final float labelCharWidth = getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint);
            final float labelCharHeight = TypefaceUtils.getCharHeight(
                    KEY_LABEL_REFERENCE_CHAR, paint);
            final float labelCharWidth = TypefaceUtils.getCharWidth(
                    KEY_LABEL_REFERENCE_CHAR, paint);

            // Vertical label text alignment.
            final float baseline = centerY + labelCharHeight / 2;
            final float baseline = centerY + labelCharHeight / 2.0f;

            // Horizontal label text alignment
            float labelWidth = 0;
            float labelWidth = 0.0f;
            if (key.isAlignLeft()) {
                positionX = mKeyLabelHorizontalPadding;
                paint.setTextAlign(Align.LEFT);
@@ -373,31 +377,31 @@ public class KeyboardView extends View {
                paint.setTextAlign(Align.RIGHT);
            } else if (key.isAlignLeftOfCenter()) {
                // TODO: Parameterise this?
                positionX = centerX - labelCharWidth * 7 / 4;
                positionX = centerX - labelCharWidth * 7.0f / 4.0f;
                paint.setTextAlign(Align.LEFT);
            } else if (key.hasLabelWithIconLeft() && icon != null) {
                labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth()
                labelWidth = TypefaceUtils.getLabelWidth(label, paint) + icon.getIntrinsicWidth()
                        + LABEL_ICON_MARGIN * keyWidth;
                positionX = centerX + labelWidth / 2;
                positionX = centerX + labelWidth / 2.0f;
                paint.setTextAlign(Align.RIGHT);
            } else if (key.hasLabelWithIconRight() && icon != null) {
                labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth()
                labelWidth = TypefaceUtils.getLabelWidth(label, paint) + icon.getIntrinsicWidth()
                        + LABEL_ICON_MARGIN * keyWidth;
                positionX = centerX - labelWidth / 2;
                positionX = centerX - labelWidth / 2.0f;
                paint.setTextAlign(Align.LEFT);
            } else {
                positionX = centerX;
                paint.setTextAlign(Align.CENTER);
            }
            if (key.needsXScale()) {
                paint.setTextScaleX(
                        Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / getLabelWidth(label, paint)));
                paint.setTextScaleX(Math.min(1.0f,
                        (keyWidth * MAX_LABEL_RATIO) / TypefaceUtils.getLabelWidth(label, paint)));
            }

            paint.setColor(key.selectTextColor(params));
            if (key.isEnabled()) {
                // Set a drop shadow for the text
                paint.setShadowLayer(mKeyTextShadowRadius, 0, 0, params.mTextShadowColor);
                paint.setShadowLayer(mKeyTextShadowRadius, 0.0f, 0.0f, params.mTextShadowColor);
            } else {
                // Make label invisible
                paint.setColor(Color.TRANSPARENT);
@@ -405,7 +409,7 @@ public class KeyboardView extends View {
            blendAlpha(paint, params.mAnimAlpha);
            canvas.drawText(label, 0, label.length(), positionX, baseline, paint);
            // Turn off drop shadow and reset x-scale.
            paint.setShadowLayer(0, 0, 0, 0);
            paint.setShadowLayer(0.0f, 0.0f, 0.0f, Color.TRANSPARENT);
            paint.setTextScaleX(1.0f);

            if (icon != null) {
@@ -413,10 +417,10 @@ public class KeyboardView extends View {
                final int iconHeight = icon.getIntrinsicHeight();
                final int iconY = (keyHeight - iconHeight) / 2;
                if (key.hasLabelWithIconLeft()) {
                    final int iconX = (int)(centerX - labelWidth / 2);
                    final int iconX = (int)(centerX - labelWidth / 2.0f);
                    drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
                } else if (key.hasLabelWithIconRight()) {
                    final int iconX = (int)(centerX + labelWidth / 2 - iconWidth);
                    final int iconX = (int)(centerX + labelWidth / 2.0f - iconWidth);
                    drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
                }
            }
@@ -439,20 +443,23 @@ public class KeyboardView extends View {
                // The hint label is placed just right of the key label. Used mainly on
                // "phone number" layout.
                // TODO: Generalize the following calculations.
                hintX = positionX + getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) * 2;
                hintY = centerY + getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint) / 2;
                hintX = positionX
                        + TypefaceUtils.getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) * 2.0f;
                hintY = centerY
                        + TypefaceUtils.getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint) / 2.0f;
                paint.setTextAlign(Align.LEFT);
            } else if (key.hasShiftedLetterHint()) {
                // The hint label is placed at top-right corner of the key. Used mainly on tablet.
                hintX = keyWidth - mKeyShiftedLetterHintPadding
                        - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2;
                        - TypefaceUtils.getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2.0f;
                paint.getFontMetrics(mFontMetrics);
                hintY = -mFontMetrics.top;
                paint.setTextAlign(Align.CENTER);
            } else { // key.hasHintLetter()
                // The hint letter is placed at top-right corner of the key. Used mainly on phone.
                hintX = keyWidth - mKeyHintLetterPadding
                        - getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint) / 2;
                        - TypefaceUtils.getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint)
                        / 2.0f;
                hintY = -paint.ascent();
                paint.setTextAlign(Align.CENTER);
            }
@@ -506,7 +513,7 @@ public class KeyboardView extends View {
        paint.setColor(params.mHintLabelColor);
        paint.setTextAlign(Align.CENTER);
        final float hintX = keyWidth - mKeyHintLetterPadding
                - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2;
                - TypefaceUtils.getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2.0f;
        final float hintY = keyHeight - mKeyPopupHintLetterPadding;
        canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint);

@@ -517,54 +524,6 @@ public class KeyboardView extends View {
        }
    }

    private static int getCharGeometryCacheKey(final char referenceChar, final Paint paint) {
        final int labelSize = (int)paint.getTextSize();
        final Typeface face = paint.getTypeface();
        final int codePointOffset = referenceChar << 15;
        if (face == Typeface.DEFAULT) {
            return codePointOffset + labelSize;
        } else if (face == Typeface.DEFAULT_BOLD) {
            return codePointOffset + labelSize + 0x1000;
        } else if (face == Typeface.MONOSPACE) {
            return codePointOffset + labelSize + 0x2000;
        } else {
            return codePointOffset + labelSize;
        }
    }

    // Working variable for the following methods.
    private final Rect mTextBounds = new Rect();

    private float getCharHeight(final char[] referenceChar, final Paint paint) {
        final int key = getCharGeometryCacheKey(referenceChar[0], paint);
        final Float cachedValue = sTextHeightCache.get(key);
        if (cachedValue != null)
            return cachedValue;

        paint.getTextBounds(referenceChar, 0, 1, mTextBounds);
        final float height = mTextBounds.height();
        sTextHeightCache.put(key, height);
        return height;
    }

    private float getCharWidth(final char[] referenceChar, final Paint paint) {
        final int key = getCharGeometryCacheKey(referenceChar[0], paint);
        final Float cachedValue = sTextWidthCache.get(key);
        if (cachedValue != null)
            return cachedValue;

        paint.getTextBounds(referenceChar, 0, 1, mTextBounds);
        final float width = mTextBounds.width();
        sTextWidthCache.put(key, width);
        return width;
    }

    // TODO: Remove this method.
    public float getLabelWidth(final String label, final Paint paint) {
        paint.getTextBounds(label, 0, label.length(), mTextBounds);
        return mTextBounds.width();
    }

    protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x,
            final int y, final int width, final int height) {
        canvas.translate(x, y);
@@ -578,7 +537,7 @@ public class KeyboardView extends View {
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(1.0f);
        paint.setColor(color);
        canvas.drawLine(0, y, w, y, paint);
        canvas.drawLine(0.0f, y, w, y, paint);
    }

    private static void drawVerticalLine(final Canvas canvas, final float x, final float h,
@@ -586,7 +545,7 @@ public class KeyboardView extends View {
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(1.0f);
        paint.setColor(color);
        canvas.drawLine(x, 0, x, h, paint);
        canvas.drawLine(x, 0.0f, x, h, paint);
    }

    private static void drawRectangle(final Canvas canvas, final float x, final float y,
@@ -595,15 +554,20 @@ public class KeyboardView extends View {
        paint.setStrokeWidth(1.0f);
        paint.setColor(color);
        canvas.translate(x, y);
        canvas.drawRect(0, 0, w, h, paint);
        canvas.drawRect(0.0f, 0.0f, w, h, paint);
        canvas.translate(-x, -y);
    }

    public Paint newDefaultLabelPaint() {
    public Paint newLabelPaint(final Key key) {
        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        if (key == null) {
            paint.setTypeface(mKeyDrawParams.mTypeface);
            paint.setTextSize(mKeyDrawParams.mLabelSize);
        } else {
            paint.setTypeface(key.selectTypeface(mKeyDrawParams));
            paint.setTextSize(key.selectTextSize(mKeyDrawParams));
        }
        return paint;
    }

+10 −9
Original line number Diff line number Diff line
@@ -530,9 +530,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);

        final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
                R.styleable.MainKeyboardView_keyHysteresisDistance, 0);
                R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f);
        final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension(
                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0);
                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
        mKeyDetector = new KeyDetector(
                keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
        mKeyTimerHandler = new KeyTimerHandler(this, mainKeyboardViewAttr);
@@ -655,7 +655,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
        mKeyTimerHandler.cancelLongPressTimer();
        super.setKeyboard(keyboard);
        mKeyDetector.setKeyboard(
                keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
                keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
        PointerTracker.setKeyDetector(mKeyDetector);
        mTouchScreenRegulator.setKeyboardGeometry(keyboard.mOccupiedWidth);
        mMoreKeysKeyboardCache.clear();
@@ -1329,7 +1329,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack

        // Overlay a dark rectangle to dim.
        if (mNeedsToDimEntireKeyboard) {
            canvas.drawRect(0, 0, getWidth(), getHeight(), mBackgroundDimAlphaPaint);
            canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mBackgroundDimAlphaPaint);
        }
    }

@@ -1353,9 +1353,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
        }
    }

    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
    private static boolean fitsTextIntoWidth(final int width, final String text,
            final Paint paint) {
        paint.setTextScaleX(1.0f);
        final float textWidth = getLabelWidth(text, paint);
        final float textWidth = TypefaceUtils.getLabelWidth(text, paint);
        if (textWidth < width) {
            return true;
        }
@@ -1366,12 +1367,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
        }

        paint.setTextScaleX(scaleX);
        return getLabelWidth(text, paint) < width;
        return TypefaceUtils.getLabelWidth(text, paint) < width;
    }

    // Layout language name on spacebar.
    private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype,
            final int width) {
    private static String layoutLanguageOnSpacebar(final Paint paint,
            final InputMethodSubtype subtype, final int width) {
        // Choose appropriate language name to fit into the width.
        final String fullText = getFullDisplayName(subtype);
        if (fitsTextIntoWidth(width, fullText, paint)) {
+11 −15
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.inputmethod.keyboard;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;

@@ -258,7 +259,6 @@ public final class MoreKeysKeyboard extends Keyboard {
        private static final float LABEL_PADDING_RATIO = 0.2f;
        private static final float DIVIDER_RATIO = 0.2f;


        /**
         * The builder of MoreKeysKeyboard.
         * @param context the context of {@link MoreKeysKeyboardView}.
@@ -290,10 +290,10 @@ public final class MoreKeysKeyboard extends Keyboard {
                // be considered because the vertical positions of both backgrounds were already
                // adjusted with their bottom paddings deducted.
                width = keyPreviewDrawParams.mPreviewVisibleWidth;
                height = keyPreviewDrawParams.mPreviewVisibleHeight
                        + mParams.mVerticalGap;
                height = keyPreviewDrawParams.mPreviewVisibleHeight + mParams.mVerticalGap;
            } else {
                width = getMaxKeyWidth(parentKeyboardView, parentKey, mParams.mDefaultKeyWidth);
                width = getMaxKeyWidth(parentKeyboardView, parentKey, mParams.mDefaultKeyWidth,
                        context.getResources());
                height = parentKeyboard.mMostCommonKeyHeight;
            }
            final int dividerWidth;
@@ -311,22 +311,18 @@ public final class MoreKeysKeyboard extends Keyboard {
        }

        private static int getMaxKeyWidth(final KeyboardView view, final Key parentKey,
                final int minKeyWidth) {
            final int padding = (int)(view.getResources()
                    .getDimension(R.dimen.more_keys_keyboard_key_horizontal_padding)
                    + (parentKey.hasLabelsInMoreKeys() ? minKeyWidth * LABEL_PADDING_RATIO : 0));
            final Paint paint = view.newDefaultLabelPaint();
            paint.setTypeface(parentKey.selectTypeface(view.mKeyDrawParams));
            paint.setTextSize(parentKey.selectMoreKeyTextSize(view.mKeyDrawParams));
                final int minKeyWidth, final Resources res) {
            final float padding =
                    res.getDimension(R.dimen.more_keys_keyboard_key_horizontal_padding)
                    + (parentKey.hasLabelsInMoreKeys() ? minKeyWidth * LABEL_PADDING_RATIO : 0.0f);
            final Paint paint = view.newLabelPaint(parentKey);
            int maxWidth = minKeyWidth;
            for (final MoreKeySpec spec : parentKey.mMoreKeys) {
                final String label = spec.mLabel;
                // If the label is single letter, minKeyWidth is enough to hold the label.
                if (label != null && StringUtils.codePointCount(label) > 1) {
                    final int width = (int)view.getLabelWidth(label, paint) + padding;
                    if (maxWidth < width) {
                        maxWidth = width;
                    }
                    maxWidth = Math.max(maxWidth,
                            (int)(TypefaceUtils.getLabelWidth(label, paint) + padding));
                }
            }
            return maxWidth;
+1 −1
Original line number Diff line number Diff line
@@ -71,7 +71,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
    public void setKeyboard(final Keyboard keyboard) {
        super.setKeyboard(keyboard);
        mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
                -getPaddingTop() + mVerticalCorrection);
                -getPaddingTop() + getVerticalCorrection());
    }

    @Override
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.inputmethod.keyboard;

import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.SparseArray;

import com.android.inputmethod.latin.CollectionUtils;

public final class TypefaceUtils {
    private TypefaceUtils() {
        // This utility class is not publicly instantiable.
    }

    // This sparse array caches key label text height in pixel indexed by key label text size.
    private static final SparseArray<Float> sTextHeightCache = CollectionUtils.newSparseArray();
    // Working variable for the following method.
    private static final Rect sTextHeightBounds = new Rect();

    public static float getCharHeight(final char[] referenceChar, final Paint paint) {
        final int key = getCharGeometryCacheKey(referenceChar[0], paint);
        synchronized (sTextHeightCache) {
            final Float cachedValue = sTextHeightCache.get(key);
            if (cachedValue != null) {
                return cachedValue;
            }

            paint.getTextBounds(referenceChar, 0, 1, sTextHeightBounds);
            final float height = sTextHeightBounds.height();
            sTextHeightCache.put(key, height);
            return height;
        }
    }

    // This sparse array caches key label text width in pixel indexed by key label text size.
    private static final SparseArray<Float> sTextWidthCache = CollectionUtils.newSparseArray();
    // Working variable for the following method.
    private static final Rect sTextWidthBounds = new Rect();

    public static float getCharWidth(final char[] referenceChar, final Paint paint) {
        final int key = getCharGeometryCacheKey(referenceChar[0], paint);
        synchronized (sTextWidthCache) {
            final Float cachedValue = sTextWidthCache.get(key);
            if (cachedValue != null) {
                return cachedValue;
            }

            paint.getTextBounds(referenceChar, 0, 1, sTextWidthBounds);
            final float width = sTextWidthBounds.width();
            sTextWidthCache.put(key, width);
            return width;
        }
    }

    private static int getCharGeometryCacheKey(final char referenceChar, final Paint paint) {
        final int labelSize = (int)paint.getTextSize();
        final Typeface face = paint.getTypeface();
        final int codePointOffset = referenceChar << 15;
        if (face == Typeface.DEFAULT) {
            return codePointOffset + labelSize;
        } else if (face == Typeface.DEFAULT_BOLD) {
            return codePointOffset + labelSize + 0x1000;
        } else if (face == Typeface.MONOSPACE) {
            return codePointOffset + labelSize + 0x2000;
        } else {
            return codePointOffset + labelSize;
        }
    }

    public static float getLabelWidth(final String label, final Paint paint) {
        final Rect textBounds = new Rect();
        paint.getTextBounds(label, 0, label.length(), textBounds);
        return textBounds.width();
    }
}
Loading