1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text; 18 19 import android.annotation.IntRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.graphics.Canvas; 24 import android.graphics.Paint; 25 import android.graphics.Path; 26 import android.text.style.ParagraphStyle; 27 28 /** 29 * A BoringLayout is a very simple Layout implementation for text that 30 * fits on a single line and is all left-to-right characters. 31 * You will probably never want to make one of these yourself; 32 * if you do, be sure to call {@link #isBoring} first to make sure 33 * the text meets the criteria. 34 * <p>This class is used by widgets to control text layout. You should not need 35 * to use this class directly unless you are implementing your own widget 36 * or custom display object, in which case 37 * you are encouraged to use a Layout instead of calling 38 * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint) 39 * Canvas.drawText()} directly.</p> 40 */ 41 public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback { 42 43 /** 44 * Utility function to construct a BoringLayout instance. 45 * 46 * @param source the text to render 47 * @param paint the default paint for the layout 48 * @param outerWidth the wrapping width for the text 49 * @param align whether to left, right, or center the text 50 * @param spacingMult this value is no longer used by BoringLayout 51 * @param spacingAdd this value is no longer used by BoringLayout 52 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and 53 * line width 54 * @param includePad set whether to include extra space beyond font ascent and descent which is 55 * needed to avoid clipping in some scripts 56 */ make(CharSequence source, TextPaint paint, int outerWidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad)57 public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth, 58 Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, 59 boolean includePad) { 60 return new BoringLayout(source, paint, outerWidth, align, spacingMult, spacingAdd, metrics, 61 includePad); 62 } 63 64 /** 65 * Utility function to construct a BoringLayout instance. 66 * 67 * @param source the text to render 68 * @param paint the default paint for the layout 69 * @param outerWidth the wrapping width for the text 70 * @param align whether to left, right, or center the text 71 * @param spacingmult this value is no longer used by BoringLayout 72 * @param spacingadd this value is no longer used by BoringLayout 73 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and 74 * line width 75 * @param includePad set whether to include extra space beyond font ascent and descent which is 76 * needed to avoid clipping in some scripts 77 * @param ellipsize whether to ellipsize the text if width of the text is longer than the 78 * requested width 79 * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is 80 * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is 81 * not used, {@code outerWidth} is used instead 82 */ make(CharSequence source, TextPaint paint, int outerWidth, Alignment align, float spacingmult, float spacingadd, BoringLayout.Metrics metrics, boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth)83 public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth, 84 Alignment align, float spacingmult, float spacingadd, BoringLayout.Metrics metrics, 85 boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { 86 return new BoringLayout(source, paint, outerWidth, align, spacingmult, spacingadd, metrics, 87 includePad, ellipsize, ellipsizedWidth); 88 } 89 90 /** 91 * Utility function to construct a BoringLayout instance. 92 * 93 * The spacing multiplier and additional amount spacing are not used by BoringLayout. 94 * {@link Layout#getSpacingMultiplier()} will return 1.0 and {@link Layout#getSpacingAdd()} will 95 * return 0.0. 96 * 97 * @param source the text to render 98 * @param paint the default paint for the layout 99 * @param outerWidth the wrapping width for the text 100 * @param align whether to left, right, or center the text 101 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and 102 * line width 103 * @param includePad set whether to include extra space beyond font ascent and descent which is 104 * needed to avoid clipping in some scripts 105 * @param ellipsize whether to ellipsize the text if width of the text is longer than the 106 * requested width. null if ellipsis is not applied. 107 * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is 108 * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is 109 * not used, {@code outerWidth} is used instead 110 * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts. 111 * False for keeping the first font's line height. If some glyphs 112 * requires larger vertical spaces, by passing true to this 113 * argument, the layout increase the line height to fit all glyphs. 114 */ make( @onNull CharSequence source, @NonNull TextPaint paint, @IntRange(from = 0) int outerWidth, @NonNull Alignment align, @NonNull BoringLayout.Metrics metrics, boolean includePad, @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth, boolean useFallbackLineSpacing)115 public static @NonNull BoringLayout make( 116 @NonNull CharSequence source, @NonNull TextPaint paint, 117 @IntRange(from = 0) int outerWidth, 118 @NonNull Alignment align, @NonNull BoringLayout.Metrics metrics, 119 boolean includePad, @Nullable TextUtils.TruncateAt ellipsize, 120 @IntRange(from = 0) int ellipsizedWidth, boolean useFallbackLineSpacing) { 121 return new BoringLayout(source, paint, outerWidth, align, 1f, 0f, metrics, includePad, 122 ellipsize, ellipsizedWidth, useFallbackLineSpacing); 123 } 124 125 /** 126 * Returns a BoringLayout for the specified text, potentially reusing 127 * this one if it is already suitable. The caller must make sure that 128 * no one is still using this Layout. 129 * 130 * @param source the text to render 131 * @param paint the default paint for the layout 132 * @param outerwidth the wrapping width for the text 133 * @param align whether to left, right, or center the text 134 * @param spacingMult this value is no longer used by BoringLayout 135 * @param spacingAdd this value is no longer used by BoringLayout 136 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and 137 * line width 138 * @param includePad set whether to include extra space beyond font ascent and descent which is 139 * needed to avoid clipping in some scripts 140 */ replaceOrMake(CharSequence source, TextPaint paint, int outerwidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad)141 public BoringLayout replaceOrMake(CharSequence source, TextPaint paint, int outerwidth, 142 Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, 143 boolean includePad) { 144 replaceWith(source, paint, outerwidth, align, spacingMult, spacingAdd); 145 146 mEllipsizedWidth = outerwidth; 147 mEllipsizedStart = 0; 148 mEllipsizedCount = 0; 149 mUseFallbackLineSpacing = false; 150 151 init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */); 152 return this; 153 } 154 155 /** 156 * Returns a BoringLayout for the specified text, potentially reusing 157 * this one if it is already suitable. The caller must make sure that 158 * no one is still using this Layout. 159 * 160 * The spacing multiplier and additional amount spacing are not used by BoringLayout. 161 * {@link Layout#getSpacingMultiplier()} will return 1.0 and {@link Layout#getSpacingAdd()} will 162 * return 0.0. 163 * 164 * @param source the text to render 165 * @param paint the default paint for the layout 166 * @param outerWidth the wrapping width for the text 167 * @param align whether to left, right, or center the text 168 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and 169 * line width 170 * @param includePad set whether to include extra space beyond font ascent and descent which is 171 * needed to avoid clipping in some scripts 172 * @param ellipsize whether to ellipsize the text if width of the text is longer than the 173 * requested width. null if ellipsis not applied. 174 * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is 175 * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is 176 * not used, {@code outerWidth} is used instead 177 * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts. 178 * False for keeping the first font's line height. If some glyphs 179 * requires larger vertical spaces, by passing true to this 180 * argument, the layout increase the line height to fit all glyphs. 181 */ replaceOrMake(@onNull CharSequence source, @NonNull TextPaint paint, @IntRange(from = 0) int outerWidth, @NonNull Alignment align, @NonNull BoringLayout.Metrics metrics, boolean includePad, @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth, boolean useFallbackLineSpacing)182 public @NonNull BoringLayout replaceOrMake(@NonNull CharSequence source, 183 @NonNull TextPaint paint, @IntRange(from = 0) int outerWidth, 184 @NonNull Alignment align, @NonNull BoringLayout.Metrics metrics, boolean includePad, 185 @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth, 186 boolean useFallbackLineSpacing) { 187 boolean trust; 188 189 if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) { 190 replaceWith(source, paint, outerWidth, align, 1f, 0f); 191 192 mEllipsizedWidth = outerWidth; 193 mEllipsizedStart = 0; 194 mEllipsizedCount = 0; 195 trust = true; 196 } else { 197 replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this), 198 paint, outerWidth, align, 1f, 0f); 199 200 mEllipsizedWidth = ellipsizedWidth; 201 trust = false; 202 } 203 204 mUseFallbackLineSpacing = useFallbackLineSpacing; 205 206 init(getText(), paint, align, metrics, includePad, trust, 207 useFallbackLineSpacing); 208 return this; 209 } 210 211 /** 212 * Returns a BoringLayout for the specified text, potentially reusing 213 * this one if it is already suitable. The caller must make sure that 214 * no one is still using this Layout. 215 * 216 * @param source the text to render 217 * @param paint the default paint for the layout 218 * @param outerWidth the wrapping width for the text 219 * @param align whether to left, right, or center the text 220 * @param spacingMult this value is no longer used by BoringLayout 221 * @param spacingAdd this value is no longer used by BoringLayout 222 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and 223 * line width 224 * @param includePad set whether to include extra space beyond font ascent and descent which is 225 * needed to avoid clipping in some scripts 226 * @param ellipsize whether to ellipsize the text if width of the text is longer than the 227 * requested width 228 * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is 229 * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is 230 * not used, {@code outerWidth} is used instead 231 */ replaceOrMake(CharSequence source, TextPaint paint, int outerWidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth)232 public BoringLayout replaceOrMake(CharSequence source, TextPaint paint, int outerWidth, 233 Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, 234 boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { 235 return replaceOrMake(source, paint, outerWidth, align, metrics, 236 includePad, ellipsize, ellipsizedWidth, false /* useFallbackLineSpacing */); 237 } 238 239 /** 240 * @param source the text to render 241 * @param paint the default paint for the layout 242 * @param outerwidth the wrapping width for the text 243 * @param align whether to left, right, or center the text 244 * @param spacingMult this value is no longer used by BoringLayout 245 * @param spacingAdd this value is no longer used by BoringLayout 246 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and 247 * line width 248 * @param includePad set whether to include extra space beyond font ascent and descent which is 249 * needed to avoid clipping in some scripts 250 */ BoringLayout(CharSequence source, TextPaint paint, int outerwidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad)251 public BoringLayout(CharSequence source, TextPaint paint, int outerwidth, Alignment align, 252 float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad) { 253 super(source, paint, outerwidth, align, spacingMult, spacingAdd); 254 255 mEllipsizedWidth = outerwidth; 256 mEllipsizedStart = 0; 257 mEllipsizedCount = 0; 258 mUseFallbackLineSpacing = false; 259 260 init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */); 261 } 262 263 /** 264 * 265 * @param source the text to render 266 * @param paint the default paint for the layout 267 * @param outerWidth the wrapping width for the text 268 * @param align whether to left, right, or center the text 269 * @param spacingMult this value is no longer used by BoringLayout 270 * @param spacingAdd this value is no longer used by BoringLayout 271 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and 272 * line width 273 * @param includePad set whether to include extra space beyond font ascent and descent which is 274 * needed to avoid clipping in some scripts 275 * @param ellipsize whether to ellipsize the text if width of the text is longer than the 276 * requested {@code outerWidth} 277 * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is 278 * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is 279 * not used, {@code outerWidth} is used instead 280 */ BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth)281 public BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align, 282 float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad, 283 TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { 284 this(source, paint, outerWidth, align, spacingMult, spacingAdd, metrics, includePad, 285 ellipsize, ellipsizedWidth, false /* fallbackLineSpacing */); 286 } 287 288 /** 289 * 290 * @param source the text to render 291 * @param paint the default paint for the layout 292 * @param outerWidth the wrapping width for the text 293 * @param align whether to left, right, or center the text 294 * @param spacingMult this value is no longer used by BoringLayout 295 * @param spacingAdd this value is no longer used by BoringLayout 296 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and 297 * line width 298 * @param includePad set whether to include extra space beyond font ascent and descent which is 299 * needed to avoid clipping in some scripts 300 * @param ellipsize whether to ellipsize the text if width of the text is longer than the 301 * requested {@code outerWidth}. null if ellipsis is not applied. 302 * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is 303 * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is 304 * not used, {@code outerWidth} is used instead 305 * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts. 306 * False for keeping the first font's line height. If some glyphs 307 * requires larger vertical spaces, by passing true to this 308 * argument, the layout increase the line height to fit all glyphs. 309 */ BoringLayout( @onNull CharSequence source, @NonNull TextPaint paint, @IntRange(from = 0) int outerWidth, @NonNull Alignment align, float spacingMult, float spacingAdd, @NonNull BoringLayout.Metrics metrics, boolean includePad, @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth, boolean useFallbackLineSpacing)310 public BoringLayout( 311 @NonNull CharSequence source, @NonNull TextPaint paint, 312 @IntRange(from = 0) int outerWidth, @NonNull Alignment align, float spacingMult, 313 float spacingAdd, @NonNull BoringLayout.Metrics metrics, boolean includePad, 314 @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth, 315 boolean useFallbackLineSpacing) { 316 /* 317 * It is silly to have to call super() and then replaceWith(), 318 * but we can't use "this" for the callback until the call to 319 * super() finishes. 320 */ 321 super(source, paint, outerWidth, align, spacingMult, spacingAdd); 322 323 boolean trust; 324 325 if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) { 326 mEllipsizedWidth = outerWidth; 327 mEllipsizedStart = 0; 328 mEllipsizedCount = 0; 329 trust = true; 330 } else { 331 replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this), 332 paint, outerWidth, align, spacingMult, spacingAdd); 333 334 mEllipsizedWidth = ellipsizedWidth; 335 trust = false; 336 } 337 338 mUseFallbackLineSpacing = useFallbackLineSpacing; 339 init(getText(), paint, align, metrics, includePad, trust, useFallbackLineSpacing); 340 } 341 init(CharSequence source, TextPaint paint, Alignment align, BoringLayout.Metrics metrics, boolean includePad, boolean trustWidth, boolean useFallbackLineSpacing)342 /* package */ void init(CharSequence source, TextPaint paint, Alignment align, 343 BoringLayout.Metrics metrics, boolean includePad, boolean trustWidth, 344 boolean useFallbackLineSpacing) { 345 int spacing; 346 347 if (source instanceof String && align == Layout.Alignment.ALIGN_NORMAL) { 348 mDirect = source.toString(); 349 } else { 350 mDirect = null; 351 } 352 353 mPaint = paint; 354 355 if (includePad) { 356 spacing = metrics.bottom - metrics.top; 357 mDesc = metrics.bottom; 358 } else { 359 spacing = metrics.descent - metrics.ascent; 360 mDesc = metrics.descent; 361 } 362 363 mBottom = spacing; 364 365 if (trustWidth) { 366 mMax = metrics.width; 367 } else { 368 /* 369 * If we have ellipsized, we have to actually calculate the 370 * width because the width that was passed in was for the 371 * full text, not the ellipsized form. 372 */ 373 TextLine line = TextLine.obtain(); 374 line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT, 375 Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null, 376 mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing); 377 mMax = (int) Math.ceil(line.metrics(null)); 378 TextLine.recycle(line); 379 } 380 381 if (includePad) { 382 mTopPadding = metrics.top - metrics.ascent; 383 mBottomPadding = metrics.bottom - metrics.descent; 384 } 385 } 386 387 /** 388 * Determine and compute metrics if given text can be handled by BoringLayout. 389 * 390 * @param text a text 391 * @param paint a paint 392 * @return layout metric for the given text. null if given text is unable to be handled by 393 * BoringLayout. 394 */ isBoring(CharSequence text, TextPaint paint)395 public static Metrics isBoring(CharSequence text, TextPaint paint) { 396 return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null); 397 } 398 399 /** 400 * Determine and compute metrics if given text can be handled by BoringLayout. 401 * 402 * @param text a text 403 * @param paint a paint 404 * @param metrics a metrics object to be recycled. If null is passed, this function creat new 405 * object. 406 * @return layout metric for the given text. If metrics is not null, this method fills values 407 * to given metrics object instead of allocating new metrics object. null if given text 408 * is unable to be handled by BoringLayout. 409 */ isBoring(CharSequence text, TextPaint paint, Metrics metrics)410 public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) { 411 return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics); 412 } 413 414 /** 415 * Returns true if the text contains any RTL characters, bidi format characters, or surrogate 416 * code units. 417 */ hasAnyInterestingChars(CharSequence text, int textLength)418 private static boolean hasAnyInterestingChars(CharSequence text, int textLength) { 419 final int MAX_BUF_LEN = 500; 420 final char[] buffer = TextUtils.obtain(MAX_BUF_LEN); 421 try { 422 for (int start = 0; start < textLength; start += MAX_BUF_LEN) { 423 final int end = Math.min(start + MAX_BUF_LEN, textLength); 424 425 // No need to worry about getting half codepoints, since we consider surrogate code 426 // units "interesting" as soon we see one. 427 TextUtils.getChars(text, start, end, buffer, 0); 428 429 final int len = end - start; 430 for (int i = 0; i < len; i++) { 431 final char c = buffer[i]; 432 if (c == '\n' || c == '\t' || TextUtils.couldAffectRtl(c)) { 433 return true; 434 } 435 } 436 } 437 return false; 438 } finally { 439 TextUtils.recycle(buffer); 440 } 441 } 442 443 /** 444 * Returns null if not boring; the width, ascent, and descent in the 445 * provided Metrics object (or a new one if the provided one was null) 446 * if boring. 447 * @hide 448 */ 449 @UnsupportedAppUsage isBoring(CharSequence text, TextPaint paint, TextDirectionHeuristic textDir, Metrics metrics)450 public static Metrics isBoring(CharSequence text, TextPaint paint, 451 TextDirectionHeuristic textDir, Metrics metrics) { 452 return isBoring(text, paint, textDir, false /* useFallbackLineSpacing */, metrics); 453 } 454 455 /** 456 * Returns null if not boring; the width, ascent, and descent in the 457 * provided Metrics object (or a new one if the provided one was null) 458 * if boring. 459 * 460 * @param text a text to be calculated text layout. 461 * @param paint a paint object used for styling. 462 * @param textDir a text direction. 463 * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts. 464 * False for keeping the first font's line height. If some glyphs 465 * requires larger vertical spaces, by passing true to this 466 * argument, the layout increase the line height to fit all glyphs. 467 * @param metrics the out metrics. 468 * @return metrics on success. null if text cannot be rendered by BoringLayout. 469 */ isBoring(@onNull CharSequence text, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing, @Nullable Metrics metrics)470 public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint, 471 @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing, 472 @Nullable Metrics metrics) { 473 final int textLength = text.length(); 474 if (hasAnyInterestingChars(text, textLength)) { 475 return null; // There are some interesting characters. Not boring. 476 } 477 if (textDir != null && textDir.isRtl(text, 0, textLength)) { 478 return null; // The heuristic considers the whole text RTL. Not boring. 479 } 480 if (text instanceof Spanned) { 481 Spanned sp = (Spanned) text; 482 Object[] styles = sp.getSpans(0, textLength, ParagraphStyle.class); 483 if (styles.length > 0) { 484 return null; // There are some ParagraphStyle spans. Not boring. 485 } 486 } 487 488 Metrics fm = metrics; 489 if (fm == null) { 490 fm = new Metrics(); 491 } else { 492 fm.reset(); 493 } 494 495 TextLine line = TextLine.obtain(); 496 line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT, 497 Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null, 498 0 /* ellipsisStart, 0 since text has not been ellipsized at this point */, 499 0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */, 500 useFallbackLineSpacing); 501 fm.width = (int) Math.ceil(line.metrics(fm)); 502 TextLine.recycle(line); 503 504 return fm; 505 } 506 507 @Override getHeight()508 public int getHeight() { 509 return mBottom; 510 } 511 512 @Override getLineCount()513 public int getLineCount() { 514 return 1; 515 } 516 517 @Override getLineTop(int line)518 public int getLineTop(int line) { 519 if (line == 0) 520 return 0; 521 else 522 return mBottom; 523 } 524 525 @Override getLineDescent(int line)526 public int getLineDescent(int line) { 527 return mDesc; 528 } 529 530 @Override getLineStart(int line)531 public int getLineStart(int line) { 532 if (line == 0) 533 return 0; 534 else 535 return getText().length(); 536 } 537 538 @Override getParagraphDirection(int line)539 public int getParagraphDirection(int line) { 540 return DIR_LEFT_TO_RIGHT; 541 } 542 543 @Override getLineContainsTab(int line)544 public boolean getLineContainsTab(int line) { 545 return false; 546 } 547 548 @Override getLineMax(int line)549 public float getLineMax(int line) { 550 return mMax; 551 } 552 553 @Override getLineWidth(int line)554 public float getLineWidth(int line) { 555 return (line == 0 ? mMax : 0); 556 } 557 558 @Override getLineDirections(int line)559 public final Directions getLineDirections(int line) { 560 return Layout.DIRS_ALL_LEFT_TO_RIGHT; 561 } 562 563 @Override getTopPadding()564 public int getTopPadding() { 565 return mTopPadding; 566 } 567 568 @Override getBottomPadding()569 public int getBottomPadding() { 570 return mBottomPadding; 571 } 572 573 @Override getEllipsisCount(int line)574 public int getEllipsisCount(int line) { 575 return mEllipsizedCount; 576 } 577 578 @Override getEllipsisStart(int line)579 public int getEllipsisStart(int line) { 580 return mEllipsizedStart; 581 } 582 583 @Override getEllipsizedWidth()584 public int getEllipsizedWidth() { 585 return mEllipsizedWidth; 586 } 587 588 @Override isFallbackLineSpacingEnabled()589 public boolean isFallbackLineSpacingEnabled() { 590 return mUseFallbackLineSpacing; 591 } 592 593 // Override draw so it will be faster. 594 @Override draw(Canvas c, Path highlight, Paint highlightpaint, int cursorOffset)595 public void draw(Canvas c, Path highlight, Paint highlightpaint, 596 int cursorOffset) { 597 if (mDirect != null && highlight == null) { 598 c.drawText(mDirect, 0, mBottom - mDesc, mPaint); 599 } else { 600 super.draw(c, highlight, highlightpaint, cursorOffset); 601 } 602 } 603 604 /** 605 * Callback for the ellipsizer to report what region it ellipsized. 606 */ ellipsized(int start, int end)607 public void ellipsized(int start, int end) { 608 mEllipsizedStart = start; 609 mEllipsizedCount = end - start; 610 } 611 612 private String mDirect; 613 private Paint mPaint; 614 private boolean mUseFallbackLineSpacing; 615 616 /* package */ int mBottom, mDesc; // for Direct 617 private int mTopPadding, mBottomPadding; 618 private float mMax; 619 private int mEllipsizedWidth, mEllipsizedStart, mEllipsizedCount; 620 621 public static class Metrics extends Paint.FontMetricsInt { 622 public int width; 623 toString()624 @Override public String toString() { 625 return super.toString() + " width=" + width; 626 } 627 reset()628 private void reset() { 629 top = 0; 630 bottom = 0; 631 ascent = 0; 632 descent = 0; 633 width = 0; 634 leading = 0; 635 } 636 } 637 } 638