|
20 | 20 | import android.text.style.UpdateLayout; |
21 | 21 | import android.text.style.WrapTogetherSpan; |
22 | 22 |
|
| 23 | +import com.android.internal.util.ArrayUtils; |
| 24 | + |
23 | 25 | import java.lang.ref.WeakReference; |
24 | 26 |
|
25 | 27 | /** |
|
30 | 32 | * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint) |
31 | 33 | * Canvas.drawText()} directly.</p> |
32 | 34 | */ |
33 | | -public class DynamicLayout |
34 | | -extends Layout |
| 35 | +public class DynamicLayout extends Layout |
35 | 36 | { |
36 | 37 | private static final int PRIORITY = 128; |
37 | 38 |
|
@@ -116,6 +117,10 @@ public DynamicLayout(CharSequence base, CharSequence display, |
116 | 117 |
|
117 | 118 | mObjects = new PackedObjectVector<Directions>(1); |
118 | 119 |
|
| 120 | + mBlockEnds = new int[] { 0 }; |
| 121 | + mBlockIndices = new int[] { INVALID_BLOCK_INDEX }; |
| 122 | + mNumberOfBlocks = 1; |
| 123 | + |
119 | 124 | mIncludePad = includepad; |
120 | 125 |
|
121 | 126 | /* |
@@ -295,9 +300,9 @@ private void reflow(CharSequence s, int where, int before, int after) { |
295 | 300 | n--; |
296 | 301 |
|
297 | 302 | // remove affected lines from old layout |
298 | | - |
299 | 303 | mInts.deleteAt(startline, endline - startline); |
300 | 304 | mObjects.deleteAt(startline, endline - startline); |
| 305 | + updateBlocks(startline, endline - 1, n); |
301 | 306 |
|
302 | 307 | // adjust offsets in layout for new height and offsets |
303 | 308 |
|
@@ -363,6 +368,124 @@ private void reflow(CharSequence s, int where, int before, int after) { |
363 | 368 | } |
364 | 369 | } |
365 | 370 |
|
| 371 | + /** |
| 372 | + * This method is called every time the layout is reflowed after an edition. |
| 373 | + * It updates the internal block data structure. The text is split in blocks |
| 374 | + * of contiguous lines, with at least one block for the entire text. |
| 375 | + * When a range of lines is edited, new blocks (from 0 to 3 depending on the |
| 376 | + * overlap structure) will replace the set of overlapping blocks. |
| 377 | + * Blocks are listed in order and are represented by their ending line number. |
| 378 | + * An index is associated to each block (which will be used by display lists), |
| 379 | + * this class simply invalidates the index of blocks overlapping a modification. |
| 380 | + * |
| 381 | + * @param startLine the first line of the range of modified lines |
| 382 | + * @param endLine the last line of the range, possibly equal to startLine, lower |
| 383 | + * than getLineCount() |
| 384 | + * @param newLineCount the number of lines that will replace the range, possibly 0 |
| 385 | + */ |
| 386 | + private void updateBlocks(int startLine, int endLine, int newLineCount) { |
| 387 | + int firstBlock = -1; |
| 388 | + int lastBlock = -1; |
| 389 | + for (int i = 0; i < mNumberOfBlocks; i++) { |
| 390 | + if (mBlockEnds[i] >= startLine) { |
| 391 | + firstBlock = i; |
| 392 | + break; |
| 393 | + } |
| 394 | + } |
| 395 | + for (int i = firstBlock; i < mNumberOfBlocks; i++) { |
| 396 | + if (mBlockEnds[i] >= endLine) { |
| 397 | + lastBlock = i; |
| 398 | + break; |
| 399 | + } |
| 400 | + } |
| 401 | + final int lastBlockEndLine = mBlockEnds[lastBlock]; |
| 402 | + |
| 403 | + boolean createBlockBefore = startLine > (firstBlock == 0 ? 0 : |
| 404 | + mBlockEnds[firstBlock - 1] + 1); |
| 405 | + boolean createBlock = newLineCount > 0; |
| 406 | + boolean createBlockAfter = endLine < mBlockEnds[lastBlock]; |
| 407 | + |
| 408 | + int numAddedBlocks = 0; |
| 409 | + if (createBlockBefore) numAddedBlocks++; |
| 410 | + if (createBlock) numAddedBlocks++; |
| 411 | + if (createBlockAfter) numAddedBlocks++; |
| 412 | + |
| 413 | + final int numRemovedBlocks = lastBlock - firstBlock + 1; |
| 414 | + final int newNumberOfBlocks = mNumberOfBlocks + numAddedBlocks - numRemovedBlocks; |
| 415 | + |
| 416 | + if (newNumberOfBlocks == 0) { |
| 417 | + // Even when text is empty, there is actually one line and hence one block |
| 418 | + mBlockEnds[0] = 0; |
| 419 | + mBlockIndices[0] = INVALID_BLOCK_INDEX; |
| 420 | + mNumberOfBlocks = 1; |
| 421 | + return; |
| 422 | + } |
| 423 | + |
| 424 | + if (newNumberOfBlocks > mBlockEnds.length) { |
| 425 | + final int newSize = ArrayUtils.idealIntArraySize(newNumberOfBlocks); |
| 426 | + int[] blockEnds = new int[newSize]; |
| 427 | + int[] blockIndices = new int[newSize]; |
| 428 | + System.arraycopy(mBlockEnds, 0, blockEnds, 0, firstBlock); |
| 429 | + System.arraycopy(mBlockIndices, 0, blockIndices, 0, firstBlock); |
| 430 | + System.arraycopy(mBlockEnds, lastBlock + 1, |
| 431 | + blockEnds, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); |
| 432 | + System.arraycopy(mBlockIndices, lastBlock + 1, |
| 433 | + blockIndices, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); |
| 434 | + mBlockEnds = blockEnds; |
| 435 | + mBlockIndices = blockIndices; |
| 436 | + } else { |
| 437 | + System.arraycopy(mBlockEnds, lastBlock + 1, |
| 438 | + mBlockEnds, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); |
| 439 | + System.arraycopy(mBlockIndices, lastBlock + 1, |
| 440 | + mBlockIndices, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); |
| 441 | + } |
| 442 | + |
| 443 | + mNumberOfBlocks = newNumberOfBlocks; |
| 444 | + final int deltaLines = newLineCount - (endLine - startLine + 1); |
| 445 | + for (int i = firstBlock + numAddedBlocks; i < mNumberOfBlocks; i++) { |
| 446 | + mBlockEnds[i] += deltaLines; |
| 447 | + } |
| 448 | + |
| 449 | + int blockIndex = firstBlock; |
| 450 | + if (createBlockBefore) { |
| 451 | + mBlockEnds[blockIndex] = startLine - 1; |
| 452 | + mBlockIndices[blockIndex] = INVALID_BLOCK_INDEX; |
| 453 | + blockIndex++; |
| 454 | + } |
| 455 | + |
| 456 | + if (createBlock) { |
| 457 | + mBlockEnds[blockIndex] = startLine + newLineCount - 1; |
| 458 | + mBlockIndices[blockIndex] = INVALID_BLOCK_INDEX; |
| 459 | + blockIndex++; |
| 460 | + } |
| 461 | + |
| 462 | + if (createBlockAfter) { |
| 463 | + mBlockEnds[blockIndex] = lastBlockEndLine + deltaLines; |
| 464 | + mBlockIndices[blockIndex] = INVALID_BLOCK_INDEX; |
| 465 | + } |
| 466 | + } |
| 467 | + |
| 468 | + /** |
| 469 | + * @hide |
| 470 | + */ |
| 471 | + public int[] getBlockEnds() { |
| 472 | + return mBlockEnds; |
| 473 | + } |
| 474 | + |
| 475 | + /** |
| 476 | + * @hide |
| 477 | + */ |
| 478 | + public int[] getBlockIndices() { |
| 479 | + return mBlockIndices; |
| 480 | + } |
| 481 | + |
| 482 | + /** |
| 483 | + * @hide |
| 484 | + */ |
| 485 | + public int getNumberOfBlocks() { |
| 486 | + return mNumberOfBlocks; |
| 487 | + } |
| 488 | + |
366 | 489 | @Override |
367 | 490 | public int getLineCount() { |
368 | 491 | return mInts.size() - 1; |
@@ -428,13 +551,15 @@ else if (s instanceof Spannable) |
428 | 551 | } |
429 | 552 |
|
430 | 553 | public void beforeTextChanged(CharSequence s, int where, int before, int after) { |
| 554 | + // Intentionally empty |
431 | 555 | } |
432 | 556 |
|
433 | 557 | public void onTextChanged(CharSequence s, int where, int before, int after) { |
434 | 558 | reflow(s, where, before, after); |
435 | 559 | } |
436 | 560 |
|
437 | 561 | public void afterTextChanged(Editable s) { |
| 562 | + // Intentionally empty |
438 | 563 | } |
439 | 564 |
|
440 | 565 | public void onSpanAdded(Spannable s, Object o, int start, int end) { |
@@ -486,6 +611,20 @@ public int getEllipsisCount(int line) { |
486 | 611 | private PackedIntVector mInts; |
487 | 612 | private PackedObjectVector<Directions> mObjects; |
488 | 613 |
|
| 614 | + /* |
| 615 | + * Value used in mBlockIndices when a block has been created or recycled and indicating that its |
| 616 | + * display list needs to be re-created. |
| 617 | + * @hide |
| 618 | + */ |
| 619 | + public static final int INVALID_BLOCK_INDEX = -1; |
| 620 | + // Stores the line numbers of the last line of each block |
| 621 | + private int[] mBlockEnds; |
| 622 | + // The indices of this block's display list in TextView's internal display list array or |
| 623 | + // INVALID_BLOCK_INDEX if this block has been invalidated during an edition |
| 624 | + private int[] mBlockIndices; |
| 625 | + // Number of items actually currently being used in the above 2 arrays |
| 626 | + private int mNumberOfBlocks; |
| 627 | + |
489 | 628 | private int mTopPadding, mBottomPadding; |
490 | 629 |
|
491 | 630 | private static StaticLayout sStaticLayout = new StaticLayout(null); |
|
0 commit comments