Skip to content

Commit 590924e

Browse files
Gilles DebunneAndroid (Google) Code Review
authored andcommitted
Merge "Multiple display lists for editable text"
2 parents 19e27c0 + 33b7de8 commit 590924e

File tree

2 files changed

+232
-31
lines changed

2 files changed

+232
-31
lines changed

core/java/android/text/DynamicLayout.java

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import android.text.style.UpdateLayout;
2121
import android.text.style.WrapTogetherSpan;
2222

23+
import com.android.internal.util.ArrayUtils;
24+
2325
import java.lang.ref.WeakReference;
2426

2527
/**
@@ -30,8 +32,7 @@
3032
* {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
3133
* Canvas.drawText()} directly.</p>
3234
*/
33-
public class DynamicLayout
34-
extends Layout
35+
public class DynamicLayout extends Layout
3536
{
3637
private static final int PRIORITY = 128;
3738

@@ -116,6 +117,10 @@ public DynamicLayout(CharSequence base, CharSequence display,
116117

117118
mObjects = new PackedObjectVector<Directions>(1);
118119

120+
mBlockEnds = new int[] { 0 };
121+
mBlockIndices = new int[] { INVALID_BLOCK_INDEX };
122+
mNumberOfBlocks = 1;
123+
119124
mIncludePad = includepad;
120125

121126
/*
@@ -295,9 +300,9 @@ private void reflow(CharSequence s, int where, int before, int after) {
295300
n--;
296301

297302
// remove affected lines from old layout
298-
299303
mInts.deleteAt(startline, endline - startline);
300304
mObjects.deleteAt(startline, endline - startline);
305+
updateBlocks(startline, endline - 1, n);
301306

302307
// adjust offsets in layout for new height and offsets
303308

@@ -363,6 +368,124 @@ private void reflow(CharSequence s, int where, int before, int after) {
363368
}
364369
}
365370

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+
366489
@Override
367490
public int getLineCount() {
368491
return mInts.size() - 1;
@@ -428,13 +551,15 @@ else if (s instanceof Spannable)
428551
}
429552

430553
public void beforeTextChanged(CharSequence s, int where, int before, int after) {
554+
// Intentionally empty
431555
}
432556

433557
public void onTextChanged(CharSequence s, int where, int before, int after) {
434558
reflow(s, where, before, after);
435559
}
436560

437561
public void afterTextChanged(Editable s) {
562+
// Intentionally empty
438563
}
439564

440565
public void onSpanAdded(Spannable s, Object o, int start, int end) {
@@ -486,6 +611,20 @@ public int getEllipsisCount(int line) {
486611
private PackedIntVector mInts;
487612
private PackedObjectVector<Directions> mObjects;
488613

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+
489628
private int mTopPadding, mBottomPadding;
490629

491630
private static StaticLayout sStaticLayout = new StaticLayout(null);

0 commit comments

Comments
 (0)