TextView с использованием Spannable - эллипсизация не работает
Проблема, которую я пытаюсь исправить, заключается в следующем: у меня есть TextView
и я использую Spannable
для Spannable
некоторых символов жирным шрифтом. Текст должен иметь максимум 2 строки (android:maxLines="2"
), и я хочу, чтобы текст был эллипсированным, но по какой-то причине я не могу сделать текст эллипсированным.
Вот простой код:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:maxLines="2"
android:ellipsize="end"
android:bufferType="spannable"
android:text="@string/app_name"
android:textSize="15dp"/>
</LinearLayout>
и деятельность:
public class MyActivity extends Activity {
private TextView name;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
name= (TextView) findViewById(R.id.name);
name.setText("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry standard dummy ");
Spannable spannable = (Spannable)name.getText();
StyleSpan boldSpan = new StyleSpan( Typeface.BOLD );
spannable.setSpan( boldSpan, 10, 15, Spannable.SPAN_INCLUSIVE_INCLUSIVE );
}
}
Текст усечен, никаких "..." не отображается. ![enter image description here]()
Ответы
Ответ 1
Я понимаю, что это очень старый пост, но, видя, что он все еще остается без ответа, и я также столкнулся с этим вопросом сегодня, я думал, что опубликую решение для этого. Надеюсь, это поможет кому-то в будущем.
ViewTreeObserver viewTreeObserver = textView.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
ViewTreeObserver viewTreeObserver = textView.getViewTreeObserver();
viewTreeObserver.removeOnGlobalLayoutListener(this);
if (textView.getLineCount() > 5)
{
int endOfLastLine = textView.getLayout().getLineEnd(4);
String newVal = textView.getText().subSequence(0, endOfLastLine - 3) + "...";
textView.setText(newVal);
}
}
});
Ответ 2
Имея такую же проблему и, похоже, для меня работают следующие работы:
Spannable wordtoSpan = new SpannableString(lorem);
wordtoSpan.setSpan(new ForegroundColorSpan(0xffff0000), 0, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
wordtoSpan.setSpan(new ForegroundColorSpan(0xff00ffff), 20, 35, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(wordtoSpan);
в xml textView имеет android:multiLine
set и android:ellipsize="end"
, android:singleLine="false
;
Ответ 3
Вы правы в этом эллипсисе, объявленном либо в xml, либо в коде, который не будет работать с текстом с паутиной.
Тем не менее, с небольшим количеством расследований вы можете фактически сделать эллипсинг самостоятельно:
private TextView name;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
name= (TextView) findViewById(R.id.name);
String lorem = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry standard dummy "
name.setText(lorem);
Spannable spannable = (Spannable)name.getText();
StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
spannable.setSpan( boldSpan, 10, 15, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
int maxLines = 2;
// in my experience, this needs to be called in code, your mileage may vary.
name.setMaxLines(maxLines);
// check line count.. this will actually be > than the # of visible lines
// if it is long enough to be truncated
if (name.getLineCount() > maxLines){
// this returns _1 past_ the index of the last character shown
// on the indicated line. the lines are zero indexed, so the last
// valid line is maxLines -1;
int lastCharShown = name.getLayout().getLineVisibleEnd(maxLines - 1);
// chop off some characters. this value is arbitrary, i chose 3 just
// to be conservative.
int numCharsToChop = 3;
String truncatedText = lorem.substring(0, lastCharShown - numCharsToChop);
// ellipsize! note ellipsis character.
name.setText(truncatedText+"…");
// reapply the span, since the text has been changed.
spannable.setSpan(boldSpan, 10, 15, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
Ответ 4
Это может немного обмануть, используя отражение, чтобы решить эту проблему. После прочтения исходного кода AOSP в TextView.java DynamicLayout содержит только статический член поля с именем sStaticLayout и он создан новым StaticLayout (null) без каких-либо параметров, включая maxLines.
Следовательно, doEllipsis всегда будет false, поскольку mMaximumVisibleLineCount по умолчанию задает Integer.MAX_VALUE.
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);
}
}
Поэтому я расширяю TextView и создаю представление с именем EllipsizeTextView
public class EllipsizeTextView extends TextView {
public EllipsizeTextView(Context context) {
super(context);
}
public EllipsizeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EllipsizeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
public EllipsizeTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
StaticLayout layout = null;
Field field = null;
try {
Field staticField = DynamicLayout.class.getDeclaredField("sStaticLayout");
staticField.setAccessible(true);
layout = (StaticLayout) staticField.get(DynamicLayout.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (layout != null) {
try {
field = StaticLayout.class.getDeclaredField("mMaximumVisibleLineCount");
field.setAccessible(true);
field.setInt(layout, getMaxLines());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (layout != null && field != null) {
try {
field.setInt(layout, Integer.MAX_VALUE);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
задача решена!
Ответ 5
Это известная проблема в системе Android: https://code.google.com/p/android/issues/detail?id=67186
Ответ 6
Простое и эффективное решение
Это мой код ->
<TextView
android:id="@+id/textViewProfileContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="false"
android:ellipsize="end"
android:maxLines="3"
android:textSize="14sp"
android:textColor="#000000" />
SpannableStringBuilder sb = new SpannableStringBuilder();
SpannableString attrAdditional = new SpannableString(additionalText);
attrAdditional.SetSpan(new StyleSpan(TypefaceStyle.Bold), 0, additionalText.Length, 0);...
sb.Append(attrAdditional);...
ProfileContent.SetText(sb, **TextView.BufferType.Normal**);
результат
Ответ 7
![enter image description here]()
https://github.com/lsjwzh/FastTextView имеет для этого чистое разрешение. Сначала необходимо обернуть свою натянутую строку, затем переопределить getSpans, getSpanStart, getSpanEnd... https://github.com/lsjwzh/FastTextView/blob/master/widget.FastTextView/src/main/java/android/text/EllipsisSpannedContainer. Ява
Строка 207 показывает, как использовать. https://github.com/lsjwzh/FastTextView/blob/master/widget.FastTextView/src/main/java/com/lsjwzh/widget/text/FastTextView.java
Вы также можете использовать FastTextView вместо Android TextView
Ответ 8
Другое решение - переписать onDraw
для TextView
. Следующее предложенное решение не использует никакой техники отражения. Следовательно, не должно нарушаться в будущем, в случае изменения имен переменных членов.
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.util.AttributeSet;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
public class EllipsizeTextView extends TextView {
private static final String THREE_DOTS = "...";
private static final int THREE_DOTS_LENGTH = THREE_DOTS.length();
private volatile boolean enableEllipsizeWorkaround = false;
private SpannableStringBuilder spannableStringBuilder;
public EllipsizeTextView(Context context) {
super(context);
}
public EllipsizeTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public EllipsizeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public EllipsizeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void setEnableEllipsizeWorkaround(boolean enableEllipsizeWorkaround) {
this.enableEllipsizeWorkaround = enableEllipsizeWorkaround;
}
// https://stackoverflow.com/questions/14691511/textview-using-spannable-ellipsize-doesnt-work
// https://blog.csdn.net/htyxz8802/article/details/50387950
@Override
protected void onDraw(Canvas canvas) {
if (enableEllipsizeWorkaround && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
final Layout layout = getLayout();
if (layout.getLineCount() >= getMaxLines()) {
CharSequence charSequence = getText();
int lastCharDown = layout.getLineVisibleEnd(getMaxLines()-1);
if (lastCharDown >= THREE_DOTS_LENGTH && charSequence.length() > lastCharDown) {
if (spannableStringBuilder == null) {
spannableStringBuilder = new SpannableStringBuilder();
} else {
spannableStringBuilder.clear();
}
spannableStringBuilder.append(charSequence.subSequence(0, lastCharDown - THREE_DOTS_LENGTH)).append(THREE_DOTS);
setText(spannableStringBuilder);
}
}
}
super.onDraw(canvas);
}
}
Ответ 9
Что касается однострочного случая, android: maxLines не работает ,, но android: singleLine в порядке.
Ответ 10
Попробуйте это для меня.
Это мой текстовый вид
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:ellipsize="end"
android:text="Type here maximum characters."
android:textSize="16sp" />
![enter image description here]()