diff options
| author | TreeHugger Robot <treehugger-gerrit@google.com> | 2018-01-18 20:32:54 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-01-18 20:32:54 +0000 |
| commit | 881a432d0802b67d4ea4fbf65b75bd2d0a066b09 (patch) | |
| tree | eed02f87a8f36d2f19d826b9f7486dc3ab908a09 /core/java/android | |
| parent | 67462296b1a18fc7aa65e0b7ae0e973e49e0a3ae (diff) | |
| parent | 87b1547c929190c77b6b2d779f1d992691f04d17 (diff) | |
Merge "Compute hyphenated word pieces in MeasuredText"
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/text/MeasuredParagraph.java | 10 | ||||
| -rw-r--r-- | core/java/android/text/MeasuredText.java | 197 | ||||
| -rw-r--r-- | core/java/android/text/StaticLayout.java | 34 |
3 files changed, 176 insertions, 65 deletions
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java index c93e0365d58d..c7d4a4ac69aa 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -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); @@ -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); } @@ -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); } @@ -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); diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index 2c30360b81bc..b96b48954ee5 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -52,70 +52,147 @@ public class MeasuredText implements Spanned { // The sorted paragraph end offsets. private final @NonNull int[] mParagraphBreakPoints; - /** - * Build MeasuredText from the text. - * - * @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()); - } + // 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 specific range of the text.. - * - * @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. + * A Builder for MeasuredText */ - public static @NonNull MeasuredText build(@NonNull CharSequence text, - @NonNull TextPaint paint, - @NonNull TextDirectionHeuristic textDir, - @IntRange(from = 0) int start, - @IntRange(from = 0) int end) { - Preconditions.checkNotNull(text); - Preconditions.checkNotNull(paint); - Preconditions.checkNotNull(textDir); - Preconditions.checkArgumentInRange(start, 0, text.length(), "start"); - Preconditions.checkArgumentInRange(end, 0, text.length(), "end"); - - 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); - if (paraEnd < 0) { - // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end. - paraEnd = end; - } else { - paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. - } + 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. + */ + public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) { + Preconditions.checkNotNull(text); + Preconditions.checkNotNull(paint); + + mText = text; + mPaint = paint; + mStart = 0; + mEnd = text.length(); + } - paragraphEnds.add(paraEnd); - measuredTexts.add(MeasuredParagraph.buildForStaticLayout( - paint, text, paraStart, paraEnd, textDir, null /* no recycle */)); + /** + * Set the range of measuring target. + * + * @param start The measuring target start offset in the text. + * @param end The measuring target end offset in the text. + */ + public @NonNull Builder setRange(@IntRange(from = 0) int start, + @IntRange(from = 0) int end) { + 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; } - return new MeasuredText(text, start, end, paint, textDir, - measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]), - paragraphEnds.toArray()); - } + /** + * 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); + 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; + } - // Use MeasuredText.build instead. + /** + * 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 = 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 = mEnd; + } else { + paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. + } + + paragraphEnds.add(paraEnd); + measuredTexts.add(MeasuredParagraph.buildForStaticLayout( + mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation, + null /* no recycle */)); + } + + return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy, + mHyphenationFrequency, measuredTexts.toArray( + new MeasuredParagraph[measuredTexts.size()]), + paragraphEnds.toArray()); + } + }; + + // 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; @@ -125,6 +202,8 @@ public class MeasuredText implements Spanned { mMeasuredParagraphs = measuredTexts; mParagraphBreakPoints = paragraphBreakPoints; mTextDir = textDir; + mBreakStrategy = breakStrategy; + mHyphenationFrequency = frequency; } /** @@ -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 // diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 36bec863e7ac..70d648657d9d 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -653,26 +653,40 @@ 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. - - // Overwrite with the one when emeasured. - // TODO: Give an option for developer not to overwrite and measure again here? - textDir = measured.getTextDir(); - paint = measured.getPaint(); + canUseMeasuredText = true; } } else { - measured = MeasuredText.build(source, paint, textDir, bufStart, bufEnd); + canUseMeasuredText = false; + } + + 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(); } try { |
