/** * Measure the text, stopping early if the measured width exceeds maxWidth. Return the number of * chars that were measured, and if measuredWidth is not null, return in it the actual width * measured. * * @param text The text to measure * @param start The offset into text to begin measuring at * @param end The end of the text slice to measure. * @param measureForwards If true, measure forwards, starting at start. Otherwise, measure * backwards, starting with end. * @param maxWidth The maximum width to accumulate. * @param measuredWidth Optional. If not null, returns the actual width measured. * @return The number of chars that were measured. Will always be <= abs(end - start). */ public int breakText( CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth) { if (start == 0 && text instanceof String && end == text.length()) { return breakText((String) text, measureForwards, maxWidth, measuredWidth); } char[] buf = TemporaryBuffer.obtain(end - start); int result; TextUtils.getChars(text, start, end, buf, 0); if (measureForwards) { result = breakText(buf, 0, end - start, maxWidth, measuredWidth); } else { result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth); } TemporaryBuffer.recycle(buf); return result; }
void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) { this.mText = text; this.mTextStart = start; int len = end - start; this.mLen = len; this.mPos = 0; if (this.mWidths == null || this.mWidths.length < len) { this.mWidths = ArrayUtils.newUnpaddedFloatArray(len); } if (this.mChars == null || this.mChars.length < len) { this.mChars = ArrayUtils.newUnpaddedCharArray(len); } TextUtils.getChars(text, start, end, this.mChars, 0); if (text instanceof Spanned) { Spanned spanned = (Spanned) text; ReplacementSpan[] spans = (ReplacementSpan[]) spanned.getSpans(start, end, ReplacementSpan.class); for (int i = 0; i < spans.length; i++) { int startInPara = spanned.getSpanStart(spans[i]) - start; int endInPara = spanned.getSpanEnd(spans[i]) - start; if (startInPara < 0) { startInPara = 0; } if (endInPara > len) { endInPara = len; } for (int j = startInPara; j < endInPara; j++) { this.mChars[j] = '\ufffc'; } } } if ((textDir == TextDirectionHeuristics.LTR || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR || textDir == TextDirectionHeuristics.ANYRTL_LTR) && TextUtils.doesNotNeedBidi(this.mChars, 0, len)) { this.mDir = 1; this.mEasy = true; return; } int bidiRequest; if (this.mLevels == null || this.mLevels.length < len) { this.mLevels = ArrayUtils.newUnpaddedByteArray(len); } if (textDir == TextDirectionHeuristics.LTR) { bidiRequest = 1; } else if (textDir == TextDirectionHeuristics.RTL) { bidiRequest = -1; } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) { bidiRequest = 2; } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) { bidiRequest = -2; } else { bidiRequest = textDir.isRtl(this.mChars, 0, len) ? -1 : 1; } this.mDir = AndroidBidi.bidi(bidiRequest, this.mChars, this.mLevels, len, false); this.mEasy = false; }
/** * Initializes a TextLine and prepares it for use. * * @param paint the base paint for the line * @param text the text, can be Styled * @param start the start of the line relative to the text * @param limit the limit of the line relative to the text * @param dir the paragraph direction of this line * @param directions the directions information of this line * @param hasTabs true if the line might contain tabs or emoji * @param tabStops the tabStops. Can be null. */ void set( TextPaint paint, CharSequence text, int start, int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) { mPaint = paint; mText = text; mStart = start; mLen = limit - start; mDir = dir; mDirections = directions; if (mDirections == null) { throw new IllegalArgumentException("Directions cannot be null"); } mHasTabs = hasTabs; mSpanned = null; boolean hasReplacement = false; if (text instanceof Spanned) { mSpanned = (Spanned) text; mReplacementSpanSpanSet.init(mSpanned, start, limit); hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; } mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; if (mCharsValid) { if (mChars == null || mChars.length < mLen) { mChars = new char[ArrayUtils.idealCharArraySize(mLen)]; } TextUtils.getChars(text, start, limit, mChars, 0); if (hasReplacement) { // Handle these all at once so we don't have to do it as we go. // Replace the first character of each replacement run with the // object-replacement character and the remainder with zero width // non-break space aka BOM. Cursor movement code skips these // zero-width characters. char[] chars = mChars; for (int i = start, inext; i < limit; i = inext) { inext = mReplacementSpanSpanSet.getNextTransition(i, limit); if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) { // transition into a span chars[i - start] = '\ufffc'; for (int j = i - start + 1, e = inext - start; j < e; ++j) { chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip } } } } } mTabs = tabStops; }
/** * Return the width of the text. * * @param text The text to measure * @param start The index of the first character to start measuring * @param end 1 beyond the index of the last character to measure * @return The width of the text */ public float measureText(CharSequence text, int start, int end) { if (text instanceof String) { return measureText((String) text, start, end); } if (text instanceof SpannedString || text instanceof SpannableString) { return measureText(text.toString(), start, end); } if (text instanceof GraphicsOperations) { return ((GraphicsOperations) text).measureText(start, end, this); } char[] buf = TemporaryBuffer.obtain(end - start); TextUtils.getChars(text, start, end, buf, 0); float result = measureText(buf, 0, end - start); TemporaryBuffer.recycle(buf); return result; }
/** * Return the advance widths for the characters in the string. * * @param text The text to measure * @param start The index of the first char to to measure * @param end The end of the text slice to measure * @param widths array to receive the advance widths of the characters. Must be at least a large * as (end - start). * @return the actual number of widths returned. */ public int getTextWidths(CharSequence text, int start, int end, float[] widths) { if (text instanceof String) { return getTextWidths((String) text, start, end, widths); } if (text instanceof SpannedString || text instanceof SpannableString) { return getTextWidths(text.toString(), start, end, widths); } if (text instanceof GraphicsOperations) { return ((GraphicsOperations) text).getTextWidths(start, end, widths, this); } char[] buf = TemporaryBuffer.obtain(end - start); TextUtils.getChars(text, start, end, buf, 0); int result = getTextWidths(buf, 0, end - start, widths); TemporaryBuffer.recycle(buf); return result; }
@Override public CharSequence filter( CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { char[] v = new char[end - start]; TextUtils.getChars(source, start, end, v, 0); Spannable emojified = EmojiProvider.getInstance(view.getContext()).emojify(new String(v), view); if (source instanceof Spanned) { TextUtils.copySpansFrom((Spanned) source, start, end, null, emojified, 0); } view.getViewTreeObserver().addOnGlobalLayoutListener(this); if (view.getWidth() == 0 || view.getEllipsize() != TruncateAt.END) { return emojified; } else { return TextUtils.ellipsize( emojified, view.getPaint(), view.getWidth() - view.getPaddingRight() - view.getPaddingLeft(), TruncateAt.END); } }