diff options
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/text/MeasuredParagraph.java | 15 | ||||
| -rw-r--r-- | core/java/android/text/PrecomputedText.java | 121 | ||||
| -rw-r--r-- | core/java/android/text/StaticLayout.java | 24 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 14 |
4 files changed, 148 insertions, 26 deletions
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java index f9370a8aa6af..7e41878b3d4e 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -377,6 +377,9 @@ public class MeasuredParagraph { * @param start the inclusive start offset of the target region in the text * @param end the exclusive end offset of the target region in the text * @param textDir the text direction + * @param computeHyphenation true if need to compute hyphenation, otherwise false + * @param computeLayout true if need to compute full layout, otherwise false. + * @param hint pass if you already have measured paragraph. * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * @return measured text @@ -389,12 +392,18 @@ public class MeasuredParagraph { @NonNull TextDirectionHeuristic textDir, boolean computeHyphenation, boolean computeLayout, + @Nullable MeasuredParagraph hint, @Nullable MeasuredParagraph recycle) { final MeasuredParagraph mt = recycle == null ? obtain() : recycle; mt.resetAndAnalyzeBidi(text, start, end, textDir); - final MeasuredText.Builder builder = new MeasuredText.Builder(mt.mCopiedBuffer); - builder.setComputeHyphenation(computeHyphenation); - builder.setComputeLayout(computeLayout); + final MeasuredText.Builder builder; + if (hint == null) { + builder = new MeasuredText.Builder(mt.mCopiedBuffer) + .setComputeHyphenation(computeHyphenation) + .setComputeLayout(computeLayout); + } else { + builder = new MeasuredText.Builder(hint.mMeasuredText); + } if (mt.mTextLength == 0) { // Need to build empty native measured text for StaticLayout. // TODO: Stop creating empty measured text for empty lines. diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java index b7ea0122cb06..08741d6a7d88 100644 --- a/core/java/android/text/PrecomputedText.java +++ b/core/java/android/text/PrecomputedText.java @@ -17,6 +17,7 @@ package android.text; import android.annotation.FloatRange; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -25,6 +26,8 @@ import android.text.style.MetricAffectingSpan; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Objects; @@ -119,6 +122,16 @@ public class PrecomputedText implements Spannable { } /** + * Builder constructor from existing params. + */ + public Builder(@NonNull Params params) { + mPaint = params.mPaint; + mTextDir = params.mTextDir; + mBreakStrategy = params.mBreakStrategy; + mHyphenationFrequency = params.mHyphenationFrequency; + } + + /** * Set the line break strategy. * * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}. @@ -220,13 +233,41 @@ public class PrecomputedText implements Spannable { } /** @hide */ - public boolean isSameTextMetricsInternal(@NonNull TextPaint paint, + @IntDef(value = { UNUSABLE, NEED_RECOMPUTE, USABLE }) + @Retention(RetentionPolicy.SOURCE) + public @interface CheckResultUsableResult {} + + /** + * Constant for returning value of checkResultUsable indicating that given parameter is not + * compatible. + * @hide + */ + public static final int UNUSABLE = 0; + + /** + * Constant for returning value of checkResultUsable indicating that given parameter is not + * compatible but partially usable for creating new PrecomputedText. + * @hide + */ + public static final int NEED_RECOMPUTE = 1; + + /** + * Constant for returning value of checkResultUsable indicating that given parameter is + * compatible. + * @hide + */ + public static final int USABLE = 2; + + /** @hide */ + public @CheckResultUsableResult int checkResultUsable(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) { - return mTextDir == textDir - && mBreakStrategy == strategy - && mHyphenationFrequency == frequency - && mPaint.equalsForTextMeasurement(paint); + if (mBreakStrategy == strategy && mHyphenationFrequency == frequency + && mPaint.equalsForTextMeasurement(paint)) { + return mTextDir == textDir ? USABLE : NEED_RECOMPUTE; + } else { + return UNUSABLE; + } } /** @@ -243,8 +284,8 @@ public class PrecomputedText implements Spannable { return false; } Params param = (Params) o; - return isSameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy, - param.mHyphenationFrequency); + return checkResultUsable(param.mPaint, param.mTextDir, param.mBreakStrategy, + param.mHyphenationFrequency) == Params.USABLE; } @Override @@ -321,11 +362,55 @@ public class PrecomputedText implements Spannable { * @return A {@link PrecomputedText} */ public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) { - ParagraphInfo[] paraInfo = createMeasuredParagraphs( - text, params, 0, text.length(), true /* computeLayout */); + ParagraphInfo[] paraInfo = null; + if (text instanceof PrecomputedText) { + final PrecomputedText hintPct = (PrecomputedText) text; + final PrecomputedText.Params hintParams = hintPct.getParams(); + final @Params.CheckResultUsableResult int checkResult = + hintParams.checkResultUsable(params.mPaint, params.mTextDir, + params.mBreakStrategy, params.mHyphenationFrequency); + switch (checkResult) { + case Params.USABLE: + return hintPct; + case Params.NEED_RECOMPUTE: + // To be able to use PrecomputedText for new params, at least break strategy and + // hyphenation frequency must be the same. + if (params.getBreakStrategy() == hintParams.getBreakStrategy() + && params.getHyphenationFrequency() + == hintParams.getHyphenationFrequency()) { + paraInfo = createMeasuredParagraphsFromPrecomputedText( + hintPct, params, true /* compute layout */); + } + break; + case Params.UNUSABLE: + // Unable to use anything in PrecomputedText. Create PrecomputedText as the + // normal text input. + } + + } + if (paraInfo == null) { + paraInfo = createMeasuredParagraphs( + text, params, 0, text.length(), true /* computeLayout */); + } return new PrecomputedText(text, 0, text.length(), params, paraInfo); } + private static ParagraphInfo[] createMeasuredParagraphsFromPrecomputedText( + @NonNull PrecomputedText pct, @NonNull Params params, boolean computeLayout) { + final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE + && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; + ArrayList<ParagraphInfo> result = new ArrayList<>(); + for (int i = 0; i < pct.getParagraphCount(); ++i) { + final int paraStart = pct.getParagraphStart(i); + final int paraEnd = pct.getParagraphEnd(i); + result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout( + params.getTextPaint(), pct, paraStart, paraEnd, params.getTextDirection(), + needHyphenation, computeLayout, pct.getMeasuredParagraph(i), + null /* no recycle */))); + } + return result.toArray(new ParagraphInfo[result.size()]); + } + /** @hide */ public static ParagraphInfo[] createMeasuredParagraphs( @NonNull CharSequence text, @NonNull Params params, @@ -350,7 +435,8 @@ public class PrecomputedText implements Spannable { result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout( params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(), - needHyphenation, computeLayout, null /* no recycle */))); + needHyphenation, computeLayout, null /* no hint */, + null /* no recycle */))); } return result.toArray(new ParagraphInfo[result.size()]); } @@ -434,12 +520,15 @@ public class PrecomputedText implements Spannable { * Returns true if the given TextPaint gives the same result of text layout for this text. * @hide */ - public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end, - @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint, - @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) { - return mStart == start - && mEnd == end - && mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency); + public @Params.CheckResultUsableResult int checkResultUsable(@IntRange(from = 0) int start, + @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir, + @NonNull TextPaint paint, @Layout.BreakStrategy int strategy, + @Layout.HyphenationFrequency int frequency) { + if (mStart != start || mEnd != end) { + return Params.UNUSABLE; + } else { + return mParams.checkResultUsable(paint, textDir, strategy, frequency); + } } /** @hide */ diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 8cb18b255589..3d0c6622d8af 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -650,10 +650,26 @@ public class StaticLayout extends Layout { final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null; if (source instanceof PrecomputedText) { PrecomputedText precomputed = (PrecomputedText) source; - if (precomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint, - b.mBreakStrategy, b.mHyphenationFrequency)) { - // Some parameters are different from the ones when measured text is created. - paragraphInfo = precomputed.getParagraphInfo(); + final @PrecomputedText.Params.CheckResultUsableResult int checkResult = + precomputed.checkResultUsable(bufStart, bufEnd, textDir, paint, + b.mBreakStrategy, b.mHyphenationFrequency); + switch (checkResult) { + case PrecomputedText.Params.UNUSABLE: + break; + case PrecomputedText.Params.NEED_RECOMPUTE: + final PrecomputedText.Params newParams = + new PrecomputedText.Params.Builder(paint) + .setBreakStrategy(b.mBreakStrategy) + .setHyphenationFrequency(b.mHyphenationFrequency) + .setTextDirection(textDir) + .build(); + precomputed = PrecomputedText.create(precomputed, newParams); + paragraphInfo = precomputed.getParagraphInfo(); + break; + case PrecomputedText.Params.USABLE: + // Some parameters are different from the ones when measured text is created. + paragraphInfo = precomputed.getParagraphInfo(); + break; } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 522be81091fb..90da81276ba3 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -6028,14 +6028,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mTextDir == null) { mTextDir = getTextDirectionHeuristic(); } - if (!precomputed.getParams().isSameTextMetricsInternal( - getPaint(), mTextDir, mBreakStrategy, mHyphenationFrequency)) { - throw new IllegalArgumentException( + final @PrecomputedText.Params.CheckResultUsableResult int checkResult = + precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy, + mHyphenationFrequency); + switch (checkResult) { + case PrecomputedText.Params.UNUSABLE: + throw new IllegalArgumentException( "PrecomputedText's Parameters don't match the parameters of this TextView." + "Consider using setTextMetricsParams(precomputedText.getParams()) " + "to override the settings of this TextView: " + "PrecomputedText: " + precomputed.getParams() + "TextView: " + getTextMetricsParams()); + case PrecomputedText.Params.NEED_RECOMPUTE: + precomputed = PrecomputedText.create(precomputed, getTextMetricsParams()); + break; + case PrecomputedText.Params.USABLE: + // pass through } } else if (type == BufferType.SPANNABLE || mMovement != null) { text = mSpannableFactory.newSpannable(text); |
