StaticLayoutpublic class StaticLayout extends Layout StaticLayout is a Layout for text that will not be edited after it
is laid out. Use {@link DynamicLayout} for text that may change.
This is used by widgets to control text layout. You should not need
to use this class directly unless you are implementing your own widget
or custom display object, or would be tempted to call
{@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
Canvas.drawText()} directly. |
Fields Summary |
---|
private static final char | FIRST_CJK | private int | mLineCount | private int | mTopPadding | private int | mBottomPadding | private int | mColumns | private int | mEllipsizedWidth | private static final int | COLUMNS_NORMAL | private static final int | COLUMNS_ELLIPSIZE | private static final int | START | private static final int | DIR | private static final int | TAB | private static final int | TOP | private static final int | DESCENT | private static final int | ELLIPSIS_START | private static final int | ELLIPSIS_COUNT | private int[] | mLines | private Directions[] | mLineDirections | private static final int | START_MASK | private static final int | DIR_MASK | private static final int | DIR_SHIFT | private static final int | TAB_MASK | private static final char | FIRST_RIGHT_TO_LEFT | private byte[] | mChdirs | private char[] | mChs | private float[] | mWidths | private Paint.FontMetricsInt | mFontMetricsInt |
Constructors Summary |
---|
public StaticLayout(CharSequence source, TextPaint paint, int width, Alignment align, float spacingmult, float spacingadd, boolean includepad)
this(source, 0, source.length(), paint, width, align,
spacingmult, spacingadd, includepad);
| public StaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint, int outerwidth, Alignment align, float spacingmult, float spacingadd, boolean includepad)
this(source, bufstart, bufend, paint, outerwidth, align,
spacingmult, spacingadd, includepad, null, 0);
| public StaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint, int outerwidth, Alignment align, float spacingmult, float spacingadd, boolean includepad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth)
super((ellipsize == null)
? source
: (source instanceof Spanned)
? new SpannedEllipsizer(source)
: new Ellipsizer(source),
paint, outerwidth, align, spacingmult, spacingadd);
/*
* This is annoying, but we can't refer to the layout until
* superclass construction is finished, and the superclass
* constructor wants the reference to the display text.
*
* This will break if the superclass constructor ever actually
* cares about the content instead of just holding the reference.
*/
if (ellipsize != null) {
Ellipsizer e = (Ellipsizer) getText();
e.mLayout = this;
e.mWidth = ellipsizedWidth;
e.mMethod = ellipsize;
mEllipsizedWidth = ellipsizedWidth;
mColumns = COLUMNS_ELLIPSIZE;
} else {
mColumns = COLUMNS_NORMAL;
mEllipsizedWidth = outerwidth;
}
mLines = new int[ArrayUtils.idealIntArraySize(2 * mColumns)];
mLineDirections = new Directions[
ArrayUtils.idealIntArraySize(2 * mColumns)];
generate(source, bufstart, bufend, paint, outerwidth, align,
spacingmult, spacingadd, includepad, includepad,
ellipsize != null, ellipsizedWidth, ellipsize);
mChdirs = null;
mChs = null;
mWidths = null;
mFontMetricsInt = null;
| StaticLayout(boolean ellipsize)
super(null, null, 0, null, 0, 0);
mColumns = COLUMNS_ELLIPSIZE;
mLines = new int[ArrayUtils.idealIntArraySize(2 * mColumns)];
mLineDirections = new Directions[
ArrayUtils.idealIntArraySize(2 * mColumns)];
|
Methods Summary |
---|
private void | calculateEllipsis(int linestart, int lineend, float[] widths, int widstart, int widoff, float avail, TextUtils.TruncateAt where, int line, float textwidth, TextPaint paint)
int len = lineend - linestart;
if (textwidth <= avail) {
// Everything fits!
mLines[mColumns * line + ELLIPSIS_START] = 0;
mLines[mColumns * line + ELLIPSIS_COUNT] = 0;
return;
}
float ellipsiswid = paint.measureText("\u2026");
int ellipsisStart, ellipsisCount;
if (where == TextUtils.TruncateAt.START) {
float sum = 0;
int i;
for (i = len; i >= 0; i--) {
float w = widths[i - 1 + linestart - widstart + widoff];
if (w + sum + ellipsiswid > avail) {
break;
}
sum += w;
}
ellipsisStart = 0;
ellipsisCount = i;
} else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE) {
float sum = 0;
int i;
for (i = 0; i < len; i++) {
float w = widths[i + linestart - widstart + widoff];
if (w + sum + ellipsiswid > avail) {
break;
}
sum += w;
}
ellipsisStart = i;
ellipsisCount = len - i;
} else /* where = TextUtils.TruncateAt.MIDDLE */ {
float lsum = 0, rsum = 0;
int left = 0, right = len;
float ravail = (avail - ellipsiswid) / 2;
for (right = len; right >= 0; right--) {
float w = widths[right - 1 + linestart - widstart + widoff];
if (w + rsum > ravail) {
break;
}
rsum += w;
}
float lavail = avail - ellipsiswid - rsum;
for (left = 0; left < right; left++) {
float w = widths[left + linestart - widstart + widoff];
if (w + lsum > lavail) {
break;
}
lsum += w;
}
ellipsisStart = left;
ellipsisCount = right - left;
}
mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
| void | generate(java.lang.CharSequence source, int bufstart, int bufend, TextPaint paint, int outerwidth, Alignment align, float spacingmult, float spacingadd, boolean includepad, boolean trackpad, boolean breakOnlyAtSpaces, float ellipsizedWidth, TextUtils.TruncateAt where)
mLineCount = 0;
int v = 0;
boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
Paint.FontMetricsInt fm = mFontMetricsInt;
int[] choosehtv = null;
int end = TextUtils.indexOf(source, '\n", bufstart, bufend);
int bufsiz = end >= 0 ? end - bufstart : bufend - bufstart;
boolean first = true;
if (mChdirs == null) {
mChdirs = new byte[ArrayUtils.idealByteArraySize(bufsiz + 1)];
mChs = new char[ArrayUtils.idealCharArraySize(bufsiz + 1)];
mWidths = new float[ArrayUtils.idealIntArraySize((bufsiz + 1) * 2)];
}
byte[] chdirs = mChdirs;
char[] chs = mChs;
float[] widths = mWidths;
AlteredCharSequence alter = null;
Spanned spanned = null;
if (source instanceof Spanned)
spanned = (Spanned) source;
int DEFAULT_DIR = DIR_LEFT_TO_RIGHT; // XXX
for (int start = bufstart; start <= bufend; start = end) {
if (first)
first = false;
else
end = TextUtils.indexOf(source, '\n", start, bufend);
if (end < 0)
end = bufend;
else
end++;
int firstwidth = outerwidth;
int restwidth = outerwidth;
LineHeightSpan[] chooseht = null;
if (spanned != null) {
LeadingMarginSpan[] sp;
sp = spanned.getSpans(start, end, LeadingMarginSpan.class);
for (int i = 0; i < sp.length; i++) {
firstwidth -= sp[i].getLeadingMargin(true);
restwidth -= sp[i].getLeadingMargin(false);
}
chooseht = spanned.getSpans(start, end, LineHeightSpan.class);
if (chooseht.length != 0) {
if (choosehtv == null ||
choosehtv.length < chooseht.length) {
choosehtv = new int[ArrayUtils.idealIntArraySize(
chooseht.length)];
}
for (int i = 0; i < chooseht.length; i++) {
int o = spanned.getSpanStart(chooseht[i]);
if (o < start) {
// starts in this layout, before the
// current paragraph
choosehtv[i] = getLineTop(getLineForOffset(o));
} else {
// starts in this paragraph
choosehtv[i] = v;
}
}
}
}
if (end - start > chdirs.length) {
chdirs = new byte[ArrayUtils.idealByteArraySize(end - start)];
mChdirs = chdirs;
}
if (end - start > chs.length) {
chs = new char[ArrayUtils.idealCharArraySize(end - start)];
mChs = chs;
}
if ((end - start) * 2 > widths.length) {
widths = new float[ArrayUtils.idealIntArraySize((end - start) * 2)];
mWidths = widths;
}
TextUtils.getChars(source, start, end, chs, 0);
final int n = end - start;
boolean easy = true;
boolean altered = false;
int dir = DEFAULT_DIR; // XXX
for (int i = 0; i < n; i++) {
if (chs[i] >= FIRST_RIGHT_TO_LEFT) {
easy = false;
break;
}
}
if (!easy) {
AndroidCharacter.getDirectionalities(chs, chdirs, end - start);
/*
* Determine primary paragraph direction
*/
for (int j = start; j < end; j++) {
int d = chdirs[j - start];
if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) {
dir = DIR_LEFT_TO_RIGHT;
break;
}
if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
dir = DIR_RIGHT_TO_LEFT;
break;
}
}
/*
* XXX Explicit overrides should go here
*/
/*
* Weak type resolution
*/
final byte SOR = dir == DIR_LEFT_TO_RIGHT ?
Character.DIRECTIONALITY_LEFT_TO_RIGHT :
Character.DIRECTIONALITY_RIGHT_TO_LEFT;
// dump(chdirs, n, "initial");
// W1 non spacing marks
for (int j = 0; j < n; j++) {
if (chdirs[j] == Character.NON_SPACING_MARK) {
if (j == 0)
chdirs[j] = SOR;
else
chdirs[j] = chdirs[j - 1];
}
}
// dump(chdirs, n, "W1");
// W2 european numbers
byte cur = SOR;
for (int j = 0; j < n; j++) {
byte d = chdirs[j];
if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
d == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
cur = d;
else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) {
if (cur ==
Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER;
}
}
// dump(chdirs, n, "W2");
// W3 arabic letters
for (int j = 0; j < n; j++) {
if (chdirs[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
chdirs[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
}
// dump(chdirs, n, "W3");
// W4 single separator between numbers
for (int j = 1; j < n - 1; j++) {
byte d = chdirs[j];
byte prev = chdirs[j - 1];
byte next = chdirs[j + 1];
if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) {
if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER &&
next == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
} else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) {
if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER &&
next == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER &&
next == Character.DIRECTIONALITY_ARABIC_NUMBER)
chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER;
}
}
// dump(chdirs, n, "W4");
// W5 european number terminators
boolean adjacent = false;
for (int j = 0; j < n; j++) {
byte d = chdirs[j];
if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
adjacent = true;
else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent)
chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
else
adjacent = false;
}
//dump(chdirs, n, "W5");
// W5 european number terminators part 2,
// W6 separators and terminators
adjacent = false;
for (int j = n - 1; j >= 0; j--) {
byte d = chdirs[j];
if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
adjacent = true;
else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) {
if (adjacent)
chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
else
chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
}
else {
adjacent = false;
if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR ||
d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR ||
d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR ||
d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR)
chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
}
}
// dump(chdirs, n, "W6");
// W7 strong direction of european numbers
cur = SOR;
for (int j = 0; j < n; j++) {
byte d = chdirs[j];
if (d == SOR ||
d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
d == Character.DIRECTIONALITY_RIGHT_TO_LEFT)
cur = d;
if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
chdirs[j] = cur;
}
// dump(chdirs, n, "W7");
// N1, N2 neutrals
cur = SOR;
for (int j = 0; j < n; j++) {
byte d = chdirs[j];
if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
cur = d;
} else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER ||
d == Character.DIRECTIONALITY_ARABIC_NUMBER) {
cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
} else {
byte dd = SOR;
int k;
for (k = j + 1; k < n; k++) {
dd = chdirs[k];
if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
break;
}
if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER ||
dd == Character.DIRECTIONALITY_ARABIC_NUMBER) {
dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
break;
}
}
for (int y = j; y < k; y++) {
if (dd == cur)
chdirs[y] = cur;
else
chdirs[y] = SOR;
}
j = k - 1;
}
}
// dump(chdirs, n, "final");
// extra: enforce that all tabs and surrogate characters go the
// primary direction
// TODO: actually do directions right for surrogates
for (int j = 0; j < n; j++) {
char c = chs[j];
if (c == '\t" || (c >= 0xD800 && c <= 0xDFFF)) {
chdirs[j] = SOR;
}
}
// extra: enforce that object replacements go to the
// primary direction
// and that none of the underlying characters are treated
// as viable breakpoints
if (source instanceof Spanned) {
Spanned sp = (Spanned) source;
ReplacementSpan[] spans = sp.getSpans(start, end, ReplacementSpan.class);
for (int y = 0; y < spans.length; y++) {
int a = sp.getSpanStart(spans[y]);
int b = sp.getSpanEnd(spans[y]);
for (int x = a; x < b; x++) {
chdirs[x - start] = SOR;
chs[x - start] = '\uFFFC";
}
}
}
// Do mirroring for right-to-left segments
for (int i = 0; i < n; i++) {
if (chdirs[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
int j;
for (j = i; j < n; j++) {
if (chdirs[j] !=
Character.DIRECTIONALITY_RIGHT_TO_LEFT)
break;
}
if (AndroidCharacter.mirror(chs, i, j - i))
altered = true;
i = j - 1;
}
}
}
CharSequence sub;
if (altered) {
if (alter == null)
alter = AlteredCharSequence.make(source, chs, start, end);
else
alter.update(chs, start, end);
sub = alter;
} else {
sub = source;
}
int width = firstwidth;
float w = 0;
int here = start;
int ok = start;
float okwidth = w;
int okascent = 0, okdescent = 0, oktop = 0, okbottom = 0;
int fit = start;
float fitwidth = w;
int fitascent = 0, fitdescent = 0, fittop = 0, fitbottom = 0;
boolean tab = false;
int next;
for (int i = start; i < end; i = next) {
if (spanned == null)
next = end;
else
next = spanned.nextSpanTransition(i, end,
MetricAffectingSpan.
class);
if (spanned == null) {
paint.getTextWidths(sub, i, next, widths);
System.arraycopy(widths, 0, widths,
end - start + (i - start), next - i);
paint.getFontMetricsInt(fm);
} else {
mWorkPaint.baselineShift = 0;
Styled.getTextWidths(paint, mWorkPaint,
spanned, i, next,
widths, fm);
System.arraycopy(widths, 0, widths,
end - start + (i - start), next - i);
if (mWorkPaint.baselineShift < 0) {
fm.ascent += mWorkPaint.baselineShift;
fm.top += mWorkPaint.baselineShift;
} else {
fm.descent += mWorkPaint.baselineShift;
fm.bottom += mWorkPaint.baselineShift;
}
}
int fmtop = fm.top;
int fmbottom = fm.bottom;
int fmascent = fm.ascent;
int fmdescent = fm.descent;
if (false) {
StringBuilder sb = new StringBuilder();
for (int j = i; j < next; j++) {
sb.append(widths[j - start + (end - start)]);
sb.append(' ");
}
Log.e("text", sb.toString());
}
for (int j = i; j < next; j++) {
char c = chs[j - start];
float before = w;
if (c == '\n") {
;
} else if (c == '\t") {
w = Layout.nextTab(sub, start, end, w, null);
tab = true;
} else if (c >= 0xD800 && c <= 0xDFFF && j + 1 < next) {
int emoji = Character.codePointAt(chs, j - start);
if (emoji >= MIN_EMOJI && emoji <= MAX_EMOJI) {
Bitmap bm = EMOJI_FACTORY.
getBitmapFromAndroidPua(emoji);
if (bm != null) {
Paint whichPaint;
if (spanned == null) {
whichPaint = paint;
} else {
whichPaint = mWorkPaint;
}
float wid = (float) bm.getWidth() *
-whichPaint.ascent() /
bm.getHeight();
w += wid;
tab = true;
j++;
} else {
w += widths[j - start + (end - start)];
}
} else {
w += widths[j - start + (end - start)];
}
} else {
w += widths[j - start + (end - start)];
}
// Log.e("text", "was " + before + " now " + w + " after " + c + " within " + width);
if (w <= width) {
fitwidth = w;
fit = j + 1;
if (fmtop < fittop)
fittop = fmtop;
if (fmascent < fitascent)
fitascent = fmascent;
if (fmdescent > fitdescent)
fitdescent = fmdescent;
if (fmbottom > fitbottom)
fitbottom = fmbottom;
/*
* From the Unicode Line Breaking Algorithm:
* (at least approximately)
*
* .,:; are class IS: breakpoints
* except when adjacent to digits
* / is class SY: a breakpoint
* except when followed by a digit.
* - is class HY: a breakpoint
* except when followed by a digit.
*
* Ideographs are class ID: breakpoints when adjacent,
* except for NS (non-starters), which can be broken
* after but not before.
*/
if (c == ' " || c == '\t" ||
((c == '." || c == '," || c == ':" || c == ';") &&
(j - 1 < here || !Character.isDigit(chs[j - 1 - start])) &&
(j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) ||
((c == '/" || c == '-") &&
(j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) ||
(c >= FIRST_CJK && isIdeographic(c, true) &&
j + 1 < next && isIdeographic(chs[j + 1 - start], false))) {
okwidth = w;
ok = j + 1;
if (fittop < oktop)
oktop = fittop;
if (fitascent < okascent)
okascent = fitascent;
if (fitdescent > okdescent)
okdescent = fitdescent;
if (fitbottom > okbottom)
okbottom = fitbottom;
}
} else if (breakOnlyAtSpaces) {
if (ok != here) {
// Log.e("text", "output ok " + here + " to " +ok);
while (ok < next && chs[ok - start] == ' ") {
ok++;
}
v = out(source,
here, ok,
okascent, okdescent, oktop, okbottom,
v,
spacingmult, spacingadd, chooseht,
choosehtv, fm, tab,
needMultiply, start, chdirs, dir, easy,
ok == bufend, includepad, trackpad,
widths, start, end - start,
where, ellipsizedWidth, okwidth,
paint);
here = ok;
} else {
// Act like it fit even though it didn't.
fitwidth = w;
fit = j + 1;
if (fmtop < fittop)
fittop = fmtop;
if (fmascent < fitascent)
fitascent = fmascent;
if (fmdescent > fitdescent)
fitdescent = fmdescent;
if (fmbottom > fitbottom)
fitbottom = fmbottom;
}
} else {
if (ok != here) {
// Log.e("text", "output ok " + here + " to " +ok);
while (ok < next && chs[ok - start] == ' ") {
ok++;
}
v = out(source,
here, ok,
okascent, okdescent, oktop, okbottom,
v,
spacingmult, spacingadd, chooseht,
choosehtv, fm, tab,
needMultiply, start, chdirs, dir, easy,
ok == bufend, includepad, trackpad,
widths, start, end - start,
where, ellipsizedWidth, okwidth,
paint);
here = ok;
} else if (fit != here) {
// Log.e("text", "output fit " + here + " to " +fit);
v = out(source,
here, fit,
fitascent, fitdescent,
fittop, fitbottom,
v,
spacingmult, spacingadd, chooseht,
choosehtv, fm, tab,
needMultiply, start, chdirs, dir, easy,
fit == bufend, includepad, trackpad,
widths, start, end - start,
where, ellipsizedWidth, fitwidth,
paint);
here = fit;
} else {
// Log.e("text", "output one " + here + " to " +(here + 1));
measureText(paint, mWorkPaint,
source, here, here + 1, fm, tab,
null);
v = out(source,
here, here+1,
fm.ascent, fm.descent,
fm.top, fm.bottom,
v,
spacingmult, spacingadd, chooseht,
choosehtv, fm, tab,
needMultiply, start, chdirs, dir, easy,
here + 1 == bufend, includepad,
trackpad,
widths, start, end - start,
where, ellipsizedWidth,
widths[here - start], paint);
here = here + 1;
}
if (here < i) {
j = next = here; // must remeasure
} else {
j = here - 1; // continue looping
}
ok = fit = here;
w = 0;
fitascent = fitdescent = fittop = fitbottom = 0;
okascent = okdescent = oktop = okbottom = 0;
width = restwidth;
}
}
}
if (end != here) {
if ((fittop | fitbottom | fitdescent | fitascent) == 0) {
paint.getFontMetricsInt(fm);
fittop = fm.top;
fitbottom = fm.bottom;
fitascent = fm.ascent;
fitdescent = fm.descent;
}
// Log.e("text", "output rest " + here + " to " + end);
v = out(source,
here, end, fitascent, fitdescent,
fittop, fitbottom,
v,
spacingmult, spacingadd, chooseht,
choosehtv, fm, tab,
needMultiply, start, chdirs, dir, easy,
end == bufend, includepad, trackpad,
widths, start, end - start,
where, ellipsizedWidth, w, paint);
}
start = end;
if (end == bufend)
break;
}
if (bufend == bufstart || source.charAt(bufend - 1) == '\n") {
// Log.e("text", "output last " + bufend);
paint.getFontMetricsInt(fm);
v = out(source,
bufend, bufend, fm.ascent, fm.descent,
fm.top, fm.bottom,
v,
spacingmult, spacingadd, null,
null, fm, false,
needMultiply, bufend, chdirs, DEFAULT_DIR, true,
true, includepad, trackpad,
widths, bufstart, 0,
where, ellipsizedWidth, 0, paint);
}
| public int | getBottomPadding()
return mBottomPadding;
| public int | getEllipsisCount(int line)
if (mColumns < COLUMNS_ELLIPSIZE) {
return 0;
}
return mLines[mColumns * line + ELLIPSIS_COUNT];
| public int | getEllipsisStart(int line)
if (mColumns < COLUMNS_ELLIPSIZE) {
return 0;
}
return mLines[mColumns * line + ELLIPSIS_START];
| public int | getEllipsizedWidth()
return mEllipsizedWidth;
| private static int | getFit(TextPaint paint, TextPaint workPaint, java.lang.CharSequence text, int start, int end, float wid)
int high = end + 1, low = start - 1, guess;
while (high - low > 1) {
guess = (high + low) / 2;
if (measureText(paint, workPaint,
text, start, guess, null, true, null) > wid)
high = guess;
else
low = guess;
}
if (low < start)
return start;
else
return low;
| public boolean | getLineContainsTab(int line)
return (mLines[mColumns * line + TAB] & TAB_MASK) != 0;
| public int | getLineCount()
return mLineCount;
| public int | getLineDescent(int line)
return mLines[mColumns * line + DESCENT];
| public final Directions | getLineDirections(int line)
return mLineDirections[line];
| public int | getLineForVertical(int vertical)
int high = mLineCount;
int low = -1;
int guess;
int[] lines = mLines;
while (high - low > 1) {
guess = (high + low) >> 1;
if (lines[mColumns * guess + TOP] > vertical){
high = guess;
} else {
low = guess;
}
}
if (low < 0) {
return 0;
} else {
return low;
}
| public int | getLineStart(int line)
return mLines[mColumns * line + START] & START_MASK;
| public int | getLineTop(int line)
return mLines[mColumns * line + TOP];
| public int | getParagraphDirection(int line)
return mLines[mColumns * line + DIR] >> DIR_SHIFT;
| public int | getTopPadding()
return mTopPadding;
| private static final boolean | isIdeographic(char c, boolean includeNonStarters)Returns true if the specified character is one of those specified
as being Ideographic (class ID) by the Unicode Line Breaking Algorithm
(http://www.unicode.org/unicode/reports/tr14/), and is therefore OK
to break between a pair of.
if (c >= '\u2E80" && c <= '\u2FFF") {
return true; // CJK, KANGXI RADICALS, DESCRIPTION SYMBOLS
}
if (c == '\u3000") {
return true; // IDEOGRAPHIC SPACE
}
if (c >= '\u3040" && c <= '\u309F") {
if (!includeNonStarters) {
switch (c) {
case '\u3041": // # HIRAGANA LETTER SMALL A
case '\u3043": // # HIRAGANA LETTER SMALL I
case '\u3045": // # HIRAGANA LETTER SMALL U
case '\u3047": // # HIRAGANA LETTER SMALL E
case '\u3049": // # HIRAGANA LETTER SMALL O
case '\u3063": // # HIRAGANA LETTER SMALL TU
case '\u3083": // # HIRAGANA LETTER SMALL YA
case '\u3085": // # HIRAGANA LETTER SMALL YU
case '\u3087": // # HIRAGANA LETTER SMALL YO
case '\u308E": // # HIRAGANA LETTER SMALL WA
case '\u3095": // # HIRAGANA LETTER SMALL KA
case '\u3096": // # HIRAGANA LETTER SMALL KE
case '\u309B": // # KATAKANA-HIRAGANA VOICED SOUND MARK
case '\u309C": // # KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
case '\u309D": // # HIRAGANA ITERATION MARK
case '\u309E": // # HIRAGANA VOICED ITERATION MARK
return false;
}
}
return true; // Hiragana (except small characters)
}
if (c >= '\u30A0" && c <= '\u30FF") {
if (!includeNonStarters) {
switch (c) {
case '\u30A0": // # KATAKANA-HIRAGANA DOUBLE HYPHEN
case '\u30A1": // # KATAKANA LETTER SMALL A
case '\u30A3": // # KATAKANA LETTER SMALL I
case '\u30A5": // # KATAKANA LETTER SMALL U
case '\u30A7": // # KATAKANA LETTER SMALL E
case '\u30A9": // # KATAKANA LETTER SMALL O
case '\u30C3": // # KATAKANA LETTER SMALL TU
case '\u30E3": // # KATAKANA LETTER SMALL YA
case '\u30E5": // # KATAKANA LETTER SMALL YU
case '\u30E7": // # KATAKANA LETTER SMALL YO
case '\u30EE": // # KATAKANA LETTER SMALL WA
case '\u30F5": // # KATAKANA LETTER SMALL KA
case '\u30F6": // # KATAKANA LETTER SMALL KE
case '\u30FB": // # KATAKANA MIDDLE DOT
case '\u30FC": // # KATAKANA-HIRAGANA PROLONGED SOUND MARK
case '\u30FD": // # KATAKANA ITERATION MARK
case '\u30FE": // # KATAKANA VOICED ITERATION MARK
return false;
}
}
return true; // Katakana (except small characters)
}
if (c >= '\u3400" && c <= '\u4DB5") {
return true; // CJK UNIFIED IDEOGRAPHS EXTENSION A
}
if (c >= '\u4E00" && c <= '\u9FBB") {
return true; // CJK UNIFIED IDEOGRAPHS
}
if (c >= '\uF900" && c <= '\uFAD9") {
return true; // CJK COMPATIBILITY IDEOGRAPHS
}
if (c >= '\uA000" && c <= '\uA48F") {
return true; // YI SYLLABLES
}
if (c >= '\uA490" && c <= '\uA4CF") {
return true; // YI RADICALS
}
if (c >= '\uFE62" && c <= '\uFE66") {
return true; // SMALL PLUS SIGN to SMALL EQUALS SIGN
}
if (c >= '\uFF10" && c <= '\uFF19") {
return true; // WIDE DIGITS
}
return false;
| private int | out(java.lang.CharSequence text, int start, int end, int above, int below, int top, int bottom, int v, float spacingmult, float spacingadd, android.text.style.LineHeightSpan[] chooseht, int[] choosehtv, Paint.FontMetricsInt fm, boolean tab, boolean needMultiply, int pstart, byte[] chdirs, int dir, boolean easy, boolean last, boolean includepad, boolean trackpad, float[] widths, int widstart, int widoff, TextUtils.TruncateAt ellipsize, float ellipsiswidth, float textwidth, TextPaint paint)
int j = mLineCount;
int off = j * mColumns;
int want = off + mColumns + TOP;
int[] lines = mLines;
// Log.e("text", "line " + start + " to " + end + (last ? "===" : ""));
if (want >= lines.length) {
int nlen = ArrayUtils.idealIntArraySize(want + 1);
int[] grow = new int[nlen];
System.arraycopy(lines, 0, grow, 0, lines.length);
mLines = grow;
lines = grow;
Directions[] grow2 = new Directions[nlen];
System.arraycopy(mLineDirections, 0, grow2, 0,
mLineDirections.length);
mLineDirections = grow2;
}
if (chooseht != null) {
fm.ascent = above;
fm.descent = below;
fm.top = top;
fm.bottom = bottom;
for (int i = 0; i < chooseht.length; i++) {
chooseht[i].chooseHeight(text, start, end, choosehtv[i], v, fm);
}
above = fm.ascent;
below = fm.descent;
top = fm.top;
bottom = fm.bottom;
}
if (j == 0) {
if (trackpad) {
mTopPadding = top - above;
}
if (includepad) {
above = top;
}
}
if (last) {
if (trackpad) {
mBottomPadding = bottom - below;
}
if (includepad) {
below = bottom;
}
}
int extra;
if (needMultiply) {
extra = (int) ((below - above) * (spacingmult - 1)
+ spacingadd + 0.5);
} else {
extra = 0;
}
lines[off + START] = start;
lines[off + TOP] = v;
lines[off + DESCENT] = below + extra;
v += (below - above) + extra;
lines[off + mColumns + START] = end;
lines[off + mColumns + TOP] = v;
if (tab)
lines[off + TAB] |= TAB_MASK;
{
lines[off + DIR] |= dir << DIR_SHIFT;
int cur = Character.DIRECTIONALITY_LEFT_TO_RIGHT;
int count = 0;
if (!easy) {
for (int k = start; k < end; k++) {
if (chdirs[k - pstart] != cur) {
count++;
cur = chdirs[k - pstart];
}
}
}
Directions linedirs;
if (count == 0) {
linedirs = DIRS_ALL_LEFT_TO_RIGHT;
} else {
short[] ld = new short[count + 1];
cur = Character.DIRECTIONALITY_LEFT_TO_RIGHT;
count = 0;
int here = start;
for (int k = start; k < end; k++) {
if (chdirs[k - pstart] != cur) {
// XXX check to make sure we don't
// overflow short
ld[count++] = (short) (k - here);
cur = chdirs[k - pstart];
here = k;
}
}
ld[count] = (short) (end - here);
if (count == 1 && ld[0] == 0) {
linedirs = DIRS_ALL_RIGHT_TO_LEFT;
} else {
linedirs = new Directions(ld);
}
}
mLineDirections[j] = linedirs;
// If ellipsize is in marquee mode, do not apply ellipsis on the first line
if (ellipsize != null && (ellipsize != TextUtils.TruncateAt.MARQUEE || j != 0)) {
calculateEllipsis(start, end, widths, widstart, widoff,
ellipsiswidth, ellipsize, j,
textwidth, paint);
}
}
mLineCount++;
return v;
|
|