Reduce emoji flickering and other ellipsize woes.
1. Switch to using default text rendering if there's no emoji present in the string. 2. Reduce redudant redraws by skipping of setText() calls for identical strings. Together, these two changes should reduce the vast majority of flickering we see with EmojiTextView ellipsizing.
This commit is contained in:
parent
70c2a863cc
commit
ceafb0d130
1 changed files with 44 additions and 31 deletions
|
@ -11,18 +11,19 @@ import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.ViewTreeObserver;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
|
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
|
||||||
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser;
|
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
|
|
||||||
public class EmojiTextView extends AppCompatTextView {
|
public class EmojiTextView extends AppCompatTextView {
|
||||||
|
|
||||||
private final boolean scaleEmojis;
|
private final boolean scaleEmojis;
|
||||||
|
|
||||||
private CharSequence source;
|
private CharSequence originalText;
|
||||||
private float originalFontSize;
|
private float originalFontSize;
|
||||||
|
|
||||||
public EmojiTextView(Context context) {
|
public EmojiTextView(Context context) {
|
||||||
|
@ -46,6 +47,12 @@ public class EmojiTextView extends AppCompatTextView {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void setText(@Nullable CharSequence text, BufferType type) {
|
@Override public void setText(@Nullable CharSequence text, BufferType type) {
|
||||||
|
if (Util.equals(originalText, text)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
originalText = text;
|
||||||
|
|
||||||
EmojiProvider provider = EmojiProvider.getInstance(getContext());
|
EmojiProvider provider = EmojiProvider.getInstance(getContext());
|
||||||
EmojiParser.CandidateList candidates = provider.getCandidates(text);
|
EmojiParser.CandidateList candidates = provider.getCandidates(text);
|
||||||
|
|
||||||
|
@ -61,37 +68,51 @@ public class EmojiTextView extends AppCompatTextView {
|
||||||
super.setTextSize(TypedValue.COMPLEX_UNIT_PX, originalFontSize);
|
super.setTextSize(TypedValue.COMPLEX_UNIT_PX, originalFontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useSystemEmoji()) {
|
if (useSystemEmoji() || candidates == null || candidates.size() == 0) {
|
||||||
super.setText(text, type);
|
super.setText(text, BufferType.NORMAL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
source = EmojiProvider.getInstance(getContext()).emojify(candidates, text, this);
|
CharSequence emojified = provider.emojify(candidates, text, this);
|
||||||
super.setText(source, BufferType.SPANNABLE);
|
super.setText(emojified, BufferType.SPANNABLE);
|
||||||
|
|
||||||
// Android fails to ellipsize spannable strings. (https://issuetracker.google.com/issues/36991688)
|
// Android fails to ellipsize spannable strings. (https://issuetracker.google.com/issues/36991688)
|
||||||
// We ellipsize them ourselves by manually truncating the appropriate section.
|
// We ellipsize them ourselves by manually truncating the appropriate section.
|
||||||
if (getEllipsize() == TextUtils.TruncateAt.END) {
|
if (getEllipsize() == TextUtils.TruncateAt.END) {
|
||||||
post(() -> {
|
ellipsize();
|
||||||
int maxLines = TextViewCompat.getMaxLines(EmojiTextView.this);
|
|
||||||
if (maxLines <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int lineCount = getLineCount();
|
|
||||||
if (lineCount > maxLines) {
|
|
||||||
int overflowStart = getLayout().getLineStart(maxLines - 1);
|
|
||||||
CharSequence overflow = getText().subSequence(overflowStart, getText().length());
|
|
||||||
CharSequence ellipsized = TextUtils.ellipsize(overflow, getPaint(), getWidth(), TextUtils.TruncateAt.END);
|
|
||||||
|
|
||||||
SpannableStringBuilder newContent = new SpannableStringBuilder();
|
|
||||||
newContent.append(getText().subSequence(0, overflowStart))
|
|
||||||
.append(ellipsized);
|
|
||||||
super.setText(newContent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ellipsize() {
|
||||||
|
post(() -> {
|
||||||
|
if (getLayout() == null || getLineCount() == 0) {
|
||||||
|
ellipsize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxLines = TextViewCompat.getMaxLines(EmojiTextView.this);
|
||||||
|
if (maxLines <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lineCount = getLineCount();
|
||||||
|
if (lineCount > maxLines) {
|
||||||
|
int overflowStart = getLayout().getLineStart(maxLines - 1);
|
||||||
|
CharSequence overflow = getText().subSequence(overflowStart, getText().length());
|
||||||
|
CharSequence ellipsized = TextUtils.ellipsize(overflow, getPaint(), getWidth(), TextUtils.TruncateAt.END);
|
||||||
|
|
||||||
|
SpannableStringBuilder newContent = new SpannableStringBuilder();
|
||||||
|
newContent.append(getText().subSequence(0, overflowStart))
|
||||||
|
.append(ellipsized.subSequence(0, ellipsized.length()));
|
||||||
|
|
||||||
|
EmojiParser.CandidateList newCandidates = EmojiProvider.getInstance(getContext()).getCandidates(newContent);
|
||||||
|
CharSequence emojified = EmojiProvider.getInstance(getContext()).emojify(newCandidates, newContent, this);
|
||||||
|
|
||||||
|
super.setText(emojified, BufferType.SPANNABLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private boolean useSystemEmoji() {
|
private boolean useSystemEmoji() {
|
||||||
return TextSecurePreferences.isSystemEmojiPreferred(getContext());
|
return TextSecurePreferences.isSystemEmojiPreferred(getContext());
|
||||||
}
|
}
|
||||||
|
@ -101,14 +122,6 @@ public class EmojiTextView extends AppCompatTextView {
|
||||||
else super.invalidateDrawable(drawable);
|
else super.invalidateDrawable(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
||||||
if (changed && !useSystemEmoji()) {
|
|
||||||
super.setText(source, BufferType.SPANNABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onLayout(changed, left, top, right, bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTextSize(float size) {
|
public void setTextSize(float size) {
|
||||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
|
setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
|
||||||
|
|
Loading…
Add table
Reference in a new issue