From 71afc39d68413d125d46ea69e6c9fb077016bb9b Mon Sep 17 00:00:00 2001 From: Gilles Debunne Date: Thu, 10 May 2012 10:24:20 -0700 Subject: [PATCH] Cut long text into multiple DL at start Bug 5763685 To improve performance, preventively cut the the into display list of 3-10 lines of text. Further updates to small parts of the text (such as adding an underline on a word) will only invalidate and redraw the affected sub display list. DLs are aligned with paragraphs, just like they will be during text edition. Change-Id: I0d60debc7fdaea8b29080a6eacb2d60205e7d547 --- core/java/android/text/DynamicLayout.java | 73 ++++++++++++++++++++--- 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index dc58ef27280e8..d909362357ed1 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -35,6 +35,7 @@ import java.lang.ref.WeakReference; public class DynamicLayout extends Layout { private static final int PRIORITY = 128; + private static final int BLOCK_MINIMUM_CHARACTER_LENGTH = 400; /** * Make a layout for the specified text that will be updated as @@ -117,10 +118,6 @@ public class DynamicLayout extends Layout mObjects = new PackedObjectVector(1); - mBlockEndLines = new int[] { 0 }; - mBlockIndices = new int[] { INVALID_BLOCK_INDEX }; - mNumberOfBlocks = 1; - mIncludePad = includepad; /* @@ -170,7 +167,6 @@ public class DynamicLayout extends Layout mObjects.insertAt(0, dirs); // Update from 0 characters to whatever the real text is - reflow(base, 0, 0, base.length()); if (base instanceof Spannable) { @@ -295,14 +291,12 @@ public class DynamicLayout extends Layout // the very end of the buffer, then we already have a line that // starts there, so disregard the blank line. - if (where + after != len && - reflowed.getLineStart(n - 1) == where + after) + if (where + after != len && reflowed.getLineStart(n - 1) == where + after) n--; // remove affected lines from old layout mInts.deleteAt(startline, endline - startline); mObjects.deleteAt(startline, endline - startline); - updateBlocks(startline, endline - 1, n); // adjust offsets in layout for new height and offsets @@ -362,12 +356,70 @@ public class DynamicLayout extends Layout mObjects.insertAt(startline + i, objects); } + updateBlocks(startline, endline - 1, n); + synchronized (sLock) { sStaticLayout = reflowed; reflowed.finish(); } } + /** + * Create the initial block structure, cutting the text into blocks of at least + * BLOCK_MINIMUM_CHARACTER_SIZE characters, aligned on the ends of paragraphs. + */ + private void createBlocks() { + int offset = BLOCK_MINIMUM_CHARACTER_LENGTH; + mNumberOfBlocks = 0; + final CharSequence text = mDisplay; + + while (true) { + offset = TextUtils.indexOf(text, '\n', offset); + if (offset < 0) { + addBlockAtOffset(text.length()); + break; + } else { + addBlockAtOffset(offset); + offset += BLOCK_MINIMUM_CHARACTER_LENGTH; + } + } + + // mBlockIndices and mBlockEndLines should have the same length + mBlockIndices = new int[mBlockEndLines.length]; + for (int i = 0; i < mBlockEndLines.length; i++) { + mBlockIndices[i] = INVALID_BLOCK_INDEX; + } + } + + /** + * Create a new block, ending at the specified character offset. + * A block will actually be created only if has at least one line, i.e. this offset is + * not on the end line of the previous block. + */ + private void addBlockAtOffset(int offset) { + final int line = getLineForOffset(offset); + + if (mBlockEndLines == null) { + // Initial creation of the array, no test on previous block ending line + mBlockEndLines = new int[ArrayUtils.idealIntArraySize(1)]; + mBlockEndLines[mNumberOfBlocks] = line; + mNumberOfBlocks++; + return; + } + + final int previousBlockEndLine = mBlockEndLines[mNumberOfBlocks - 1]; + if (line > previousBlockEndLine) { + if (mNumberOfBlocks == mBlockEndLines.length) { + // Grow the array if needed + int[] blockEndLines = new int[ArrayUtils.idealIntArraySize(mNumberOfBlocks + 1)]; + System.arraycopy(mBlockEndLines, 0, blockEndLines, 0, mNumberOfBlocks); + mBlockEndLines = blockEndLines; + } + mBlockEndLines[mNumberOfBlocks] = line; + mNumberOfBlocks++; + } + } + /** * This method is called every time the layout is reflowed after an edition. * It updates the internal block data structure. The text is split in blocks @@ -388,6 +440,11 @@ public class DynamicLayout extends Layout * @hide */ void updateBlocks(int startLine, int endLine, int newLineCount) { + if (mBlockEndLines == null) { + createBlocks(); + return; + } + int firstBlock = -1; int lastBlock = -1; for (int i = 0; i < mNumberOfBlocks; i++) {