StaticLayout visual fix for maxLines
When maxLines is set on StaticLayout, the height calculation includes the lineSpacing for the lastLine, which causes the ellipsized version and non-ellipsized version to have different heights. With this CL: * maxLines is always set on StaticLayout * the correct line count for a given text is preserved, in other words a text that would be n lines will not be cut at maxLines. * The visual height for StaticLayout for ellipsized and non-ellipsized cases are the same when maxLines is set. Bug: 28988744 Bug: 18864800 Change-Id: I1e1cae31cf33d503a8cf1c942f422893efc480bb
This commit is contained in:
@@ -615,6 +615,17 @@ public abstract class Layout {
|
||||
return getLineTop(getLineCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total height of this layout.
|
||||
*
|
||||
* @param cap if true and max lines is set, returns the height of the layout at the max lines.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public int getHeight(boolean cap) {
|
||||
return getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base alignment of this layout.
|
||||
*/
|
||||
|
||||
@@ -836,7 +836,7 @@ public class StaticLayout extends Layout {
|
||||
here = endPos;
|
||||
breakIndex++;
|
||||
|
||||
if (mLineCount >= mMaximumVisibleLineCount) {
|
||||
if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -920,7 +920,25 @@ public class StaticLayout extends Layout {
|
||||
|
||||
boolean firstLine = (j == 0);
|
||||
boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
|
||||
boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd);
|
||||
|
||||
if (ellipsize != null) {
|
||||
// If there is only one line, then do any type of ellipsis except when it is MARQUEE
|
||||
// if there are multiple lines, just allow END ellipsis on the last line
|
||||
boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
|
||||
|
||||
boolean doEllipsis =
|
||||
(((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
|
||||
ellipsize != TextUtils.TruncateAt.MARQUEE) ||
|
||||
(!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
|
||||
ellipsize == TextUtils.TruncateAt.END);
|
||||
if (doEllipsis) {
|
||||
calculateEllipsis(start, end, widths, widthStart,
|
||||
ellipsisWidth, ellipsize, j,
|
||||
textWidth, paint, forceEllipsis);
|
||||
}
|
||||
}
|
||||
|
||||
boolean lastLine = mEllipsized || (end == bufEnd);
|
||||
|
||||
if (firstLine) {
|
||||
if (trackPad) {
|
||||
@@ -944,7 +962,6 @@ public class StaticLayout extends Layout {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (needMultiply && !lastLine) {
|
||||
double ex = (below - above) * (spacingmult - 1) + spacingadd;
|
||||
if (ex >= 0) {
|
||||
@@ -960,6 +977,15 @@ public class StaticLayout extends Layout {
|
||||
lines[off + TOP] = v;
|
||||
lines[off + DESCENT] = below + extra;
|
||||
|
||||
// special case for non-ellipsized last visible line when maxLines is set
|
||||
// store the height as if it was ellipsized
|
||||
if (!mEllipsized && currentLineIsTheLastVisibleOne) {
|
||||
// below calculation as if it was the last line
|
||||
int maxLineBelow = includePad ? bottom : below;
|
||||
// similar to the calculation of v below, without the extra.
|
||||
mMaxLineHeight = v + (maxLineBelow - above);
|
||||
}
|
||||
|
||||
v += (below - above) + extra;
|
||||
lines[off + mColumns + START] = end;
|
||||
lines[off + mColumns + TOP] = v;
|
||||
@@ -981,23 +1007,6 @@ public class StaticLayout extends Layout {
|
||||
start - widthStart, end - start);
|
||||
}
|
||||
|
||||
if (ellipsize != null) {
|
||||
// If there is only one line, then do any type of ellipsis except when it is MARQUEE
|
||||
// if there are multiple lines, just allow END ellipsis on the last line
|
||||
boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
|
||||
|
||||
boolean doEllipsis =
|
||||
(((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
|
||||
ellipsize != TextUtils.TruncateAt.MARQUEE) ||
|
||||
(!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
|
||||
ellipsize == TextUtils.TruncateAt.END);
|
||||
if (doEllipsis) {
|
||||
calculateEllipsis(start, end, widths, widthStart,
|
||||
ellipsisWidth, ellipsize, j,
|
||||
textWidth, paint, forceEllipsis);
|
||||
}
|
||||
}
|
||||
|
||||
mLineCount++;
|
||||
return v;
|
||||
}
|
||||
@@ -1101,7 +1110,7 @@ public class StaticLayout extends Layout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mEllipsized = true;
|
||||
mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
|
||||
mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
|
||||
}
|
||||
@@ -1239,6 +1248,25 @@ public class StaticLayout extends Layout {
|
||||
return mEllipsizedWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total height of this layout.
|
||||
*
|
||||
* @param cap if true and max lines is set, returns the height of the layout at the max lines.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public int getHeight(boolean cap) {
|
||||
if (cap && mLineCount >= mMaximumVisibleLineCount && mMaxLineHeight == -1 &&
|
||||
Log.isLoggable(TAG, Log.WARN)) {
|
||||
Log.w(TAG, "maxLineHeight should not be -1. "
|
||||
+ " maxLines:" + mMaximumVisibleLineCount
|
||||
+ " lineCount:" + mLineCount);
|
||||
}
|
||||
|
||||
return cap && mLineCount >= mMaximumVisibleLineCount && mMaxLineHeight != -1 ?
|
||||
mMaxLineHeight : super.getHeight();
|
||||
}
|
||||
|
||||
private static native long nNewBuilder();
|
||||
private static native void nFreeBuilder(long nativePtr);
|
||||
private static native void nFinishBuilder(long nativePtr);
|
||||
@@ -1277,6 +1305,21 @@ public class StaticLayout extends Layout {
|
||||
private int mColumns;
|
||||
private int mEllipsizedWidth;
|
||||
|
||||
/**
|
||||
* Keeps track if ellipsize is applied to the text.
|
||||
*/
|
||||
private boolean mEllipsized;
|
||||
|
||||
/**
|
||||
* If maxLines is set, ellipsize is not set, and the actual line count of text is greater than
|
||||
* or equal to maxLine, this variable holds the ideal visual height of the maxLine'th line
|
||||
* starting from the top of the layout. If maxLines is not set its value will be -1.
|
||||
*
|
||||
* The value is the same as getLineTop(maxLines) for ellipsized version where structurally no
|
||||
* more than maxLines is contained.
|
||||
*/
|
||||
private int mMaxLineHeight = -1;
|
||||
|
||||
private static final int COLUMNS_NORMAL = 4;
|
||||
private static final int COLUMNS_ELLIPSIZE = 6;
|
||||
private static final int START = 0;
|
||||
|
||||
@@ -5030,7 +5030,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
* call {@link InputMethodManager#restartInput(View)}.</p>
|
||||
* @param hintLocales List of the languages that the user is supposed to switch to no matter
|
||||
* what input method subtype is currently used. Set {@code null} to clear the current "hint".
|
||||
* @see #getImeHIntLocales()
|
||||
* @see #getImeHintLocales()
|
||||
* @see android.view.inputmethod.EditorInfo#hintLocales
|
||||
*/
|
||||
public void setImeHintLocales(@Nullable LocaleList hintLocales) {
|
||||
@@ -6857,11 +6857,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
.setLineSpacing(mSpacingAdd, mSpacingMult)
|
||||
.setIncludePad(mIncludePad)
|
||||
.setBreakStrategy(mBreakStrategy)
|
||||
.setHyphenationFrequency(mHyphenationFrequency);
|
||||
.setHyphenationFrequency(mHyphenationFrequency)
|
||||
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
|
||||
if (shouldEllipsize) {
|
||||
builder.setEllipsize(mEllipsize)
|
||||
.setEllipsizedWidth(ellipsisWidth)
|
||||
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
|
||||
.setEllipsizedWidth(ellipsisWidth);
|
||||
}
|
||||
mHintLayout = builder.build();
|
||||
}
|
||||
@@ -6948,11 +6948,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
.setLineSpacing(mSpacingAdd, mSpacingMult)
|
||||
.setIncludePad(mIncludePad)
|
||||
.setBreakStrategy(mBreakStrategy)
|
||||
.setHyphenationFrequency(mHyphenationFrequency);
|
||||
.setHyphenationFrequency(mHyphenationFrequency)
|
||||
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
|
||||
if (shouldEllipsize) {
|
||||
builder.setEllipsize(effectiveEllipsize)
|
||||
.setEllipsizedWidth(ellipsisWidth)
|
||||
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
|
||||
.setEllipsizedWidth(ellipsisWidth);
|
||||
}
|
||||
// TODO: explore always setting maxLines
|
||||
result = builder.build();
|
||||
@@ -7222,9 +7222,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
return 0;
|
||||
}
|
||||
|
||||
int linecount = layout.getLineCount();
|
||||
int pad = getCompoundPaddingTop() + getCompoundPaddingBottom();
|
||||
int desired = layout.getLineTop(linecount);
|
||||
/*
|
||||
* Don't cap the hint to a certain number of lines.
|
||||
* (Do cap it, though, if we have a maximum pixel height.)
|
||||
*/
|
||||
int desired = layout.getHeight(cap);
|
||||
|
||||
final Drawables dr = mDrawables;
|
||||
if (dr != null) {
|
||||
@@ -7232,31 +7234,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
desired = Math.max(desired, dr.mDrawableHeightRight);
|
||||
}
|
||||
|
||||
desired += pad;
|
||||
desired += getCompoundPaddingTop() + getCompoundPaddingBottom();
|
||||
|
||||
if (mMaxMode == LINES) {
|
||||
/*
|
||||
* Don't cap the hint to a certain number of lines.
|
||||
* (Do cap it, though, if we have a maximum pixel height.)
|
||||
*/
|
||||
if (cap) {
|
||||
if (linecount > mMaximum) {
|
||||
desired = layout.getLineTop(mMaximum);
|
||||
|
||||
if (dr != null) {
|
||||
desired = Math.max(desired, dr.mDrawableHeightLeft);
|
||||
desired = Math.max(desired, dr.mDrawableHeightRight);
|
||||
}
|
||||
|
||||
desired += pad;
|
||||
linecount = mMaximum;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mMaxMode != LINES) {
|
||||
desired = Math.min(desired, mMaximum);
|
||||
}
|
||||
|
||||
if (mMinMode == LINES) {
|
||||
int linecount = layout.getLineCount();
|
||||
if (linecount < mMinimum) {
|
||||
desired += getLineHeight() * (mMinimum - linecount);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user