Loading apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +25 −10 Original line number Diff line number Diff line Loading @@ -190,8 +190,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading @@ -206,8 +209,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading @@ -222,8 +228,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading @@ -238,8 +247,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading @@ -254,8 +266,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading api/current.txt +11 −2 Original line number Diff line number Diff line Loading @@ -42295,10 +42295,10 @@ package android.text { } public class MeasuredText implements android.text.Spanned { method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic); method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int); method public char charAt(int); method public int getBreakStrategy(); method public int getEnd(); method public int getHyphenationFrequency(); method public android.text.TextPaint getPaint(); method public int getParagraphCount(); method public int getParagraphEnd(int); Loading @@ -42315,6 +42315,15 @@ package android.text { method public java.lang.CharSequence subSequence(int, int); } public static final class MeasuredText.Builder { ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint); method public android.text.MeasuredText build(); method public android.text.MeasuredText.Builder setBreakStrategy(int); method public android.text.MeasuredText.Builder setHyphenationFrequency(int); method public android.text.MeasuredText.Builder setRange(int, int); method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic); } public abstract interface NoCopySpan { } core/java/android/text/MeasuredParagraph.java +7 −3 Original line number Diff line number Diff line Loading @@ -356,6 +356,7 @@ public class MeasuredParagraph { @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir, boolean computeHyphenation, @Nullable MeasuredParagraph recycle) { final MeasuredParagraph mt = recycle == null ? obtain() : recycle; mt.resetAndAnalyzeBidi(text, start, end, textDir); Loading @@ -365,7 +366,8 @@ public class MeasuredParagraph { long nativeBuilderPtr = nInitBuilder(); try { mt.bindNativeObject( nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer)); nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, computeHyphenation)); } finally { nFreeBuilder(nativeBuilderPtr); } Loading Loading @@ -394,7 +396,8 @@ public class MeasuredParagraph { mt.mSpanEndCache.append(spanEnd); } } mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer)); mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, computeHyphenation)); } finally { nFreeBuilder(nativeBuilderPtr); } Loading Loading @@ -668,7 +671,8 @@ public class MeasuredParagraph { @FloatRange(from = 0) float width); private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr, @NonNull char[] text); @NonNull char[] text, boolean computeHyphenation); private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr); Loading core/java/android/text/MeasuredText.java +145 −52 Original line number Diff line number Diff line Loading @@ -52,70 +52,147 @@ public class MeasuredText implements Spanned { // The sorted paragraph end offsets. private final @NonNull int[] mParagraphBreakPoints; // The break strategy for this measured text. private final @Layout.BreakStrategy int mBreakStrategy; // The hyphenation frequency for this measured text. private final @Layout.HyphenationFrequency int mHyphenationFrequency; /** * Build MeasuredText from the text. * A Builder for MeasuredText */ public static final class Builder { // Mandatory parameters. private final @NonNull CharSequence mText; private final @NonNull TextPaint mPaint; // Members to be updated by setters. private @IntRange(from = 0) int mStart; private @IntRange(from = 0) int mEnd; private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; private @Layout.HyphenationFrequency int mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NORMAL; /** * Builder constructor * * @param text The text to be measured. * @param paint The paint to be used for drawing. * @param textDir The text direction. * @return The measured text. */ public static @NonNull MeasuredText build(@NonNull CharSequence text, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir) { return MeasuredText.build(text, paint, textDir, 0, text.length()); public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) { Preconditions.checkNotNull(text); Preconditions.checkNotNull(paint); mText = text; mPaint = paint; mStart = 0; mEnd = text.length(); } /** * Build MeasuredText from the specific range of the text.. * Set the range of measuring target. * * @param text The text to be measured. * @param paint The paint to be used for drawing. * @param textDir The text direction. * @param start The inclusive start offset of the text. * @param end The exclusive start offset of the text. * @return The measured text. * @param start The measuring target start offset in the text. * @param end The measuring target end offset in the text. */ public static @NonNull MeasuredText build(@NonNull CharSequence text, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, @IntRange(from = 0) int start, public @NonNull Builder setRange(@IntRange(from = 0) int start, @IntRange(from = 0) int end) { Preconditions.checkNotNull(text); Preconditions.checkNotNull(paint); Preconditions.checkArgumentInRange(start, 0, mText.length(), "start"); Preconditions.checkArgumentInRange(end, 0, mText.length(), "end"); Preconditions.checkArgument(start <= end, "The range is reversed."); mStart = start; mEnd = end; return this; } /** * Set the text direction heuristic * * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}. * * @param textDir The text direction heuristic for resolving bidi behavior. * @return this builder, useful for chaining. */ public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) { Preconditions.checkNotNull(textDir); Preconditions.checkArgumentInRange(start, 0, text.length(), "start"); Preconditions.checkArgumentInRange(end, 0, text.length(), "end"); mTextDir = textDir; return this; } /** * Set the break strategy * * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}. * * @param breakStrategy The break strategy. * @return this builder, useful for chaining. */ public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) { mBreakStrategy = breakStrategy; return this; } /** * Set the hyphenation frequency * * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. * * @param hyphenationFrequency The hyphenation frequency. * @return this builder, useful for chaining. */ public @NonNull Builder setHyphenationFrequency( @Layout.HyphenationFrequency int hyphenationFrequency) { mHyphenationFrequency = hyphenationFrequency; return this; } /** * Build the measured text * * @return the measured text. */ public @NonNull MeasuredText build() { final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE; final IntArray paragraphEnds = new IntArray(); final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>(); int paraEnd = 0; for (int paraStart = start; paraStart < end; paraStart = paraEnd) { paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end); for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) { paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd); if (paraEnd < 0) { // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end. paraEnd = end; // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph // end. paraEnd = mEnd; } else { paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. } paragraphEnds.add(paraEnd); measuredTexts.add(MeasuredParagraph.buildForStaticLayout( paint, text, paraStart, paraEnd, textDir, null /* no recycle */)); mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation, null /* no recycle */)); } return new MeasuredText(text, start, end, paint, textDir, measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]), return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency, measuredTexts.toArray( new MeasuredParagraph[measuredTexts.size()]), paragraphEnds.toArray()); } }; // Use MeasuredText.build instead. // Use MeasuredText.Builder instead. private MeasuredText(@NonNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int breakStrategy, @Layout.HyphenationFrequency int frequency, @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) { mText = text; Loading @@ -125,6 +202,8 @@ public class MeasuredText implements Spanned { mMeasuredParagraphs = measuredTexts; mParagraphBreakPoints = paragraphBreakPoints; mTextDir = textDir; mBreakStrategy = breakStrategy; mHyphenationFrequency = frequency; } /** Loading Loading @@ -190,6 +269,20 @@ public class MeasuredText implements Spanned { return mMeasuredParagraphs[paraIndex]; } /** * Returns the break strategy for this text. */ public @Layout.BreakStrategy int getBreakStrategy() { return mBreakStrategy; } /** * Returns the hyphenation frequency for this text. */ public @Layout.HyphenationFrequency int getHyphenationFrequency() { return mHyphenationFrequency; } /////////////////////////////////////////////////////////////////////////////////////////////// // Spanned overrides // Loading core/java/android/text/StaticLayout.java +24 −10 Original line number Diff line number Diff line Loading @@ -653,27 +653,41 @@ public class StaticLayout extends Layout { MeasuredText measured = null; final Spanned spanned; final boolean canUseMeasuredText; if (source instanceof MeasuredText) { measured = (MeasuredText) source; final CharSequence original = measured.getText(); spanned = (original instanceof Spanned) ? (Spanned) original : null; if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) { // The buffer position has changed. Re-measure here. measured = MeasuredText.build(original, paint, textDir, bufStart, bufEnd); canUseMeasuredText = false; } else if (b.mBreakStrategy != measured.getBreakStrategy() || b.mHyphenationFrequency != measured.getHyphenationFrequency()) { // The computed hyphenation pieces may not be able to used. Re-measure it. canUseMeasuredText = false; } else { // We can use measured information. canUseMeasuredText = true; } } else { canUseMeasuredText = false; } // Overwrite with the one when emeasured. if (!canUseMeasuredText) { measured = new MeasuredText.Builder(source, paint) .setRange(bufStart, bufEnd) .setTextDirection(textDir) .setBreakStrategy(b.mBreakStrategy) .setHyphenationFrequency(b.mHyphenationFrequency) .build(); spanned = (source instanceof Spanned) ? (Spanned) source : null; } else { final CharSequence original = measured.getText(); spanned = (original instanceof Spanned) ? (Spanned) original : null; // Overwrite with the one when measured. // TODO: Give an option for developer not to overwrite and measure again here? textDir = measured.getTextDir(); paint = measured.getPaint(); } } else { measured = MeasuredText.build(source, paint, textDir, bufStart, bufEnd); spanned = (source instanceof Spanned) ? (Spanned) source : null; } try { for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) { Loading Loading
apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +25 −10 Original line number Diff line number Diff line Loading @@ -190,8 +190,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading @@ -206,8 +209,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading @@ -222,8 +228,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading @@ -238,8 +247,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading @@ -254,8 +266,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, LTR); final MeasuredText text = new MeasuredText.Builder( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) Loading
api/current.txt +11 −2 Original line number Diff line number Diff line Loading @@ -42295,10 +42295,10 @@ package android.text { } public class MeasuredText implements android.text.Spanned { method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic); method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int); method public char charAt(int); method public int getBreakStrategy(); method public int getEnd(); method public int getHyphenationFrequency(); method public android.text.TextPaint getPaint(); method public int getParagraphCount(); method public int getParagraphEnd(int); Loading @@ -42315,6 +42315,15 @@ package android.text { method public java.lang.CharSequence subSequence(int, int); } public static final class MeasuredText.Builder { ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint); method public android.text.MeasuredText build(); method public android.text.MeasuredText.Builder setBreakStrategy(int); method public android.text.MeasuredText.Builder setHyphenationFrequency(int); method public android.text.MeasuredText.Builder setRange(int, int); method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic); } public abstract interface NoCopySpan { }
core/java/android/text/MeasuredParagraph.java +7 −3 Original line number Diff line number Diff line Loading @@ -356,6 +356,7 @@ public class MeasuredParagraph { @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir, boolean computeHyphenation, @Nullable MeasuredParagraph recycle) { final MeasuredParagraph mt = recycle == null ? obtain() : recycle; mt.resetAndAnalyzeBidi(text, start, end, textDir); Loading @@ -365,7 +366,8 @@ public class MeasuredParagraph { long nativeBuilderPtr = nInitBuilder(); try { mt.bindNativeObject( nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer)); nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, computeHyphenation)); } finally { nFreeBuilder(nativeBuilderPtr); } Loading Loading @@ -394,7 +396,8 @@ public class MeasuredParagraph { mt.mSpanEndCache.append(spanEnd); } } mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer)); mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, computeHyphenation)); } finally { nFreeBuilder(nativeBuilderPtr); } Loading Loading @@ -668,7 +671,8 @@ public class MeasuredParagraph { @FloatRange(from = 0) float width); private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr, @NonNull char[] text); @NonNull char[] text, boolean computeHyphenation); private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr); Loading
core/java/android/text/MeasuredText.java +145 −52 Original line number Diff line number Diff line Loading @@ -52,70 +52,147 @@ public class MeasuredText implements Spanned { // The sorted paragraph end offsets. private final @NonNull int[] mParagraphBreakPoints; // The break strategy for this measured text. private final @Layout.BreakStrategy int mBreakStrategy; // The hyphenation frequency for this measured text. private final @Layout.HyphenationFrequency int mHyphenationFrequency; /** * Build MeasuredText from the text. * A Builder for MeasuredText */ public static final class Builder { // Mandatory parameters. private final @NonNull CharSequence mText; private final @NonNull TextPaint mPaint; // Members to be updated by setters. private @IntRange(from = 0) int mStart; private @IntRange(from = 0) int mEnd; private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; private @Layout.HyphenationFrequency int mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NORMAL; /** * Builder constructor * * @param text The text to be measured. * @param paint The paint to be used for drawing. * @param textDir The text direction. * @return The measured text. */ public static @NonNull MeasuredText build(@NonNull CharSequence text, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir) { return MeasuredText.build(text, paint, textDir, 0, text.length()); public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) { Preconditions.checkNotNull(text); Preconditions.checkNotNull(paint); mText = text; mPaint = paint; mStart = 0; mEnd = text.length(); } /** * Build MeasuredText from the specific range of the text.. * Set the range of measuring target. * * @param text The text to be measured. * @param paint The paint to be used for drawing. * @param textDir The text direction. * @param start The inclusive start offset of the text. * @param end The exclusive start offset of the text. * @return The measured text. * @param start The measuring target start offset in the text. * @param end The measuring target end offset in the text. */ public static @NonNull MeasuredText build(@NonNull CharSequence text, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, @IntRange(from = 0) int start, public @NonNull Builder setRange(@IntRange(from = 0) int start, @IntRange(from = 0) int end) { Preconditions.checkNotNull(text); Preconditions.checkNotNull(paint); Preconditions.checkArgumentInRange(start, 0, mText.length(), "start"); Preconditions.checkArgumentInRange(end, 0, mText.length(), "end"); Preconditions.checkArgument(start <= end, "The range is reversed."); mStart = start; mEnd = end; return this; } /** * Set the text direction heuristic * * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}. * * @param textDir The text direction heuristic for resolving bidi behavior. * @return this builder, useful for chaining. */ public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) { Preconditions.checkNotNull(textDir); Preconditions.checkArgumentInRange(start, 0, text.length(), "start"); Preconditions.checkArgumentInRange(end, 0, text.length(), "end"); mTextDir = textDir; return this; } /** * Set the break strategy * * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}. * * @param breakStrategy The break strategy. * @return this builder, useful for chaining. */ public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) { mBreakStrategy = breakStrategy; return this; } /** * Set the hyphenation frequency * * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. * * @param hyphenationFrequency The hyphenation frequency. * @return this builder, useful for chaining. */ public @NonNull Builder setHyphenationFrequency( @Layout.HyphenationFrequency int hyphenationFrequency) { mHyphenationFrequency = hyphenationFrequency; return this; } /** * Build the measured text * * @return the measured text. */ public @NonNull MeasuredText build() { final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE; final IntArray paragraphEnds = new IntArray(); final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>(); int paraEnd = 0; for (int paraStart = start; paraStart < end; paraStart = paraEnd) { paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end); for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) { paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd); if (paraEnd < 0) { // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end. paraEnd = end; // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph // end. paraEnd = mEnd; } else { paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. } paragraphEnds.add(paraEnd); measuredTexts.add(MeasuredParagraph.buildForStaticLayout( paint, text, paraStart, paraEnd, textDir, null /* no recycle */)); mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation, null /* no recycle */)); } return new MeasuredText(text, start, end, paint, textDir, measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]), return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency, measuredTexts.toArray( new MeasuredParagraph[measuredTexts.size()]), paragraphEnds.toArray()); } }; // Use MeasuredText.build instead. // Use MeasuredText.Builder instead. private MeasuredText(@NonNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int breakStrategy, @Layout.HyphenationFrequency int frequency, @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) { mText = text; Loading @@ -125,6 +202,8 @@ public class MeasuredText implements Spanned { mMeasuredParagraphs = measuredTexts; mParagraphBreakPoints = paragraphBreakPoints; mTextDir = textDir; mBreakStrategy = breakStrategy; mHyphenationFrequency = frequency; } /** Loading Loading @@ -190,6 +269,20 @@ public class MeasuredText implements Spanned { return mMeasuredParagraphs[paraIndex]; } /** * Returns the break strategy for this text. */ public @Layout.BreakStrategy int getBreakStrategy() { return mBreakStrategy; } /** * Returns the hyphenation frequency for this text. */ public @Layout.HyphenationFrequency int getHyphenationFrequency() { return mHyphenationFrequency; } /////////////////////////////////////////////////////////////////////////////////////////////// // Spanned overrides // Loading
core/java/android/text/StaticLayout.java +24 −10 Original line number Diff line number Diff line Loading @@ -653,27 +653,41 @@ public class StaticLayout extends Layout { MeasuredText measured = null; final Spanned spanned; final boolean canUseMeasuredText; if (source instanceof MeasuredText) { measured = (MeasuredText) source; final CharSequence original = measured.getText(); spanned = (original instanceof Spanned) ? (Spanned) original : null; if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) { // The buffer position has changed. Re-measure here. measured = MeasuredText.build(original, paint, textDir, bufStart, bufEnd); canUseMeasuredText = false; } else if (b.mBreakStrategy != measured.getBreakStrategy() || b.mHyphenationFrequency != measured.getHyphenationFrequency()) { // The computed hyphenation pieces may not be able to used. Re-measure it. canUseMeasuredText = false; } else { // We can use measured information. canUseMeasuredText = true; } } else { canUseMeasuredText = false; } // Overwrite with the one when emeasured. if (!canUseMeasuredText) { measured = new MeasuredText.Builder(source, paint) .setRange(bufStart, bufEnd) .setTextDirection(textDir) .setBreakStrategy(b.mBreakStrategy) .setHyphenationFrequency(b.mHyphenationFrequency) .build(); spanned = (source instanceof Spanned) ? (Spanned) source : null; } else { final CharSequence original = measured.getText(); spanned = (original instanceof Spanned) ? (Spanned) original : null; // Overwrite with the one when measured. // TODO: Give an option for developer not to overwrite and measure again here? textDir = measured.getTextDir(); paint = measured.getPaint(); } } else { measured = MeasuredText.build(source, paint, textDir, bufStart, bufEnd); spanned = (source instanceof Spanned) ? (Spanned) source : null; } try { for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) { Loading