Skip to content

Commit 38e1c7a

Browse files
adampAndroid (Google) Code Review
authored andcommitted
Merge "Ellipsize marquee TextViews that aren't currently animating"
2 parents 2561475 + 282e377 commit 38e1c7a

File tree

1 file changed

+159
-71
lines changed

1 file changed

+159
-71
lines changed

core/java/android/widget/TextView.java

Lines changed: 159 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import android.text.TextPaint;
6565
import android.text.TextUtils;
6666
import android.text.TextWatcher;
67+
import android.text.TextUtils.TruncateAt;
6768
import android.text.method.AllCapsTransformationMethod;
6869
import android.text.method.ArrowKeyMovementMethod;
6970
import android.text.method.DateKeyListener;
@@ -366,6 +367,35 @@ private static enum TextAlign {
366367

367368
private boolean mResolvedDrawables = false;
368369

370+
/**
371+
* On some devices the fading edges add a performance penalty if used
372+
* extensively in the same layout. This mode indicates how the marquee
373+
* is currently being shown, if applicable. (mEllipsize will == MARQUEE)
374+
*/
375+
private int mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
376+
377+
/**
378+
* When mMarqueeFadeMode is not MARQUEE_FADE_NORMAL, this stores
379+
* the layout that should be used when the mode switches.
380+
*/
381+
private Layout mSavedMarqueeModeLayout;
382+
383+
/**
384+
* Draw marquee text with fading edges as usual
385+
*/
386+
private static final int MARQUEE_FADE_NORMAL = 0;
387+
388+
/**
389+
* Draw marquee text as ellipsize end while inactive instead of with the fade.
390+
* (Useful for devices where the fade can be expensive if overdone)
391+
*/
392+
private static final int MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS = 1;
393+
394+
/**
395+
* Draw marquee text with fading edges because it is currently active/animating.
396+
*/
397+
private static final int MARQUEE_FADE_SWITCH_SHOW_FADE = 2;
398+
369399
/*
370400
* Kick-start the font cache for the zygote process (to pay the cost of
371401
* initializing freetype for our default font only once).
@@ -997,8 +1027,13 @@ public TextView(Context context,
9971027
setEllipsize(TextUtils.TruncateAt.END);
9981028
break;
9991029
case 4:
1000-
setHorizontalFadingEdgeEnabled(
1001-
ViewConfiguration.get(context).isFadingMarqueeEnabled());
1030+
if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) {
1031+
setHorizontalFadingEdgeEnabled(true);
1032+
mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
1033+
} else {
1034+
setHorizontalFadingEdgeEnabled(false);
1035+
mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
1036+
}
10021037
setEllipsize(TextUtils.TruncateAt.MARQUEE);
10031038
break;
10041039
}
@@ -3069,8 +3104,13 @@ private void setText(CharSequence text, BufferType type,
30693104

30703105
if (text instanceof Spanned &&
30713106
((Spanned) text).getSpanStart(TextUtils.TruncateAt.MARQUEE) >= 0) {
3072-
setHorizontalFadingEdgeEnabled(
3073-
ViewConfiguration.get(mContext).isFadingMarqueeEnabled());
3107+
if (ViewConfiguration.get(mContext).isFadingMarqueeEnabled()) {
3108+
setHorizontalFadingEdgeEnabled(true);
3109+
mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
3110+
} else {
3111+
setHorizontalFadingEdgeEnabled(false);
3112+
mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
3113+
}
30743114
setEllipsize(TextUtils.TruncateAt.MARQUEE);
30753115
}
30763116

@@ -4763,7 +4803,8 @@ protected void onDraw(Canvas canvas) {
47634803

47644804
final int layoutDirection = getResolvedLayoutDirection();
47654805
final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
4766-
if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
4806+
if (mEllipsize == TextUtils.TruncateAt.MARQUEE &&
4807+
mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
47674808
if (!mSingleLine && getLineCount() == 1 && canMarquee() &&
47684809
(absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
47694810
canvas.translate(mLayout.getLineRight(0) - (mRight - mLeft -
@@ -5947,7 +5988,7 @@ private void nullLayouts() {
59475988
mSavedHintLayout = (BoringLayout) mHintLayout;
59485989
}
59495990

5950-
mLayout = mHintLayout = null;
5991+
mSavedMarqueeModeLayout = mLayout = mHintLayout = null;
59515992

59525993
// Since it depends on the value of mLayout
59535994
prepareCursorControllers();
@@ -6067,73 +6108,25 @@ protected void makeNewLayout(int w, int hintWidth,
60676108

60686109
Layout.Alignment alignment = getLayoutAlignment();
60696110
boolean shouldEllipsize = mEllipsize != null && mInput == null;
6111+
final boolean switchEllipsize = mEllipsize == TruncateAt.MARQUEE &&
6112+
mMarqueeFadeMode != MARQUEE_FADE_NORMAL;
6113+
TruncateAt effectiveEllipsize = mEllipsize;
6114+
if (mEllipsize == TruncateAt.MARQUEE &&
6115+
mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
6116+
effectiveEllipsize = TruncateAt.END;
6117+
}
60706118

60716119
if (mTextDir == null) {
60726120
resolveTextDirection();
60736121
}
6074-
if (mText instanceof Spannable) {
6075-
mLayout = new DynamicLayout(mText, mTransformed, mTextPaint, w,
6076-
alignment, mTextDir, mSpacingMult,
6077-
mSpacingAdd, mIncludePad, mInput == null ? mEllipsize : null,
6078-
ellipsisWidth);
6079-
} else {
6080-
if (boring == UNKNOWN_BORING) {
6081-
boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
6082-
if (boring != null) {
6083-
mBoring = boring;
6084-
}
6085-
}
60866122

6087-
if (boring != null) {
6088-
if (boring.width <= w &&
6089-
(mEllipsize == null || boring.width <= ellipsisWidth)) {
6090-
if (mSavedLayout != null) {
6091-
mLayout = mSavedLayout.
6092-
replaceOrMake(mTransformed, mTextPaint,
6093-
w, alignment, mSpacingMult, mSpacingAdd,
6094-
boring, mIncludePad);
6095-
} else {
6096-
mLayout = BoringLayout.make(mTransformed, mTextPaint,
6097-
w, alignment, mSpacingMult, mSpacingAdd,
6098-
boring, mIncludePad);
6099-
}
6100-
6101-
mSavedLayout = (BoringLayout) mLayout;
6102-
} else if (shouldEllipsize && boring.width <= w) {
6103-
if (mSavedLayout != null) {
6104-
mLayout = mSavedLayout.
6105-
replaceOrMake(mTransformed, mTextPaint,
6106-
w, alignment, mSpacingMult, mSpacingAdd,
6107-
boring, mIncludePad, mEllipsize,
6108-
ellipsisWidth);
6109-
} else {
6110-
mLayout = BoringLayout.make(mTransformed, mTextPaint,
6111-
w, alignment, mSpacingMult, mSpacingAdd,
6112-
boring, mIncludePad, mEllipsize,
6113-
ellipsisWidth);
6114-
}
6115-
} else if (shouldEllipsize) {
6116-
mLayout = new StaticLayout(mTransformed,
6117-
0, mTransformed.length(),
6118-
mTextPaint, w, alignment, mTextDir, mSpacingMult,
6119-
mSpacingAdd, mIncludePad, mEllipsize,
6120-
ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
6121-
} else {
6122-
mLayout = new StaticLayout(mTransformed, mTextPaint,
6123-
w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
6124-
mIncludePad);
6125-
}
6126-
} else if (shouldEllipsize) {
6127-
mLayout = new StaticLayout(mTransformed,
6128-
0, mTransformed.length(),
6129-
mTextPaint, w, alignment, mTextDir, mSpacingMult,
6130-
mSpacingAdd, mIncludePad, mEllipsize,
6131-
ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
6132-
} else {
6133-
mLayout = new StaticLayout(mTransformed, mTextPaint,
6134-
w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
6135-
mIncludePad);
6136-
}
6123+
mLayout = makeSingleLayout(w, boring, ellipsisWidth, alignment, shouldEllipsize,
6124+
effectiveEllipsize, effectiveEllipsize == mEllipsize);
6125+
if (switchEllipsize) {
6126+
TruncateAt oppositeEllipsize = effectiveEllipsize == TruncateAt.MARQUEE ?
6127+
TruncateAt.END : TruncateAt.MARQUEE;
6128+
mSavedMarqueeModeLayout = makeSingleLayout(w, boring, ellipsisWidth, alignment,
6129+
shouldEllipsize, oppositeEllipsize, effectiveEllipsize != mEllipsize);
61376130
}
61386131

61396132
shouldEllipsize = mEllipsize != null;
@@ -6224,6 +6217,77 @@ protected void makeNewLayout(int w, int hintWidth,
62246217
prepareCursorControllers();
62256218
}
62266219

6220+
private Layout makeSingleLayout(int w, BoringLayout.Metrics boring, int ellipsisWidth,
6221+
Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
6222+
boolean useSaved) {
6223+
Layout result = null;
6224+
if (mText instanceof Spannable) {
6225+
result = new DynamicLayout(mText, mTransformed, mTextPaint, w,
6226+
alignment, mTextDir, mSpacingMult,
6227+
mSpacingAdd, mIncludePad, mInput == null ? effectiveEllipsize : null,
6228+
ellipsisWidth);
6229+
} else {
6230+
if (boring == UNKNOWN_BORING) {
6231+
boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
6232+
if (boring != null) {
6233+
mBoring = boring;
6234+
}
6235+
}
6236+
6237+
if (boring != null) {
6238+
if (boring.width <= w &&
6239+
(effectiveEllipsize == null || boring.width <= ellipsisWidth)) {
6240+
if (useSaved && mSavedLayout != null) {
6241+
result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint,
6242+
w, alignment, mSpacingMult, mSpacingAdd,
6243+
boring, mIncludePad);
6244+
} else {
6245+
result = BoringLayout.make(mTransformed, mTextPaint,
6246+
w, alignment, mSpacingMult, mSpacingAdd,
6247+
boring, mIncludePad);
6248+
}
6249+
6250+
if (useSaved) {
6251+
mSavedLayout = (BoringLayout) result;
6252+
}
6253+
} else if (shouldEllipsize && boring.width <= w) {
6254+
if (useSaved && mSavedLayout != null) {
6255+
result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint,
6256+
w, alignment, mSpacingMult, mSpacingAdd,
6257+
boring, mIncludePad, effectiveEllipsize,
6258+
ellipsisWidth);
6259+
} else {
6260+
result = BoringLayout.make(mTransformed, mTextPaint,
6261+
w, alignment, mSpacingMult, mSpacingAdd,
6262+
boring, mIncludePad, effectiveEllipsize,
6263+
ellipsisWidth);
6264+
}
6265+
} else if (shouldEllipsize) {
6266+
result = new StaticLayout(mTransformed,
6267+
0, mTransformed.length(),
6268+
mTextPaint, w, alignment, mTextDir, mSpacingMult,
6269+
mSpacingAdd, mIncludePad, effectiveEllipsize,
6270+
ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
6271+
} else {
6272+
result = new StaticLayout(mTransformed, mTextPaint,
6273+
w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
6274+
mIncludePad);
6275+
}
6276+
} else if (shouldEllipsize) {
6277+
result = new StaticLayout(mTransformed,
6278+
0, mTransformed.length(),
6279+
mTextPaint, w, alignment, mTextDir, mSpacingMult,
6280+
mSpacingAdd, mIncludePad, effectiveEllipsize,
6281+
ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
6282+
} else {
6283+
result = new StaticLayout(mTransformed, mTextPaint,
6284+
w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
6285+
mIncludePad);
6286+
}
6287+
}
6288+
return result;
6289+
}
6290+
62276291
private boolean compressText(float width) {
62286292
if (isHardwareAccelerated()) return false;
62296293

@@ -7179,7 +7243,9 @@ private boolean isCursorVisible() {
71797243

71807244
private boolean canMarquee() {
71817245
int width = (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight());
7182-
return width > 0 && mLayout.getLineWidth(0) > width;
7246+
return width > 0 && (mLayout.getLineWidth(0) > width ||
7247+
(mMarqueeFadeMode != MARQUEE_FADE_NORMAL && mSavedMarqueeModeLayout != null &&
7248+
mSavedMarqueeModeLayout.getLineWidth(0) > width));
71837249
}
71847250

71857251
private void startMarquee() {
@@ -7193,6 +7259,16 @@ private void startMarquee() {
71937259
if ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected()) &&
71947260
getLineCount() == 1 && canMarquee()) {
71957261

7262+
if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
7263+
mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_FADE;
7264+
final Layout tmp = mLayout;
7265+
mLayout = mSavedMarqueeModeLayout;
7266+
mSavedMarqueeModeLayout = tmp;
7267+
setHorizontalFadingEdgeEnabled(true);
7268+
requestLayout();
7269+
invalidate();
7270+
}
7271+
71967272
if (mMarquee == null) mMarquee = new Marquee(this);
71977273
mMarquee.start(mMarqueeRepeatLimit);
71987274
}
@@ -7202,6 +7278,16 @@ private void stopMarquee() {
72027278
if (mMarquee != null && !mMarquee.isStopped()) {
72037279
mMarquee.stop();
72047280
}
7281+
7282+
if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_FADE) {
7283+
mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
7284+
final Layout tmp = mSavedMarqueeModeLayout;
7285+
mSavedMarqueeModeLayout = mLayout;
7286+
mLayout = tmp;
7287+
setHorizontalFadingEdgeEnabled(false);
7288+
requestLayout();
7289+
invalidate();
7290+
}
72057291
}
72067292

72077293
private void startStopMarquee(boolean start) {
@@ -8407,7 +8493,8 @@ private void makeBlink() {
84078493
@Override
84088494
protected float getLeftFadingEdgeStrength() {
84098495
if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return 0.0f;
8410-
if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
8496+
if (mEllipsize == TextUtils.TruncateAt.MARQUEE &&
8497+
mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
84118498
if (mMarquee != null && !mMarquee.isStopped()) {
84128499
final Marquee marquee = mMarquee;
84138500
if (marquee.shouldDrawLeftFade()) {
@@ -8436,7 +8523,8 @@ protected float getLeftFadingEdgeStrength() {
84368523
@Override
84378524
protected float getRightFadingEdgeStrength() {
84388525
if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return 0.0f;
8439-
if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
8526+
if (mEllipsize == TextUtils.TruncateAt.MARQUEE &&
8527+
mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
84408528
if (mMarquee != null && !mMarquee.isStopped()) {
84418529
final Marquee marquee = mMarquee;
84428530
return (marquee.mMaxFadeScroll - marquee.mScroll) / getHorizontalFadingEdgeLength();

0 commit comments

Comments
 (0)