28

我要解决的问题如下:我有 aTextView并且我正在使用 aSpannable将一些字符设置为粗体。文本需要有 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's standard dummy ");
        Spannable spannable = (Spannable)name.getText();
        StyleSpan boldSpan = new StyleSpan( Typeface.BOLD );
        spannable.setSpan( boldSpan, 10, 15, Spannable.SPAN_INCLUSIVE_INCLUSIVE );

    }
}

文本被截断,不显示“...”。 在此处输入图像描述

4

11 回答 11

16

我意识到这是一个非常古老的帖子,但是看到它仍然没有答案,我今天也遇到了这个问题,我想我会发布一个解决方案。希望它可以帮助将来的人。

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);
        }
    }
});
于 2015-05-25T22:28:30.390 回答
11

有同样的问题,似乎以下对我有用:

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设置,和android:ellipsize="end",和android:singleLine="false

于 2014-02-08T23:14:46.347 回答
7

您在那个椭圆中是对的,在 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's 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);
    }

}
于 2013-03-07T16:12:14.873 回答
5

通过使用反射来解决这个问题,这可能是一个小技巧。看了AOSP的源码,TextView.java中DynamicLayout只包含一个名为sStaticLayout的静态字段成员,它是由new 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();
        }
    }
}

}

问题解决了!

于 2016-11-09T11:02:04.643 回答
2

另一种解决方案是覆盖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);
    }
}
于 2019-03-22T18:23:25.770 回答
1

至于单行情况,android:maxLines不工作,但android:singleLine 没问题

于 2019-06-04T03:11:04.240 回答
1

这是 Android 框架中的一个已知问题:https ://code.google.com/p/android/issues/detail?id=67186

于 2015-07-08T11:11:37.453 回答
1

一个简单而有效的解决方案

这是我的代码->

<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**);

结果

于 2017-01-26T11:52:25.083 回答
0

在此处输入图像描述

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

于 2017-10-31T14:31:54.640 回答
0
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if (maxLength < 0) {
        Layout layout = getLayout();
        if (layout.getLineCount() > getMaxLines()) {
            maxLength = layout.getLineVisibleEnd(getMaxLines() - 1) - 1;
            setSpannableString();
        }
    }
}

“setSpannableString()”如下所示:

private void setSpannableString() {
    // ShowString is real visible string. FullString is original string which
    // is useful when caching and calculating.
    String showString = fullString;
    if (maxLength > 0) {
        showString = fullString.substring(0, maxLength) + THREE_DOTS;
    }
    SpannableStringBuilder builder = new SpannableStringBuilder(showString);
    for (int i = 0; i < mHighLightColor.size(); i++) {
        String highLightString = mHighLightString.get(i);
        int color = mHighLightColor.get(i);
        int start = fullString.indexOf(highLightString);
        int end = Math.min(start + highLightString.length(), showString.length());
        if (mClickableSpanList.get(i) != null) {
            builder.setSpan(mClickableSpanList.get(i), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            setMovementMethod(LinkMovementMethod.getInstance());
        }
        builder.setSpan(new ColorBoldSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
    setText(builder, BufferType.SPANNABLE);
}
于 2020-06-03T07:41:39.997 回答
-4

试试这对我有用。

这是我的文本视图

 <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" />

在此处输入图像描述

于 2018-04-12T13:45:33.820 回答