diff options
| -rw-r--r-- | core/java/android/text/DynamicLayout.java | 20 | ||||
| -rw-r--r-- | core/java/android/text/Layout.java | 12 | ||||
| -rw-r--r-- | core/java/android/text/StaticLayout.java | 23 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/text/DynamicLayoutTest.java | 74 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/text/LayoutTest.java | 11 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/text/StaticLayoutTest.java | 122 |
6 files changed, 212 insertions, 50 deletions
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index c7a5fce26c3e..91d829099917 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -365,6 +365,7 @@ public class DynamicLayout extends Layout desc += botpad; ints[DESCENT] = desc; + ints[EXTRA] = reflowed.getLineExtra(i); objects[0] = reflowed.getLineDirections(i); final int end = (i == n - 1) ? where + after : reflowed.getLineStart(i + 1); @@ -692,6 +693,14 @@ public class DynamicLayout extends Layout return mInts.getValue(line, DESCENT); } + /** + * @hide + */ + @Override + public int getLineExtra(int line) { + return mInts.getValue(line, EXTRA); + } + @Override public int getLineStart(int line) { return mInts.getValue(line, START) & START_MASK; @@ -851,14 +860,15 @@ public class DynamicLayout extends Layout private static final int TAB = START; private static final int TOP = 1; private static final int DESCENT = 2; + private static final int EXTRA = 3; // HYPHEN and MAY_PROTRUDE_FROM_TOP_OR_BOTTOM share the same entry. - private static final int HYPHEN = 3; + private static final int HYPHEN = 4; private static final int MAY_PROTRUDE_FROM_TOP_OR_BOTTOM = HYPHEN; - private static final int COLUMNS_NORMAL = 4; + private static final int COLUMNS_NORMAL = 5; - private static final int ELLIPSIS_START = 4; - private static final int ELLIPSIS_COUNT = 5; - private static final int COLUMNS_ELLIPSIZE = 6; + private static final int ELLIPSIS_START = 5; + private static final int ELLIPSIS_COUNT = 6; + private static final int COLUMNS_ELLIPSIZE = 7; private static final int START_MASK = 0x1FFFFFFF; private static final int DIR_SHIFT = 30; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index a233ba118e7d..ff9ee313200d 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -17,6 +17,7 @@ package android.text; import android.annotation.IntDef; +import android.annotation.IntRange; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; @@ -1466,6 +1467,17 @@ public abstract class Layout { return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line)); } + /** + * Return the extra space added as a result of line spacing attributes + * {@link #getSpacingAdd()} and {@link #getSpacingMultiplier()}. Default value is {@code zero}. + * + * @param line the index of the line, the value should be equal or greater than {@code zero} + * @hide + */ + public int getLineExtra(@IntRange(from = 0) int line) { + return 0; + } + public int getOffsetToLeftOf(int offset) { return getOffsetToLeftRightOf(offset, true); } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index a8c6aa6cbbbb..16badd50bf79 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -1005,6 +1005,7 @@ public class StaticLayout extends Layout { lines[off + START] = start; lines[off + TOP] = v; lines[off + DESCENT] = below + extra; + lines[off + EXTRA] = extra; // special case for non-ellipsized last visible line when maxLines is set // store the height as if it was ellipsized @@ -1195,6 +1196,14 @@ public class StaticLayout extends Layout { return mLines[mColumns * line + TOP]; } + /** + * @hide + */ + @Override + public int getLineExtra(int line) { + return mLines[mColumns * line + EXTRA]; + } + @Override public int getLineDescent(int line) { return mLines[mColumns * line + DESCENT]; @@ -1217,6 +1226,9 @@ public class StaticLayout extends Layout { @Override public final Directions getLineDirections(int line) { + if (line > getLineCount()) { + throw new ArrayIndexOutOfBoundsException(); + } return mLineDirections[line]; } @@ -1368,16 +1380,17 @@ public class StaticLayout extends Layout { */ private int mMaxLineHeight = -1; - private static final int COLUMNS_NORMAL = 4; - private static final int COLUMNS_ELLIPSIZE = 6; + private static final int COLUMNS_NORMAL = 5; + private static final int COLUMNS_ELLIPSIZE = 7; private static final int START = 0; private static final int DIR = START; private static final int TAB = START; private static final int TOP = 1; private static final int DESCENT = 2; - private static final int HYPHEN = 3; - private static final int ELLIPSIS_START = 4; - private static final int ELLIPSIS_COUNT = 5; + private static final int EXTRA = 3; + private static final int HYPHEN = 4; + private static final int ELLIPSIS_START = 5; + private static final int ELLIPSIS_COUNT = 6; private int[] mLines; private Directions[] mLineDirections; diff --git a/core/tests/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java index 811bf2c43320..5ef08e02029e 100644 --- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java +++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java @@ -18,6 +18,7 @@ package android.text; import static android.text.Layout.Alignment.ALIGN_NORMAL; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -112,4 +113,77 @@ public class DynamicLayoutTest { assertFalse(layout.getBlocksAlwaysNeedToBeRedrawn().contains(0)); assertTrue(layout.getBlocksAlwaysNeedToBeRedrawn().isEmpty()); } + + @Test + public void testGetLineExtra_withoutLinespacing() { + final SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc"); + final TextPaint textPaint = new TextPaint(); + + // create a StaticLayout to check against + final StaticLayout staticLayout = StaticLayout.Builder.obtain(text, 0, + text.length(), textPaint, WIDTH) + .setAlignment(ALIGN_NORMAL) + .setIncludePad(false) + .build(); + + // create the DynamicLayout + final DynamicLayout dynamicLayout = new DynamicLayout(text, + textPaint, + WIDTH, + ALIGN_NORMAL, + 1f /*spacingMultiplier*/, + 0 /*spacingAdd*/, + false /*includepad*/); + + final int lineCount = staticLayout.getLineCount(); + assertEquals(lineCount, dynamicLayout.getLineCount()); + for (int i = 0; i < lineCount; i++) { + assertEquals(staticLayout.getLineExtra(i), dynamicLayout.getLineExtra(i)); + } + } + + @Test + public void testGetLineExtra_withLinespacing() { + final SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc"); + final TextPaint textPaint = new TextPaint(); + final float spacingMultiplier = 2f; + final float spacingAdd = 4; + + // create a StaticLayout to check against + final StaticLayout staticLayout = StaticLayout.Builder.obtain(text, 0, + text.length(), textPaint, WIDTH) + .setAlignment(ALIGN_NORMAL) + .setIncludePad(false) + .setLineSpacing(spacingAdd, spacingMultiplier) + .build(); + + // create the DynamicLayout + final DynamicLayout dynamicLayout = new DynamicLayout(text, + textPaint, + WIDTH, + ALIGN_NORMAL, + spacingMultiplier, + spacingAdd, + false /*includepad*/); + + final int lineCount = staticLayout.getLineCount(); + assertEquals(lineCount, dynamicLayout.getLineCount()); + for (int i = 0; i < lineCount - 1; i++) { + assertEquals(staticLayout.getLineExtra(i), dynamicLayout.getLineExtra(i)); + } + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testGetLineExtra_withNegativeValue() { + final DynamicLayout layout = new DynamicLayout("", new TextPaint(), 10 /*width*/, + ALIGN_NORMAL, 1.0f /*spacingMultiplier*/, 0f /*spacingAdd*/, false /*includepad*/); + layout.getLineExtra(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testGetLineExtra_withParamGreaterThanLineCount() { + final DynamicLayout layout = new DynamicLayout("", new TextPaint(), 10 /*width*/, + ALIGN_NORMAL, 1.0f /*spacingMultiplier*/, 0f /*spacingAdd*/, false /*includepad*/); + layout.getLineExtra(100); + } } diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java index 6d610bbbf5e9..6b262ebf2c41 100644 --- a/core/tests/coretests/src/android/text/LayoutTest.java +++ b/core/tests/coretests/src/android/text/LayoutTest.java @@ -215,6 +215,17 @@ public class LayoutTest { } @Test + public void testGetLineExtra_returnsZeroByDefault() { + final String text = "a\nb\nc\n"; + final Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, + mAlign, 100 /* spacingMult*/, 100 /*spacingAdd*/); + final int lineCount = text.split("\n").length; + for (int i = 0; i < lineCount; i++) { + assertEquals(0, layout.getLineExtra(i)); + } + } + + @Test public void testGetLineVisibleEnd() { Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, mSpacingAdd); diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java index fb60e38fcbf6..ad16e8944736 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java @@ -107,7 +107,7 @@ public class StaticLayoutTest { Layout l = b.build(); assertVertMetrics(l, 0, 0, - fmi.ascent, fmi.descent); + new int[][]{{fmi.ascent, fmi.descent, 0}}); // other quick metrics assertEquals(0, l.getLineStart(0)); @@ -124,14 +124,14 @@ public class StaticLayoutTest { * Top and bottom padding are affected, as is the line descent and height. */ @Test - public void testGetters2() { + public void testLineMetrics_withPadding() { LayoutBuilder b = builder() .setIncludePad(true); FontMetricsInt fmi = b.paint.getFontMetricsInt(); Layout l = b.build(); assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, - fmi.top, fmi.bottom); + new int[][]{{fmi.top, fmi.bottom, 0}}); } /** @@ -139,16 +139,18 @@ public class StaticLayoutTest { * Ascent of top line and descent of bottom line are affected. */ @Test - public void testGetters3() { + public void testLineMetrics_withPaddingAndWidth() { LayoutBuilder b = builder() .setIncludePad(true) .setWidth(50); FontMetricsInt fmi = b.paint.getFontMetricsInt(); - Layout l = b.build(); + Layout l = b.build(); assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, - fmi.top, fmi.descent, - fmi.ascent, fmi.bottom); + new int[][]{ + {fmi.top, fmi.descent, 0}, + {fmi.ascent, fmi.bottom, 0} + }); } /** @@ -156,7 +158,7 @@ public class StaticLayoutTest { * First line ascent is top, bottom line descent is bottom. */ @Test - public void testGetters4() { + public void testLineMetrics_withThreeLines() { LayoutBuilder b = builder() .setText("This is a longer test") .setIncludePad(true) @@ -165,9 +167,11 @@ public class StaticLayoutTest { Layout l = b.build(); assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, - fmi.top, fmi.descent, - fmi.ascent, fmi.descent, - fmi.ascent, fmi.bottom); + new int[][]{ + {fmi.top, fmi.descent, 0}, + {fmi.ascent, fmi.descent, 0}, + {fmi.ascent, fmi.bottom, 0} + }); } /** @@ -176,7 +180,7 @@ public class StaticLayoutTest { * even be non-zero leading. */ @Test - public void testGetters5() { + public void testLineMetrics_withLargeText() { LayoutBuilder b = builder() .setText("This is a longer test") .setIncludePad(true) @@ -193,9 +197,11 @@ public class StaticLayoutTest { // using leading, this will fail. Layout l = b.build(); assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, - fmi.top, fmi.descent, - fmi.ascent, fmi.descent, - fmi.ascent, fmi.bottom); + new int[][]{ + {fmi.top, fmi.descent, 0}, + {fmi.ascent, fmi.descent, 0}, + {fmi.ascent, fmi.bottom, 0} + }); } /** @@ -203,7 +209,7 @@ public class StaticLayoutTest { * to 3 lines. */ @Test - public void testGetters6() { + public void testLineMetrics_withSpacingAdd() { int spacingAdd = 2; // int so expressions return int LayoutBuilder b = builder() .setText("This is a longer test") @@ -214,9 +220,11 @@ public class StaticLayoutTest { Layout l = b.build(); assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, - fmi.top, fmi.descent + spacingAdd, - fmi.ascent, fmi.descent + spacingAdd, - fmi.ascent, fmi.bottom); + new int[][]{ + {fmi.top, fmi.descent + spacingAdd, spacingAdd}, + {fmi.ascent, fmi.descent + spacingAdd, spacingAdd}, + {fmi.ascent, fmi.bottom, 0} + }); } /** @@ -224,7 +232,7 @@ public class StaticLayoutTest { * spacingMult = 1.5, wrapping to 3 lines. */ @Test - public void testGetters7() { + public void testLineMetrics_withSpacingMult() { LayoutBuilder b = builder() .setText("This is a longer test") .setIncludePad(true) @@ -236,9 +244,13 @@ public class StaticLayoutTest { Layout l = b.build(); assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, - fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top), - fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent), - fmi.ascent, fmi.bottom); + new int[][]{ + {fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top), + s.scale(fmi.descent - fmi.top)}, + {fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent), + s.scale(fmi.descent - fmi.ascent)}, + {fmi.ascent, fmi.bottom, 0} + }); } /** @@ -246,7 +258,7 @@ public class StaticLayoutTest { * spacingMult = 0.8 when wrapping to 3 lines. */ @Test - public void testGetters8() { + public void testLineMetrics_withUnitIntervalSpacingMult() { LayoutBuilder b = builder() .setText("This is a longer test") .setIncludePad(true) @@ -258,9 +270,25 @@ public class StaticLayoutTest { Layout l = b.build(); assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, - fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top), - fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent), - fmi.ascent, fmi.bottom); + new int[][]{ + {fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top), + s.scale(fmi.descent - fmi.top)}, + {fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent), + s.scale(fmi.descent - fmi.ascent)}, + {fmi.ascent, fmi.bottom, 0} + }); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testGetLineExtra_withNegativeValue() { + final Layout layout = builder().build(); + layout.getLineExtra(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testGetLineExtra_withParamGreaterThanLineCount() { + final Layout layout = builder().build(); + layout.getLineExtra(100); } // ----- test utility classes and methods ----- @@ -341,26 +369,39 @@ public class StaticLayoutTest { } } - private void assertVertMetrics(Layout l, int topPad, int botPad, int... values) { + /** + * Assert vertical metrics such as top, bottom, ascent, descent. + * @param l layout instance + * @param topPad top padding + * @param botPad bottom padding + * @param values values for each line where first is ascent, second is descent, and last one is + * extra + */ + private void assertVertMetrics(Layout l, int topPad, int botPad, int[][] values) { assertTopBotPadding(l, topPad, botPad); assertLinesMetrics(l, values); } - private void assertLinesMetrics(Layout l, int... values) { - // sanity check - if ((values.length & 0x1) != 0) { - throw new IllegalArgumentException(String.valueOf(values.length)); - } - - int lines = values.length >> 1; + /** + * Check given expected values against the Layout values. + * @param l layout instance + * @param values values for each line where first is ascent, second is descent, and last one is + * extra + */ + private void assertLinesMetrics(Layout l, int[][] values) { + final int lines = values.length; assertEquals(lines, l.getLineCount()); int t = 0; - for (int i = 0, n = 0; i < lines; ++i, n += 2) { - int a = values[n]; - int d = values[n+1]; + for (int i = 0, n = 0; i < lines; ++i, n += 3) { + if (values[i].length != 3) { + throw new IllegalArgumentException(String.valueOf(values.length)); + } + int a = values[i][0]; + int d = values[i][1]; + int extra = values[i][2]; int h = -a + d; - assertLineMetrics(l, i, t, a, d, h); + assertLineMetrics(l, i, t, a, d, h, extra); t += h; } @@ -368,12 +409,13 @@ public class StaticLayoutTest { } private void assertLineMetrics(Layout l, int line, - int top, int ascent, int descent, int height) { + int top, int ascent, int descent, int height, int extra) { String info = "line " + line; assertEquals(info, top, l.getLineTop(line)); assertEquals(info, ascent, l.getLineAscent(line)); assertEquals(info, descent, l.getLineDescent(line)); assertEquals(info, height, l.getLineBottom(line) - top); + assertEquals(info, extra, l.getLineExtra(line)); } private void assertTopBotPadding(Layout l, int topPad, int botPad) { |