Loading core/java/android/widget/Editor.java +106 −68 Original line number Diff line number Diff line Loading @@ -176,11 +176,21 @@ public class Editor { InputMethodState mInputMethodState; private static class TextRenderNode { // Render node has 3 recording states: // 1. Recorded operations are valid. // #needsRecord() returns false, but needsToBeShifted is false. // 2. Recorded operations are not valid, but just the position needed to be updated. // #needsRecord() returns false, but needsToBeShifted is true. // 3. Recorded operations are not valid. Need to record operations. #needsRecord() returns // true. RenderNode renderNode; boolean isDirty; // Becomes true when recorded operations can be reused, but the position has to be updated. boolean needsToBeShifted; public TextRenderNode(String name) { isDirty = true; renderNode = RenderNode.create(name, null); isDirty = true; needsToBeShifted = true; } boolean needsRecord() { return isDirty || !renderNode.isValid(); Loading Loading @@ -1686,34 +1696,71 @@ public class Editor { final int numberOfBlocks = dynamicLayout.getNumberOfBlocks(); final int indexFirstChangedBlock = dynamicLayout.getIndexFirstChangedBlock(); int endOfPreviousBlock = -1; int searchStartIndex = 0; for (int i = 0; i < numberOfBlocks; i++) { int blockEndLine = blockEndLines[i]; int blockIndex = blockIndices[i]; int startBlock = Arrays.binarySearch(blockEndLines, 0, numberOfBlocks, firstLine); if (startBlock < 0) { startBlock = -(startBlock + 1); } startBlock = Math.min(indexFirstChangedBlock, startBlock); int startIndexToFindAvailableRenderNode = 0; int lastIndex = numberOfBlocks; for (int i = startBlock; i < numberOfBlocks; i++) { final int blockIndex = blockIndices[i]; if (i >= indexFirstChangedBlock && blockIndex != DynamicLayout.INVALID_BLOCK_INDEX && mTextRenderNodes[blockIndex] != null) { mTextRenderNodes[blockIndex].needsToBeShifted = true; } if (blockEndLines[i] < firstLine) { // Blocks in [indexFirstChangedBlock, firstLine) are not redrawn here. They will // be redrawn after they get scrolled into drawing range. continue; } startIndexToFindAvailableRenderNode = drawHardwareAcceleratedInner(canvas, layout, highlight, highlightPaint, cursorOffsetVertical, blockEndLines, blockIndices, i, numberOfBlocks, startIndexToFindAvailableRenderNode); if (blockEndLines[i] >= lastLine) { lastIndex = Math.max(indexFirstChangedBlock, i + 1); break; } } dynamicLayout.setIndexFirstChangedBlock(lastIndex); } else { // Boring layout is used for empty and hint text layout.drawText(canvas, firstLine, lastLine); } } private int drawHardwareAcceleratedInner(Canvas canvas, Layout layout, Path highlight, Paint highlightPaint, int cursorOffsetVertical, int[] blockEndLines, int[] blockIndices, int blockInfoIndex, int numberOfBlocks, int startIndexToFindAvailableRenderNode) { final int blockEndLine = blockEndLines[blockInfoIndex]; int blockIndex = blockIndices[blockInfoIndex]; final boolean blockIsInvalid = blockIndex == DynamicLayout.INVALID_BLOCK_INDEX; if (blockIsInvalid) { blockIndex = getAvailableDisplayListIndex(blockIndices, numberOfBlocks, searchStartIndex); startIndexToFindAvailableRenderNode); // Note how dynamic layout's internal block indices get updated from Editor blockIndices[i] = blockIndex; blockIndices[blockInfoIndex] = blockIndex; if (mTextRenderNodes[blockIndex] != null) { mTextRenderNodes[blockIndex].isDirty = true; } searchStartIndex = blockIndex + 1; startIndexToFindAvailableRenderNode = blockIndex + 1; } if (mTextRenderNodes[blockIndex] == null) { mTextRenderNodes[blockIndex] = new TextRenderNode("Text " + blockIndex); mTextRenderNodes[blockIndex] = new TextRenderNode("Text " + blockIndex); } final boolean blockDisplayListIsInvalid = mTextRenderNodes[blockIndex].needsRecord(); final boolean blockDisplayListIsInvalid = mTextRenderNodes[blockIndex].needsRecord(); RenderNode blockDisplayList = mTextRenderNodes[blockIndex].renderNode; if (i >= indexFirstChangedBlock || blockDisplayListIsInvalid) { final int blockBeginLine = endOfPreviousBlock + 1; if (mTextRenderNodes[blockIndex].needsToBeShifted || blockDisplayListIsInvalid) { final int blockBeginLine = blockInfoIndex == 0 ? 0 : blockEndLines[blockInfoIndex - 1] + 1; final int top = layout.getLineTop(blockBeginLine); final int bottom = layout.getLineBottom(blockEndLine); int left = 0; Loading Loading @@ -1748,21 +1795,12 @@ public class Editor { } } // Valid disply list whose index is >= indexFirstChangedBlock // only needs to update its drawing location. // Valid display list only needs to update its drawing location. blockDisplayList.setLeftTopRightBottom(left, top, right, bottom); mTextRenderNodes[blockIndex].needsToBeShifted = false; } ((DisplayListCanvas) canvas).drawRenderNode(blockDisplayList); endOfPreviousBlock = blockEndLine; } dynamicLayout.setIndexFirstChangedBlock(numberOfBlocks); } else { // Boring layout is used for empty and hint text layout.drawText(canvas, firstLine, lastLine); } return startIndexToFindAvailableRenderNode; } private int getAvailableDisplayListIndex(int[] blockIndices, int numberOfBlocks, Loading Loading
core/java/android/widget/Editor.java +106 −68 Original line number Diff line number Diff line Loading @@ -176,11 +176,21 @@ public class Editor { InputMethodState mInputMethodState; private static class TextRenderNode { // Render node has 3 recording states: // 1. Recorded operations are valid. // #needsRecord() returns false, but needsToBeShifted is false. // 2. Recorded operations are not valid, but just the position needed to be updated. // #needsRecord() returns false, but needsToBeShifted is true. // 3. Recorded operations are not valid. Need to record operations. #needsRecord() returns // true. RenderNode renderNode; boolean isDirty; // Becomes true when recorded operations can be reused, but the position has to be updated. boolean needsToBeShifted; public TextRenderNode(String name) { isDirty = true; renderNode = RenderNode.create(name, null); isDirty = true; needsToBeShifted = true; } boolean needsRecord() { return isDirty || !renderNode.isValid(); Loading Loading @@ -1686,34 +1696,71 @@ public class Editor { final int numberOfBlocks = dynamicLayout.getNumberOfBlocks(); final int indexFirstChangedBlock = dynamicLayout.getIndexFirstChangedBlock(); int endOfPreviousBlock = -1; int searchStartIndex = 0; for (int i = 0; i < numberOfBlocks; i++) { int blockEndLine = blockEndLines[i]; int blockIndex = blockIndices[i]; int startBlock = Arrays.binarySearch(blockEndLines, 0, numberOfBlocks, firstLine); if (startBlock < 0) { startBlock = -(startBlock + 1); } startBlock = Math.min(indexFirstChangedBlock, startBlock); int startIndexToFindAvailableRenderNode = 0; int lastIndex = numberOfBlocks; for (int i = startBlock; i < numberOfBlocks; i++) { final int blockIndex = blockIndices[i]; if (i >= indexFirstChangedBlock && blockIndex != DynamicLayout.INVALID_BLOCK_INDEX && mTextRenderNodes[blockIndex] != null) { mTextRenderNodes[blockIndex].needsToBeShifted = true; } if (blockEndLines[i] < firstLine) { // Blocks in [indexFirstChangedBlock, firstLine) are not redrawn here. They will // be redrawn after they get scrolled into drawing range. continue; } startIndexToFindAvailableRenderNode = drawHardwareAcceleratedInner(canvas, layout, highlight, highlightPaint, cursorOffsetVertical, blockEndLines, blockIndices, i, numberOfBlocks, startIndexToFindAvailableRenderNode); if (blockEndLines[i] >= lastLine) { lastIndex = Math.max(indexFirstChangedBlock, i + 1); break; } } dynamicLayout.setIndexFirstChangedBlock(lastIndex); } else { // Boring layout is used for empty and hint text layout.drawText(canvas, firstLine, lastLine); } } private int drawHardwareAcceleratedInner(Canvas canvas, Layout layout, Path highlight, Paint highlightPaint, int cursorOffsetVertical, int[] blockEndLines, int[] blockIndices, int blockInfoIndex, int numberOfBlocks, int startIndexToFindAvailableRenderNode) { final int blockEndLine = blockEndLines[blockInfoIndex]; int blockIndex = blockIndices[blockInfoIndex]; final boolean blockIsInvalid = blockIndex == DynamicLayout.INVALID_BLOCK_INDEX; if (blockIsInvalid) { blockIndex = getAvailableDisplayListIndex(blockIndices, numberOfBlocks, searchStartIndex); startIndexToFindAvailableRenderNode); // Note how dynamic layout's internal block indices get updated from Editor blockIndices[i] = blockIndex; blockIndices[blockInfoIndex] = blockIndex; if (mTextRenderNodes[blockIndex] != null) { mTextRenderNodes[blockIndex].isDirty = true; } searchStartIndex = blockIndex + 1; startIndexToFindAvailableRenderNode = blockIndex + 1; } if (mTextRenderNodes[blockIndex] == null) { mTextRenderNodes[blockIndex] = new TextRenderNode("Text " + blockIndex); mTextRenderNodes[blockIndex] = new TextRenderNode("Text " + blockIndex); } final boolean blockDisplayListIsInvalid = mTextRenderNodes[blockIndex].needsRecord(); final boolean blockDisplayListIsInvalid = mTextRenderNodes[blockIndex].needsRecord(); RenderNode blockDisplayList = mTextRenderNodes[blockIndex].renderNode; if (i >= indexFirstChangedBlock || blockDisplayListIsInvalid) { final int blockBeginLine = endOfPreviousBlock + 1; if (mTextRenderNodes[blockIndex].needsToBeShifted || blockDisplayListIsInvalid) { final int blockBeginLine = blockInfoIndex == 0 ? 0 : blockEndLines[blockInfoIndex - 1] + 1; final int top = layout.getLineTop(blockBeginLine); final int bottom = layout.getLineBottom(blockEndLine); int left = 0; Loading Loading @@ -1748,21 +1795,12 @@ public class Editor { } } // Valid disply list whose index is >= indexFirstChangedBlock // only needs to update its drawing location. // Valid display list only needs to update its drawing location. blockDisplayList.setLeftTopRightBottom(left, top, right, bottom); mTextRenderNodes[blockIndex].needsToBeShifted = false; } ((DisplayListCanvas) canvas).drawRenderNode(blockDisplayList); endOfPreviousBlock = blockEndLine; } dynamicLayout.setIndexFirstChangedBlock(numberOfBlocks); } else { // Boring layout is used for empty and hint text layout.drawText(canvas, firstLine, lastLine); } return startIndexToFindAvailableRenderNode; } private int getAvailableDisplayListIndex(int[] blockIndices, int numberOfBlocks, Loading