Fix ellipsizing and text scale x calculation
Bug: 17742721 Bug: 11158757 Change-Id: I98b55a8e661804e84f75e75675c6f2ef33ac1a11
This commit is contained in:
parent
45ebb2001d
commit
049d642cc4
1 changed files with 62 additions and 57 deletions
|
@ -60,6 +60,9 @@ import com.android.inputmethod.latin.utils.ViewLayoutUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
final class SuggestionStripLayoutHelper {
|
final class SuggestionStripLayoutHelper {
|
||||||
private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
|
private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
|
||||||
private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
|
private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
|
||||||
|
@ -213,15 +216,14 @@ final class SuggestionStripLayoutHelper {
|
||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int len = word.length();
|
|
||||||
final Spannable spannedWord = new SpannableString(word);
|
final Spannable spannedWord = new SpannableString(word);
|
||||||
final int options = mSuggestionStripOptions;
|
final int options = mSuggestionStripOptions;
|
||||||
if ((isAutoCorrection && (options & AUTO_CORRECT_BOLD) != 0)
|
if ((isAutoCorrection && (options & AUTO_CORRECT_BOLD) != 0)
|
||||||
|| (isTypedWordValid && (options & VALID_TYPED_WORD_BOLD) != 0)) {
|
|| (isTypedWordValid && (options & VALID_TYPED_WORD_BOLD) != 0)) {
|
||||||
spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
addStyleSpan(spannedWord, BOLD_SPAN);
|
||||||
}
|
}
|
||||||
if (isAutoCorrection && (options & AUTO_CORRECT_UNDERLINE) != 0) {
|
if (isAutoCorrection && (options & AUTO_CORRECT_UNDERLINE) != 0) {
|
||||||
spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
addStyleSpan(spannedWord, UNDERLINE_SPAN);
|
||||||
}
|
}
|
||||||
return spannedWord;
|
return spannedWord;
|
||||||
}
|
}
|
||||||
|
@ -446,10 +448,11 @@ final class SuggestionStripLayoutHelper {
|
||||||
// {@link StyleSpan} in a content description may cause an issue of TTS/TalkBack.
|
// {@link StyleSpan} in a content description may cause an issue of TTS/TalkBack.
|
||||||
// Use a simple {@link String} to avoid the issue.
|
// Use a simple {@link String} to avoid the issue.
|
||||||
wordView.setContentDescription(TextUtils.isEmpty(word) ? null : word.toString());
|
wordView.setContentDescription(TextUtils.isEmpty(word) ? null : word.toString());
|
||||||
final CharSequence text = getEllipsizedText(word, width, wordView.getPaint());
|
final CharSequence text = getEllipsizedTextWithSettingScaleX(
|
||||||
final float scaleX = getTextScaleX(word, width, wordView.getPaint());
|
word, width, wordView.getPaint());
|
||||||
|
final float scaleX = wordView.getTextScaleX();
|
||||||
wordView.setText(text); // TextView.setText() resets text scale x to 1.0.
|
wordView.setText(text); // TextView.setText() resets text scale x to 1.0.
|
||||||
wordView.setTextScaleX(Math.max(scaleX, MIN_TEXT_XSCALE));
|
wordView.setTextScaleX(scaleX);
|
||||||
// A <code>wordView</code> should be disabled when <code>word</code> is empty in order to
|
// A <code>wordView</code> should be disabled when <code>word</code> is empty in order to
|
||||||
// make it unclickable.
|
// make it unclickable.
|
||||||
// With accessibility touch exploration on, <code>wordView</code> should be enabled even
|
// With accessibility touch exploration on, <code>wordView</code> should be enabled even
|
||||||
|
@ -562,7 +565,8 @@ final class SuggestionStripLayoutHelper {
|
||||||
final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save);
|
final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save);
|
||||||
wordView.setTextColor(mColorTypedWord);
|
wordView.setTextColor(mColorTypedWord);
|
||||||
final int wordWidth = (int)(width * mCenterSuggestionWeight);
|
final int wordWidth = (int)(width * mCenterSuggestionWeight);
|
||||||
final CharSequence wordToSave = getEllipsizedText(word, wordWidth, wordView.getPaint());
|
final CharSequence wordToSave = getEllipsizedTextWithSettingScaleX(
|
||||||
|
word, wordWidth, wordView.getPaint());
|
||||||
final float wordScaleX = wordView.getTextScaleX();
|
final float wordScaleX = wordView.getTextScaleX();
|
||||||
wordView.setText(wordToSave);
|
wordView.setText(wordToSave);
|
||||||
wordView.setTextScaleX(wordScaleX);
|
wordView.setTextScaleX(wordScaleX);
|
||||||
|
@ -596,7 +600,7 @@ final class SuggestionStripLayoutHelper {
|
||||||
}
|
}
|
||||||
hintView.setTextColor(mColorAutoCorrect);
|
hintView.setTextColor(mColorAutoCorrect);
|
||||||
final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
|
final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
|
||||||
hintView.setText(hintText);
|
hintView.setText(hintText); // TextView.setText() resets text scale x to 1.0.
|
||||||
hintView.setTextScaleX(hintScaleX);
|
hintView.setTextScaleX(hintScaleX);
|
||||||
setLayoutWeight(hintView, hintWeight, ViewGroup.LayoutParams.MATCH_PARENT);
|
setLayoutWeight(hintView, hintWeight, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||||
}
|
}
|
||||||
|
@ -608,8 +612,7 @@ final class SuggestionStripLayoutHelper {
|
||||||
final int width = titleView.getWidth() - titleView.getPaddingLeft()
|
final int width = titleView.getWidth() - titleView.getPaddingLeft()
|
||||||
- titleView.getPaddingRight();
|
- titleView.getPaddingRight();
|
||||||
titleView.setTextColor(mColorAutoCorrect);
|
titleView.setTextColor(mColorAutoCorrect);
|
||||||
titleView.setText(importantNoticeTitle);
|
titleView.setText(importantNoticeTitle); // TextView.setText() resets text scale x to 1.0.
|
||||||
titleView.setTextScaleX(1.0f); // Reset textScaleX.
|
|
||||||
final float titleScaleX = getTextScaleX(importantNoticeTitle, width, titleView.getPaint());
|
final float titleScaleX = getTextScaleX(importantNoticeTitle, width, titleView.getPaint());
|
||||||
titleView.setTextScaleX(titleScaleX);
|
titleView.setTextScaleX(titleScaleX);
|
||||||
}
|
}
|
||||||
|
@ -624,7 +627,7 @@ final class SuggestionStripLayoutHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float getTextScaleX(final CharSequence text, final int maxWidth,
|
private static float getTextScaleX(@Nullable final CharSequence text, final int maxWidth,
|
||||||
final TextPaint paint) {
|
final TextPaint paint) {
|
||||||
paint.setTextScaleX(1.0f);
|
paint.setTextScaleX(1.0f);
|
||||||
final int width = getTextWidth(text, paint);
|
final int width = getTextWidth(text, paint);
|
||||||
|
@ -634,8 +637,9 @@ final class SuggestionStripLayoutHelper {
|
||||||
return maxWidth / (float) width;
|
return maxWidth / (float) width;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
|
@Nullable
|
||||||
final TextPaint paint) {
|
private static CharSequence getEllipsizedTextWithSettingScaleX(
|
||||||
|
@Nullable final CharSequence text, final int maxWidth, @Nonnull final TextPaint paint) {
|
||||||
if (text == null) {
|
if (text == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -645,62 +649,63 @@ final class SuggestionStripLayoutHelper {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To
|
// <code>text</code> must be ellipsized with minimum text scale x.
|
||||||
// get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
|
|
||||||
final float upscaledWidth = maxWidth / MIN_TEXT_XSCALE;
|
|
||||||
CharSequence ellipsized = TextUtils.ellipsize(
|
|
||||||
text, paint, upscaledWidth, TextUtils.TruncateAt.MIDDLE);
|
|
||||||
// For an unknown reason, ellipsized seems to return a text that does indeed fit inside the
|
|
||||||
// passed width according to paint.measureText, but not according to paint.getTextWidths.
|
|
||||||
// But when rendered, the text seems to actually take up as many pixels as returned by
|
|
||||||
// paint.getTextWidths, hence problem.
|
|
||||||
// To save this case, we compare the measured size of the new text, and if it's too much,
|
|
||||||
// try it again removing the difference. This may still give a text too long by one or
|
|
||||||
// two pixels so we take an additional 2 pixels cushion and call it a day.
|
|
||||||
// TODO: figure out why getTextWidths and measureText don't agree with each other, and
|
|
||||||
// remove the following code.
|
|
||||||
final float ellipsizedTextWidth = getTextWidth(ellipsized, paint);
|
|
||||||
if (upscaledWidth <= ellipsizedTextWidth) {
|
|
||||||
ellipsized = TextUtils.ellipsize(
|
|
||||||
text, paint, upscaledWidth - (ellipsizedTextWidth - upscaledWidth) - 2,
|
|
||||||
TextUtils.TruncateAt.MIDDLE);
|
|
||||||
}
|
|
||||||
paint.setTextScaleX(MIN_TEXT_XSCALE);
|
paint.setTextScaleX(MIN_TEXT_XSCALE);
|
||||||
return ellipsized;
|
final boolean hasBoldStyle = hasStyleSpan(text, BOLD_SPAN);
|
||||||
|
final boolean hasUnderlineStyle = hasStyleSpan(text, UNDERLINE_SPAN);
|
||||||
|
// TextUtils.ellipsize erases any span object existed after ellipsized point.
|
||||||
|
// We have to restore these spans afterward.
|
||||||
|
final CharSequence ellipsizedText = TextUtils.ellipsize(
|
||||||
|
text, paint, maxWidth, TextUtils.TruncateAt.MIDDLE);
|
||||||
|
if (!hasBoldStyle && !hasUnderlineStyle) {
|
||||||
|
return ellipsizedText;
|
||||||
|
}
|
||||||
|
final Spannable spannableText = (ellipsizedText instanceof Spannable)
|
||||||
|
? (Spannable)ellipsizedText : new SpannableString(ellipsizedText);
|
||||||
|
if (hasBoldStyle) {
|
||||||
|
addStyleSpan(spannableText, BOLD_SPAN);
|
||||||
|
}
|
||||||
|
if (hasUnderlineStyle) {
|
||||||
|
addStyleSpan(spannableText, UNDERLINE_SPAN);
|
||||||
|
}
|
||||||
|
return spannableText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getTextWidth(final CharSequence text, final TextPaint paint) {
|
private static boolean hasStyleSpan(@Nullable final CharSequence text,
|
||||||
|
final CharacterStyle style) {
|
||||||
|
if (text instanceof Spanned) {
|
||||||
|
return ((Spanned)text).getSpanStart(style) >= 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addStyleSpan(@Nonnull final Spannable text, final CharacterStyle style) {
|
||||||
|
text.removeSpan(style);
|
||||||
|
text.setSpan(style, 0, text.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getTextWidth(@Nullable final CharSequence text, final TextPaint paint) {
|
||||||
if (TextUtils.isEmpty(text)) {
|
if (TextUtils.isEmpty(text)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
final int length = text.length();
|
||||||
|
final float[] widths = new float[length];
|
||||||
|
final int count;
|
||||||
final Typeface savedTypeface = paint.getTypeface();
|
final Typeface savedTypeface = paint.getTypeface();
|
||||||
|
try {
|
||||||
paint.setTypeface(getTextTypeface(text));
|
paint.setTypeface(getTextTypeface(text));
|
||||||
final int len = text.length();
|
count = paint.getTextWidths(text, 0, length, widths);
|
||||||
final float[] widths = new float[len];
|
} finally {
|
||||||
final int count = paint.getTextWidths(text, 0, len, widths);
|
paint.setTypeface(savedTypeface);
|
||||||
|
}
|
||||||
int width = 0;
|
int width = 0;
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
width += Math.round(widths[i] + 0.5f);
|
width += Math.round(widths[i] + 0.5f);
|
||||||
}
|
}
|
||||||
paint.setTypeface(savedTypeface);
|
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Typeface getTextTypeface(final CharSequence text) {
|
private static Typeface getTextTypeface(@Nullable final CharSequence text) {
|
||||||
if (!(text instanceof SpannableString)) {
|
return hasStyleSpan(text, BOLD_SPAN) ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT;
|
||||||
return Typeface.DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
final SpannableString ss = (SpannableString)text;
|
|
||||||
final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class);
|
|
||||||
if (styles.length == 0) {
|
|
||||||
return Typeface.DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (styles[0].getStyle() == Typeface.BOLD) {
|
|
||||||
return Typeface.DEFAULT_BOLD;
|
|
||||||
}
|
|
||||||
// TODO: BOLD_ITALIC, ITALIC case?
|
|
||||||
return Typeface.DEFAULT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue