1beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka/* 2beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Copyright (C) 2017 The Android Open Source Project 3beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 4beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Licensed under the Apache License, Version 2.0 (the "License"); 5beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * you may not use this file except in compliance with the License. 6beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * You may obtain a copy of the License at 7beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 8beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * http://www.apache.org/licenses/LICENSE-2.0 9beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 10beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Unless required by applicable law or agreed to in writing, software 11beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * distributed under the License is distributed on an "AS IS" BASIS, 12beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * See the License for the specific language governing permissions and 14beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * limitations under the License. 15beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 16beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 17beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonakapackage android.text; 18beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 19151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonakaimport android.annotation.FloatRange; 20beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonakaimport android.annotation.IntRange; 21beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonakaimport android.annotation.NonNull; 22beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonakaimport android.annotation.Nullable; 23a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonakaimport android.graphics.Rect; 24a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonakaimport android.text.style.MetricAffectingSpan; 25beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 26beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonakaimport com.android.internal.util.Preconditions; 27beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 28beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonakaimport java.util.ArrayList; 29beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonakaimport java.util.Objects; 30beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 31beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka/** 32beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * A text which has the character metrics data. 33beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 34beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * A text object that contains the character metrics data and can be used to improve the performance 35beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * of text layout operations. When a PrecomputedText is created with a given {@link CharSequence}, 36beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * it will measure the text metrics during the creation. This PrecomputedText instance can be set on 37beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * {@link android.widget.TextView} or {@link StaticLayout}. Since the text layout information will 38beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * be included in this instance, {@link android.widget.TextView} or {@link StaticLayout} will not 39beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * have to recalculate this information. 40beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 41beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Note that the {@link PrecomputedText} created from different parameters of the target {@link 42beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * android.widget.TextView} will be rejected internally and compute the text layout again with the 43beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * current {@link android.widget.TextView} parameters. 44beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 45beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * <pre> 46beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * An example usage is: 47beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * <code> 48151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * static void asyncSetText(TextView textView, final String longString, Executor bgExecutor) { 49beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * // construct precompute related parameters using the TextView that we will set the text on. 50151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * final PrecomputedText.Params params = textView.getTextMetricsParams(); 51151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * final Reference textViewRef = new WeakReference<>(textView); 52151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * bgExecutor.submit(() -> { 53151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * TextView textView = textViewRef.get(); 54151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * if (textView == null) return; 55151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * final PrecomputedText precomputedText = PrecomputedText.create(longString, params); 56beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * textView.post(() -> { 57151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * TextView textView = textViewRef.get(); 58151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * if (textView == null) return; 59beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * textView.setText(precomputedText); 60beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * }); 61beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * }); 62beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * } 63beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * </code> 64beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * </pre> 65beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 66beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Note that the {@link PrecomputedText} created from different parameters of the target 67a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka * {@link android.widget.TextView} will be rejected. 68a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka * 69a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka * Note that any {@link android.text.NoCopySpan} attached to the original text won't be passed to 70a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka * PrecomputedText. 71beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 72a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonakapublic class PrecomputedText implements Spannable { 73beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private static final char LINE_FEED = '\n'; 74beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 75beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 76beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * The information required for building {@link PrecomputedText}. 77beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 78beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Contains information required for precomputing text measurement metadata, so it can be done 79beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * in isolation of a {@link android.widget.TextView} or {@link StaticLayout}, when final layout 80beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * constraints are not known. 81beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 82beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public static final class Params { 83beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The TextPaint used for measurement. 84beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private final @NonNull TextPaint mPaint; 85beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 86beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The requested text direction. 87beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private final @NonNull TextDirectionHeuristic mTextDir; 88beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 89beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The break strategy for this measured text. 90beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private final @Layout.BreakStrategy int mBreakStrategy; 91beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 92beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The hyphenation frequency for this measured text. 93beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private final @Layout.HyphenationFrequency int mHyphenationFrequency; 94beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 95beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 96beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * A builder for creating {@link Params}. 97beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 98beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public static class Builder { 99beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The TextPaint used for measurement. 100beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private final @NonNull TextPaint mPaint; 101beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 102beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The requested text direction. 103beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; 104beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 105beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The break strategy for this measured text. 106beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; 107beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 108beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The hyphenation frequency for this measured text. 109beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private @Layout.HyphenationFrequency int mHyphenationFrequency = 110beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka Layout.HYPHENATION_FREQUENCY_NORMAL; 111beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 112beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 113beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Builder constructor. 114beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 115beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @param paint the paint to be used for drawing 116beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 117beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public Builder(@NonNull TextPaint paint) { 118beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mPaint = paint; 119beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 120beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 121beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 122beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Set the line break strategy. 123beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 124beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}. 125beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 126beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @param strategy the break strategy 127beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return this builder, useful for chaining 128beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @see StaticLayout.Builder#setBreakStrategy 129beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @see android.widget.TextView#setBreakStrategy 130beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 131beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public Builder setBreakStrategy(@Layout.BreakStrategy int strategy) { 132beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mBreakStrategy = strategy; 133beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return this; 134beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 135beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 136beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 137beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Set the hyphenation frequency. 138beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 139beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. 140beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 141beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @param frequency the hyphenation frequency 142beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return this builder, useful for chaining 143beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @see StaticLayout.Builder#setHyphenationFrequency 144beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @see android.widget.TextView#setHyphenationFrequency 145beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 146beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public Builder setHyphenationFrequency(@Layout.HyphenationFrequency int frequency) { 147beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mHyphenationFrequency = frequency; 148beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return this; 149beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 150beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 151beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 152beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Set the text direction heuristic. 153beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 154beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}. 155beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 156beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @param textDir the text direction heuristic for resolving bidi behavior 157beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return this builder, useful for chaining 158beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @see StaticLayout.Builder#setTextDirection 159beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 160beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) { 161beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mTextDir = textDir; 162beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return this; 163beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 164beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 165beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 166beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Build the {@link Params}. 167beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 168beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return the layout parameter 169beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 170beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @NonNull Params build() { 171beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return new Params(mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency); 172beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 173beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 174beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 175beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // This is public hidden for internal use. 176beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // For the external developers, use Builder instead. 177beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** @hide */ 178beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public Params(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, 179beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) { 180beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mPaint = paint; 181beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mTextDir = textDir; 182beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mBreakStrategy = strategy; 183beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mHyphenationFrequency = frequency; 184beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 185beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 186beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 187beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the {@link TextPaint} for this text. 188beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 189beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return A {@link TextPaint} 190beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 191beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @NonNull TextPaint getTextPaint() { 192beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mPaint; 193beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 194beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 195beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 196beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the {@link TextDirectionHeuristic} for this text. 197beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 198beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return A {@link TextDirectionHeuristic} 199beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 200beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @NonNull TextDirectionHeuristic getTextDirection() { 201beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mTextDir; 202beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 203beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 204beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 205beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the break strategy for this text. 206beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 207beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return A line break strategy 208beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 209beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @Layout.BreakStrategy int getBreakStrategy() { 210beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mBreakStrategy; 211beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 212beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 213beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 214beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the hyphenation frequency for this text. 215beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 216beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return A hyphenation frequency 217beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 218beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @Layout.HyphenationFrequency int getHyphenationFrequency() { 219beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mHyphenationFrequency; 220beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 221beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 222e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka /** @hide */ 223e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka public boolean isSameTextMetricsInternal(@NonNull TextPaint paint, 224beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy, 225beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Layout.HyphenationFrequency int frequency) { 226beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mTextDir == textDir 227beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka && mBreakStrategy == strategy 228beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka && mHyphenationFrequency == frequency 229beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka && mPaint.equalsForTextMeasurement(paint); 230beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 231beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 232beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 233beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Check if the same text layout. 234beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 235beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return true if this and the given param result in the same text layout 236beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 237beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 238beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public boolean equals(@Nullable Object o) { 239beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka if (o == this) { 240beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return true; 241beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 242beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka if (o == null || !(o instanceof Params)) { 243beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return false; 244beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 245beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka Params param = (Params) o; 246beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return isSameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy, 247beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka param.mHyphenationFrequency); 248beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 249beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 250beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 251beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public int hashCode() { 252beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // TODO: implement MinikinPaint::hashCode and use it to keep consistency with equals. 253beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return Objects.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), mPaint.getTextSkewX(), 254beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(), 255beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mPaint.getTextLocales(), mPaint.getTypeface(), 256beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir, 257beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mBreakStrategy, mHyphenationFrequency); 258beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 259e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka 260e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka @Override 261e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka public String toString() { 262e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka return "{" 263e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + "textSize=" + mPaint.getTextSize() 264e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", textScaleX=" + mPaint.getTextScaleX() 265e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", textSkewX=" + mPaint.getTextSkewX() 266e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", letterSpacing=" + mPaint.getLetterSpacing() 267e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", textLocale=" + mPaint.getTextLocales() 268e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", typeface=" + mPaint.getTypeface() 269e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", variationSettings=" + mPaint.getFontVariationSettings() 270e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", elegantTextHeight=" + mPaint.isElegantTextHeight() 271e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", textDir=" + mTextDir 272e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", breakStrategy=" + mBreakStrategy 273e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + ", hyphenationFrequency=" + mHyphenationFrequency 274e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka + "}"; 275e1ffb54167c8ff78855352a324ff8332a33fc805Seigo Nonaka } 276beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka }; 277beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 278c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka /** @hide */ 279c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka public static class ParagraphInfo { 280c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka public final @IntRange(from = 0) int paragraphEnd; 281c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka public final @NonNull MeasuredParagraph measured; 282c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka 283c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka /** 284c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka * @param paraEnd the end offset of this paragraph 285c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka * @param measured a measured paragraph 286c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka */ 287c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka public ParagraphInfo(@IntRange(from = 0) int paraEnd, @NonNull MeasuredParagraph measured) { 288c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka this.paragraphEnd = paraEnd; 289c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka this.measured = measured; 290c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka } 291c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka }; 292c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka 293c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka 294beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The original text. 295a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka private final @NonNull SpannableString mText; 296beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 297beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The inclusive start offset of the measuring target. 298beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private final @IntRange(from = 0) int mStart; 299beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 300beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // The exclusive end offset of the measuring target. 301beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private final @IntRange(from = 0) int mEnd; 302beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 303beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private final @NonNull Params mParams; 304beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 305c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka // The list of measured paragraph info. 306c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka private final @NonNull ParagraphInfo[] mParagraphInfo; 307beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 308beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 309beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph 310beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * positioning information. 311beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * <p> 312beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * This can be expensive, so computing this on a background thread before your text will be 313beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * presented can save work on the UI thread. 314beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * </p> 315beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 316a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka * Note that any {@link android.text.NoCopySpan} attached to the text won't be passed to the 317a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka * created PrecomputedText. 318a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka * 319beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @param text the text to be measured 320c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka * @param params parameters that define how text will be precomputed 321beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @return A {@link PrecomputedText} 322beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 323c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) { 324c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka ParagraphInfo[] paraInfo = createMeasuredParagraphs( 325c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka text, params, 0, text.length(), true /* computeLayout */); 326c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka return new PrecomputedText(text, 0, text.length(), params, paraInfo); 327beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 328beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 329beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** @hide */ 330c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka public static ParagraphInfo[] createMeasuredParagraphs( 331c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka @NonNull CharSequence text, @NonNull Params params, 332beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) { 333c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka ArrayList<ParagraphInfo> result = new ArrayList<>(); 334beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 335c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka Preconditions.checkNotNull(text); 336c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka Preconditions.checkNotNull(params); 337c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE 338c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; 339beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 340beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka int paraEnd = 0; 341beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka for (int paraStart = start; paraStart < end; paraStart = paraEnd) { 342beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end); 343beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka if (paraEnd < 0) { 344beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph 345beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // end. 346beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka paraEnd = end; 347beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } else { 348beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. 349beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 350beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 351c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout( 352c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(), 353c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka needHyphenation, computeLayout, null /* no recycle */))); 354beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 355c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka return result.toArray(new ParagraphInfo[result.size()]); 356beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 357beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 358beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // Use PrecomputedText.create instead. 359beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start, 360c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka @IntRange(from = 0) int end, @NonNull Params params, 361c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka @NonNull ParagraphInfo[] paraInfo) { 362a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka mText = new SpannableString(text, true /* ignoreNoCopySpan */); 363beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mStart = start; 364beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka mEnd = end; 365c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka mParams = params; 366c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka mParagraphInfo = paraInfo; 367beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 368beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 369beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 370beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Return the underlying text. 371151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * @hide 372beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 373beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @NonNull CharSequence getText() { 374beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mText; 375beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 376beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 377beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 378beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the inclusive start offset of measured region. 379beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @hide 380beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 381beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @IntRange(from = 0) int getStart() { 382beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mStart; 383beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 384beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 385beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 386beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the exclusive end offset of measured region. 387beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @hide 388beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 389beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @IntRange(from = 0) int getEnd() { 390beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mEnd; 391beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 392beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 393beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 394beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the layout parameters used to measure this text. 395beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 396beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @NonNull Params getParams() { 397beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mParams; 398beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 399beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 400beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 401beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the count of paragraphs. 402beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 403beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @IntRange(from = 0) int getParagraphCount() { 404c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka return mParagraphInfo.length; 405beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 406beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 407beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 408beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the paragraph start offset of the text. 409beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 410beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) { 411beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); 412c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka return paraIndex == 0 ? mStart : getParagraphEnd(paraIndex - 1); 413beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 414beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 415beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 416beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the paragraph end offset of the text. 417beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 418beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) { 419beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); 420c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka return mParagraphInfo[paraIndex].paragraphEnd; 421beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 422beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 423beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** @hide */ 424beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) { 425c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka return mParagraphInfo[paraIndex].measured; 426c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka } 427c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka 428c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka /** @hide */ 429c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka public @NonNull ParagraphInfo[] getParagraphInfo() { 430c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka return mParagraphInfo; 431beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 432beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 433beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 434beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns true if the given TextPaint gives the same result of text layout for this text. 435beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @hide 436beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 437beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end, 438beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint, 439beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) { 440beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka final TextPaint mtPaint = mParams.getTextPaint(); 441beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mStart == start 442beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka && mEnd == end 443beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka && mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency); 444beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 445beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 446beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** @hide */ 447beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public int findParaIndex(@IntRange(from = 0) int pos) { 448beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring 449beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // layout support to StaticLayout. 450c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka for (int i = 0; i < mParagraphInfo.length; ++i) { 451c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka if (pos < mParagraphInfo[i].paragraphEnd) { 452beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return i; 453beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 454beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 455beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka throw new IndexOutOfBoundsException( 456c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka "pos must be less than " + mParagraphInfo[mParagraphInfo.length - 1].paragraphEnd 457beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka + ", gave " + pos); 458beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 459beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 460151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka /** 461151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * Returns text width for the given range. 462151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * Both {@code start} and {@code end} offset need to be in the same paragraph, otherwise 463151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * IllegalArgumentException will be thrown. 464151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * 465151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * @param start the inclusive start offset in the text 466151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * @param end the exclusive end offset in the text 467151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * @return the text width 468151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * @throws IllegalArgumentException if start and end offset are in the different paragraph. 469151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka */ 470151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka public @FloatRange(from = 0) float getWidth(@IntRange(from = 0) int start, 471151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka @IntRange(from = 0) int end) { 472151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka Preconditions.checkArgument(0 <= start && start <= mText.length(), "invalid start offset"); 473151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka Preconditions.checkArgument(0 <= end && end <= mText.length(), "invalid end offset"); 474151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka Preconditions.checkArgument(start <= end, "start offset can not be larger than end offset"); 475151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka 476151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka if (start == end) { 477151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka return 0; 478151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka } 479beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka final int paraIndex = findParaIndex(start); 480beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka final int paraStart = getParagraphStart(paraIndex); 481beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka final int paraEnd = getParagraphEnd(paraIndex); 482beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka if (start < paraStart || paraEnd < end) { 483151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka throw new IllegalArgumentException("Cannot measured across the paragraph:" 484beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka + "para: (" + paraStart + ", " + paraEnd + "), " 485beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka + "request: (" + start + ", " + end + ")"); 486beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 487beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart); 488beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 489beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 490151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka /** 491151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * Retrieves the text bounding box for the given range. 492151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * Both {@code start} and {@code end} offset need to be in the same paragraph, otherwise 493151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * IllegalArgumentException will be thrown. 494151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * 495151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * @param start the inclusive start offset in the text 496151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * @param end the exclusive end offset in the text 497151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * @param bounds the output rectangle 498151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka * @throws IllegalArgumentException if start and end offset are in the different paragraph. 499151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka */ 500a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end, 501a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka @NonNull Rect bounds) { 502151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka Preconditions.checkArgument(0 <= start && start <= mText.length(), "invalid start offset"); 503151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka Preconditions.checkArgument(0 <= end && end <= mText.length(), "invalid end offset"); 504151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka Preconditions.checkArgument(start <= end, "start offset can not be larger than end offset"); 505151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka Preconditions.checkNotNull(bounds); 506151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka if (start == end) { 507151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka bounds.set(0, 0, 0, 0); 508151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka return; 509151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka } 510a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka final int paraIndex = findParaIndex(start); 511a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka final int paraStart = getParagraphStart(paraIndex); 512a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka final int paraEnd = getParagraphEnd(paraIndex); 513a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka if (start < paraStart || paraEnd < end) { 514151108a2c66dd55663bea9e3f686793418d5f047Seigo Nonaka throw new IllegalArgumentException("Cannot measured across the paragraph:" 515a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka + "para: (" + paraStart + ", " + paraEnd + "), " 516a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka + "request: (" + start + ", " + end + ")"); 517a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka } 518fb0abe1feb2cadcee2a3edb2028e1da04bb8a8d5Seigo Nonaka getMeasuredParagraph(paraIndex).getBounds(start - paraStart, end - paraStart, bounds); 519a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka } 520a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka 521beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /** 522beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Returns the size of native PrecomputedText memory usage. 523beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * 524beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * Note that this is not guaranteed to be accurate. Must be used only for testing purposes. 525beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka * @hide 526beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka */ 527beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public int getMemoryUsage() { 528beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka int r = 0; 529beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka for (int i = 0; i < getParagraphCount(); ++i) { 530beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka r += getMeasuredParagraph(i).getMemoryUsage(); 531beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 532beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return r; 533beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 534beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 535beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /////////////////////////////////////////////////////////////////////////////////////////////// 536a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka // Spannable overrides 537a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka // 538a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka // Do not allow to modify MetricAffectingSpan 539a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka 540a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka /** 541a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified. 542a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka */ 543a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka @Override 544a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka public void setSpan(Object what, int start, int end, int flags) { 545a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka if (what instanceof MetricAffectingSpan) { 546a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka throw new IllegalArgumentException( 547a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka "MetricAffectingSpan can not be set to PrecomputedText."); 548a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka } 549a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka mText.setSpan(what, start, end, flags); 550a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka } 551a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka 552a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka /** 553a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified. 554a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka */ 555a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka @Override 556a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka public void removeSpan(Object what) { 557a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka if (what instanceof MetricAffectingSpan) { 558a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka throw new IllegalArgumentException( 559a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka "MetricAffectingSpan can not be removed from PrecomputedText."); 560a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka } 561a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka mText.removeSpan(what); 562a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka } 563a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka 564a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka /////////////////////////////////////////////////////////////////////////////////////////////// 565beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // Spanned overrides 566beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // 567beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // Just proxy for underlying mText if appropriate. 568beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 569beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 570beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public <T> T[] getSpans(int start, int end, Class<T> type) { 571beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mText.getSpans(start, end, type); 572beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 573beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 574beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 575beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public int getSpanStart(Object tag) { 576beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mText.getSpanStart(tag); 577beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 578beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 579beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 580beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public int getSpanEnd(Object tag) { 581beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mText.getSpanEnd(tag); 582beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 583beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 584beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 585beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public int getSpanFlags(Object tag) { 586beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mText.getSpanFlags(tag); 587beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 588beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 589beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 590beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public int nextSpanTransition(int start, int limit, Class type) { 591beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mText.nextSpanTransition(start, limit, type); 592beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 593beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 594beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka /////////////////////////////////////////////////////////////////////////////////////////////// 595beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // CharSequence overrides. 596beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // 597beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka // Just proxy for underlying mText. 598beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 599beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 600beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public int length() { 601beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mText.length(); 602beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 603beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 604beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 605beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public char charAt(int index) { 606beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mText.charAt(index); 607beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 608beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 609beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 610beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public CharSequence subSequence(int start, int end) { 611beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return PrecomputedText.create(mText.subSequence(start, end), mParams); 612beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 613beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka 614beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka @Override 615beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka public String toString() { 616beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka return mText.toString(); 617beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka } 618beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka} 619