Мне нужно эллипсировать многострочный текст. Мой компонент достаточно велик, чтобы отображать не менее 4 строк с эллипсом, но отображаются только две строки. Я попытался изменить минимальное и максимальное количество строк компонента, но ничего не изменил.
Андроидный эллипсовый многострочный текст
Ответ 1
Вот решение проблемы. Это подкласс TextView, который фактически работает для эллипсинга. Код android-textview-multiline-эллипс, указанный в более раннем ответе, который я обнаружил в определенных обстоятельствах неправильно, а также под GPL, что на самом деле не работает для большинства из нас. Не стесняйтесь использовать этот код свободно и без атрибуции или по лицензии Apache, если хотите. Обратите внимание, что есть слушатель, который уведомляет вас, когда текст становится эллипсированным, что я нашел весьма полезным.
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.text.Layout;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.widget.TextView;
public class EllipsizingTextView extends TextView {
private static final String ELLIPSIS = "...";
public interface EllipsizeListener {
void ellipsizeStateChanged(boolean ellipsized);
}
private final List<EllipsizeListener> ellipsizeListeners = new ArrayList<EllipsizeListener>();
private boolean isEllipsized;
private boolean isStale;
private boolean programmaticChange;
private String fullText;
private int maxLines = -1;
private float lineSpacingMultiplier = 1.0f;
private float lineAdditionalVerticalPadding = 0.0f;
public EllipsizingTextView(Context context) {
super(context);
}
public EllipsizingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void addEllipsizeListener(EllipsizeListener listener) {
if (listener == null) {
throw new NullPointerException();
}
ellipsizeListeners.add(listener);
}
public void removeEllipsizeListener(EllipsizeListener listener) {
ellipsizeListeners.remove(listener);
}
public boolean isEllipsized() {
return isEllipsized;
}
@Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
this.maxLines = maxLines;
isStale = true;
}
public int getMaxLines() {
return maxLines;
}
@Override
public void setLineSpacing(float add, float mult) {
this.lineAdditionalVerticalPadding = add;
this.lineSpacingMultiplier = mult;
super.setLineSpacing(add, mult);
}
@Override
protected void onTextChanged(CharSequence text, int start, int before, int after) {
super.onTextChanged(text, start, before, after);
if (!programmaticChange) {
fullText = text.toString();
isStale = true;
}
}
@Override
protected void onDraw(Canvas canvas) {
if (isStale) {
super.setEllipsize(null);
resetText();
}
super.onDraw(canvas);
}
private void resetText() {
int maxLines = getMaxLines();
String workingText = fullText;
boolean ellipsized = false;
if (maxLines != -1) {
Layout layout = createWorkingLayout(workingText);
if (layout.getLineCount() > maxLines) {
workingText = fullText.substring(0, layout.getLineEnd(maxLines - 1)).trim();
while (createWorkingLayout(workingText + ELLIPSIS).getLineCount() > maxLines) {
int lastSpace = workingText.lastIndexOf(' ');
if (lastSpace == -1) {
break;
}
workingText = workingText.substring(0, lastSpace);
}
workingText = workingText + ELLIPSIS;
ellipsized = true;
}
}
if (!workingText.equals(getText())) {
programmaticChange = true;
try {
setText(workingText);
} finally {
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized;
for (EllipsizeListener listener : ellipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized);
}
}
}
private Layout createWorkingLayout(String workingText) {
return new StaticLayout(workingText, getPaint(), getWidth() - getPaddingLeft() - getPaddingRight(),
Alignment.ALIGN_NORMAL, lineSpacingMultiplier, lineAdditionalVerticalPadding, false);
}
@Override
public void setEllipsize(TruncateAt where) {
// Ellipsize settings are not respected
}
}
Ответ 2
В моем приложении у меня была аналогичная проблема: 2 строки строки и, в конце концов, добавить "...", если строка была слишком длинной. Я использовал этот код в XML файле в теге textview:
android:maxLines="2"
android:ellipsize="end"
android:singleLine="false"
Ответ 3
Я тоже столкнулся с этой проблемой. Там довольно старая ошибка, которая остается без ответа: Ошибка 2254
Ответ 4
Попробуйте это, он работает для меня, у меня есть 4 строки, и он добавляет "..." в конец последней/четвертой строки. Это то же самое, что и моральный ответ, но у меня есть singeLine = "false".
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLines="4"
android:ellipsize="marquee"
android:singleLine="false"
android:text="Hi make this a very long string that wraps at least 4 lines, seriously make it really really long so it gets cut off at the fourth line not joke. Just do it!" />
Ответ 5
Получил эту проблему, и, наконец, я построю короткое решение. Вам просто нужно эллипсонировать вручную нужную строку, ваш атрибут maxLine сократит ваш текст.
В этом примере вырезаем текст для 3 строк max
final TextView title = (TextView)findViewById(R.id.text);
title.setText("A really long text");
ViewTreeObserver vto = title.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewTreeObserver obs = title.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
if(title.getLineCount() > 3){
Log.d("","Line["+title.getLineCount()+"]"+title.getText());
int lineEndIndex = title.getLayout().getLineEnd(2);
String text = title.getText().subSequence(0, lineEndIndex-3)+"...";
title.setText(text);
Log.d("","NewText:"+text);
}
}
});
Ответ 6
Я объединил решения Micah Hainline, Alex Băluţ и Paul Imhoff, чтобы создать эллипсирующий многострочный TextView
, который также поддерживает текст Spanned
.
Вам нужно только установить android:ellipsize
и android:maxLines
.
/*
* Copyright (C) 2011 Micah Hainline
* Copyright (C) 2012 Triposo
* Copyright (C) 2013 Paul Imhoff
* Copyright (C) 2014 Shahin Yousefi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.text.Layout;
import android.text.Layout.Alignment;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class EllipsizingTextView extends TextView {
private static final CharSequence ELLIPSIS = "\u2026";
private static final Pattern DEFAULT_END_PUNCTUATION
= Pattern.compile("[\\.!?,;:\u2026]*$", Pattern.DOTALL);
private final List<EllipsizeListener> mEllipsizeListeners = new ArrayList<>();
private EllipsizeStrategy mEllipsizeStrategy;
private boolean isEllipsized;
private boolean isStale;
private boolean programmaticChange;
private CharSequence mFullText;
private int mMaxLines;
private float mLineSpacingMult = 1.0f;
private float mLineAddVertPad = 0.0f;
private Pattern mEndPunctPattern;
public EllipsizingTextView(Context context) {
this(context, null);
}
public EllipsizingTextView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
new int[]{ android.R.attr.maxLines }, defStyle, 0);
setMaxLines(a.getInt(0, Integer.MAX_VALUE));
a.recycle();
setEndPunctuationPattern(DEFAULT_END_PUNCTUATION);
}
public void setEndPunctuationPattern(Pattern pattern) {
mEndPunctPattern = pattern;
}
public void addEllipsizeListener(@NonNull EllipsizeListener listener) {
mEllipsizeListeners.add(listener);
}
public void removeEllipsizeListener(EllipsizeListener listener) {
mEllipsizeListeners.remove(listener);
}
public boolean isEllipsized() {
return isEllipsized;
}
@SuppressLint("Override")
public int getMaxLines() {
return mMaxLines;
}
@Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
mMaxLines = maxLines;
isStale = true;
}
public boolean ellipsizingLastFullyVisibleLine() {
return mMaxLines == Integer.MAX_VALUE;
}
@Override
public void setLineSpacing(float add, float mult) {
mLineAddVertPad = add;
mLineSpacingMult = mult;
super.setLineSpacing(add, mult);
}
@Override
public void setText(CharSequence text, BufferType type) {
if (!programmaticChange) {
mFullText = text;
isStale = true;
}
super.setText(text, type);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (ellipsizingLastFullyVisibleLine()) isStale = true;
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
if (ellipsizingLastFullyVisibleLine()) isStale = true;
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
if (isStale) resetText();
super.onDraw(canvas);
}
private void resetText() {
int maxLines = getMaxLines();
CharSequence workingText = mFullText;
boolean ellipsized = false;
if (maxLines != -1) {
if (mEllipsizeStrategy == null) setEllipsize(null);
workingText = mEllipsizeStrategy.processText(mFullText);
ellipsized = !mEllipsizeStrategy.isInLayout(mFullText);
}
if (!workingText.equals(getText())) {
programmaticChange = true;
try {
setText(workingText);
} finally {
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized;
for (EllipsizeListener listener : mEllipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized);
}
}
}
@Override
public void setEllipsize(TruncateAt where) {
if (where == null) {
mEllipsizeStrategy = new EllipsizeNoneStrategy();
return;
}
switch (where) {
case END:
mEllipsizeStrategy = new EllipsizeEndStrategy();
break;
case START:
mEllipsizeStrategy = new EllipsizeStartStrategy();
break;
case MIDDLE:
mEllipsizeStrategy = new EllipsizeMiddleStrategy();
break;
case MARQUEE:
super.setEllipsize(where);
isStale = false;
default:
mEllipsizeStrategy = new EllipsizeNoneStrategy();
break;
}
}
public interface EllipsizeListener {
void ellipsizeStateChanged(boolean ellipsized);
}
private abstract class EllipsizeStrategy {
public CharSequence processText(CharSequence text) {
return !isInLayout(text) ? createEllipsizedText(text) : text;
}
public boolean isInLayout(CharSequence text) {
Layout layout = createWorkingLayout(text);
return layout.getLineCount() <= getLinesCount();
}
protected Layout createWorkingLayout(CharSequence workingText) {
return new StaticLayout(workingText, getPaint(),
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
Alignment.ALIGN_NORMAL, mLineSpacingMult,
mLineAddVertPad, false /* includepad */);
}
protected int getLinesCount() {
if (ellipsizingLastFullyVisibleLine()) {
int fullyVisibleLinesCount = getFullyVisibleLinesCount();
return fullyVisibleLinesCount == -1 ? 1 : fullyVisibleLinesCount;
} else {
return mMaxLines;
}
}
protected int getFullyVisibleLinesCount() {
Layout layout = createWorkingLayout("");
int height = getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
int lineHeight = layout.getLineBottom(0);
return height / lineHeight;
}
protected abstract CharSequence createEllipsizedText(CharSequence fullText);
}
private class EllipsizeNoneStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
return fullText;
}
}
private class EllipsizeEndStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
Layout layout = createWorkingLayout(fullText);
int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
int textLength = fullText.length();
int cutOffLength = textLength - cutOffIndex;
if (cutOffLength < ELLIPSIS.length()) cutOffLength = ELLIPSIS.length();
String workingText = TextUtils.substring(fullText, 0, textLength - cutOffLength).trim();
String strippedText = stripEndPunctuation(workingText);
while (!isInLayout(strippedText + ELLIPSIS)) {
int lastSpace = workingText.lastIndexOf(' ');
if (lastSpace == -1) break;
workingText = workingText.substring(0, lastSpace).trim();
strippedText = stripEndPunctuation(workingText);
}
workingText = strippedText + ELLIPSIS;
SpannableStringBuilder dest = new SpannableStringBuilder(workingText);
if (fullText instanceof Spanned) {
TextUtils.copySpansFrom((Spanned) fullText, 0, workingText.length(), null, dest, 0);
}
return dest;
}
public String stripEndPunctuation(CharSequence workingText) {
return mEndPunctPattern.matcher(workingText).replaceFirst("");
}
}
private class EllipsizeStartStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
Layout layout = createWorkingLayout(fullText);
int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
int textLength = fullText.length();
int cutOffLength = textLength - cutOffIndex;
if (cutOffLength < ELLIPSIS.length()) cutOffLength = ELLIPSIS.length();
String workingText = TextUtils.substring(fullText, cutOffLength, textLength).trim();
while (!isInLayout(ELLIPSIS + workingText)) {
int firstSpace = workingText.indexOf(' ');
if (firstSpace == -1) break;
workingText = workingText.substring(firstSpace, workingText.length()).trim();
}
workingText = ELLIPSIS + workingText;
SpannableStringBuilder dest = new SpannableStringBuilder(workingText);
if (fullText instanceof Spanned) {
TextUtils.copySpansFrom((Spanned) fullText, textLength - workingText.length(),
textLength, null, dest, 0);
}
return dest;
}
}
private class EllipsizeMiddleStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
Layout layout = createWorkingLayout(fullText);
int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
int textLength = fullText.length();
int cutOffLength = textLength - cutOffIndex;
if (cutOffLength < ELLIPSIS.length()) cutOffLength = ELLIPSIS.length();
cutOffLength += cutOffIndex % 2; // Make it even.
String firstPart = TextUtils.substring(
fullText, 0, textLength / 2 - cutOffLength / 2).trim();
String secondPart = TextUtils.substring(
fullText, textLength / 2 + cutOffLength / 2, textLength).trim();
while (!isInLayout(firstPart + ELLIPSIS + secondPart)) {
int lastSpaceFirstPart = firstPart.lastIndexOf(' ');
int firstSpaceSecondPart = secondPart.indexOf(' ');
if (lastSpaceFirstPart == -1 || firstSpaceSecondPart == -1) break;
firstPart = firstPart.substring(0, lastSpaceFirstPart).trim();
secondPart = secondPart.substring(firstSpaceSecondPart, secondPart.length()).trim();
}
SpannableStringBuilder firstDest = new SpannableStringBuilder(firstPart);
SpannableStringBuilder secondDest = new SpannableStringBuilder(secondPart);
if (fullText instanceof Spanned) {
TextUtils.copySpansFrom((Spanned) fullText, 0, firstPart.length(),
null, firstDest, 0);
TextUtils.copySpansFrom((Spanned) fullText, textLength - secondPart.length(),
textLength, null, secondDest, 0);
}
return TextUtils.concat(firstDest, ELLIPSIS, secondDest);
}
}
}
Полный источник: EllipsizingTextView.java
Ответ 7
На основе решений Micah Hainline и alebs comment я пришел со следующим подходом, который работает со Spanned текстами, так что, например, myTextView.setText(Html.fromHtml("<b>Testheader</b> - Testcontent"));
работает!
Обратите внимание, что это работает только с Spanned
прямо сейчас. Его можно было бы изменить для работы с String
и Spanned
в любом случае.
public class EllipsizingTextView extends TextView {
private static final Spanned ELLIPSIS = new SpannedString("…");
public interface EllipsizeListener {
void ellipsizeStateChanged(boolean ellipsized);
}
private final List<EllipsizeListener> ellipsizeListeners = new ArrayList<EllipsizeListener>();
private boolean isEllipsized;
private boolean isStale;
private boolean programmaticChange;
private Spanned fullText;
private int maxLines;
private float lineSpacingMultiplier = 1.0f;
private float lineAdditionalVerticalPadding = 0.0f;
public EllipsizingTextView(Context context) {
this(context, null);
}
public EllipsizingTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
super.setEllipsize(null);
TypedArray a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.maxLines });
setMaxLines(a.getInt(0, Integer.MAX_VALUE));
}
public void addEllipsizeListener(EllipsizeListener listener) {
if (listener == null) {
throw new NullPointerException();
}
ellipsizeListeners.add(listener);
}
public void removeEllipsizeListener(EllipsizeListener listener) {
ellipsizeListeners.remove(listener);
}
public boolean isEllipsized() {
return isEllipsized;
}
@Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
this.maxLines = maxLines;
isStale = true;
}
public int getMaxLines() {
return maxLines;
}
public boolean ellipsizingLastFullyVisibleLine() {
return maxLines == Integer.MAX_VALUE;
}
@Override
public void setLineSpacing(float add, float mult) {
this.lineAdditionalVerticalPadding = add;
this.lineSpacingMultiplier = mult;
super.setLineSpacing(add, mult);
}
@Override
public void setText(CharSequence text, BufferType type) {
if (!programmaticChange && text instanceof Spanned) {
fullText = (Spanned) text;
isStale = true;
}
super.setText(text, type);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (ellipsizingLastFullyVisibleLine()) {
isStale = true;
}
}
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
if (ellipsizingLastFullyVisibleLine()) {
isStale = true;
}
}
@Override
protected void onDraw(Canvas canvas) {
if (isStale) {
resetText();
}
super.onDraw(canvas);
}
private void resetText() {
Spanned workingText = fullText;
boolean ellipsized = false;
Layout layout = createWorkingLayout(workingText);
int linesCount = getLinesCount();
if (layout.getLineCount() > linesCount) {
// We have more lines of text than we are allowed to display.
workingText = (Spanned) fullText.subSequence(0, layout.getLineEnd(linesCount - 1));
while (createWorkingLayout((Spanned) TextUtils.concat(workingText, ELLIPSIS)).getLineCount() > linesCount) {
int lastSpace = workingText.toString().lastIndexOf(' ');
if (lastSpace == -1) {
break;
}
workingText = (Spanned) workingText.subSequence(0, lastSpace);
}
workingText = (Spanned) TextUtils.concat(workingText, ELLIPSIS);
ellipsized = true;
}
if (!workingText.equals(getText())) {
programmaticChange = true;
try {
setText(workingText);
} finally {
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized;
for (EllipsizeListener listener : ellipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized);
}
}
}
/**
* Get how many lines of text we are allowed to display.
*/
private int getLinesCount() {
if (ellipsizingLastFullyVisibleLine()) {
int fullyVisibleLinesCount = getFullyVisibleLinesCount();
if (fullyVisibleLinesCount == -1) {
return 1;
} else {
return fullyVisibleLinesCount;
}
} else {
return maxLines;
}
}
/**
* Get how many lines of text we can display so their full height is visible.
*/
private int getFullyVisibleLinesCount() {
Layout layout = createWorkingLayout(new SpannedString(""));
int height = getHeight() - getPaddingTop() - getPaddingBottom();
int lineHeight = layout.getLineBottom(0);
return height / lineHeight;
}
private Layout createWorkingLayout(Spanned workingText) {
return new StaticLayout(workingText, getPaint(),
getWidth() - getPaddingLeft() - getPaddingRight(),
Alignment.ALIGN_NORMAL, lineSpacingMultiplier,
lineAdditionalVerticalPadding, false /* includepad */);
}
@Override
public void setEllipsize(TruncateAt where) {
// Ellipsize settings are not respected
}
}
Ответ 8
Для тех, кто интересуется, здесь С# Xamarin.Android порт Micah прекрасное решение:
public delegate void EllipsizeEvent(bool ellipsized);
public class EllipsizingTextView : TextView
{
private const string Ellipsis = "...";
public event EllipsizeEvent EllipsizeStateChanged;
private bool isEllipsized;
private bool isStale;
private bool programmaticChange;
private string fullText;
private int maxLines = -1;
private float lineSpacingMultiplier = 1.0f;
private float lineAdditionalVerticalPadding;
public EllipsizingTextView(Context context) : base(context)
{
}
public EllipsizingTextView(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public EllipsizingTextView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
public EllipsizingTextView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public bool IsEllipsized
{
get { return isEllipsized; }
}
public override void SetMaxLines(int maxLines) {
base.SetMaxLines(maxLines);
this.maxLines = maxLines;
isStale = true;
}
public int GetMaxLines()
{
return maxLines;
}
public override void SetLineSpacing(float add, float mult)
{
lineAdditionalVerticalPadding = add;
lineSpacingMultiplier = mult;
base.SetLineSpacing(add, mult);
}
protected override void OnTextChanged(ICharSequence text, int start, int before, int after)
{
base.OnTextChanged(text, start, before, after);
if (!programmaticChange)
{
fullText = text.ToString();
isStale = true;
}
}
protected override void OnDraw(Canvas canvas)
{
if (isStale)
{
base.Ellipsize = null;
ResetText();
}
base.OnDraw(canvas);
}
private void ResetText()
{
int maxLines = GetMaxLines();
string workingText = fullText;
bool ellipsized = false;
if (maxLines != -1)
{
Layout layout = CreateWorkingLayout(workingText);
if (layout.LineCount > maxLines)
{
workingText = fullText.Substring(0, layout.GetLineEnd(maxLines - 1)).Trim();
while (CreateWorkingLayout(workingText + Ellipsis).LineCount > maxLines)
{
int lastSpace = workingText.LastIndexOf(' ');
if (lastSpace == -1)
{
break;
}
workingText = workingText.Substring(0, lastSpace);
}
workingText = workingText + Ellipsis;
ellipsized = true;
}
}
if (workingText != Text)
{
programmaticChange = true;
try
{
Text = workingText;
}
finally
{
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized)
{
isEllipsized = ellipsized;
if (EllipsizeStateChanged != null)
EllipsizeStateChanged(ellipsized);
}
}
private Layout CreateWorkingLayout(string workingText)
{
return new StaticLayout(workingText, Paint, Width - PaddingLeft - PaddingRight, Layout.Alignment.AlignNormal, lineSpacingMultiplier, lineAdditionalVerticalPadding, false);
}
public override TextUtils.TruncateAt Ellipsize
{
get
{
return base.Ellipsize;
}
set
{
}
}
}
Ответ 9
Нет ничего, что можно было бы сделать из коробки. Но вы могли бы посмотреть на это
http://code.google.com/p/android-textview-multiline-ellipse/
и адаптируйте его к вашим потребностям.
Ответ 10
Код работал очень хорошо! Вы можете перегрузить метод onSizeChanged, если не только текст должен быть изменен.
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
isStale = true;
super.onSizeChanged(w, h, oldw, oldh);
}
Ответ 11
Просто добавьте код в свою деятельность
Textview.setEllipsize(TextUtils.TruncateAt.END)
это добавит эллипсис в конец текстового поля
Ответ 12
Расширьте TextView и переопределите эти методы:
CharSequence origText = "";
int maxLines = 2;
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
origText = text;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
CharSequence text = origText;
onPreDraw();
while(getLineCount() > maxLines) {
text = text.subSequence(0, text.length()-1);
super.setText(text + "...");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
onPreDraw();
}
}
Ответ 13
Это мое решение. вы можете скачать демо на моем github. https://github.com/krossford/KrossLib/tree/master/android-project
Этот снимок экрана был демонстрацией maxLines = 4
, я думаю, что он работает хорошо.
package com.krosshuang.krosslib.lib.view;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.TextView;
import java.util.ArrayList;
/*
如何使用?
How to use it?
> 1.在xml或者java代码中常规使用
> 1.use it like other views on xml and java code.
> 2.[必须] setMaxLines 方法替代在xml中的 "android:maxLines" 属性
> 2.[must] call the setMaxLines method to instead of the xml property android:maxLines.
> 3.[可选] 注意调用 setMultilineEllipsizeMode() 方法,具体请查看注释
> 3.[option] you can invoke setMultilineEllipsizeMode method, but I have not implement it.
*/
/**
* android自己的TextView对多行ellipsize处理的不好
* Created by krosshuang on 2015/12/17.
*/
public class EllipsizeEndTextView extends TextView {
private static final String LOG_TAG = "EllipsizeTextView";
/** 每一行都有省略号 */
//TODO 该特性待完成
public static final int MODE_EACH_LINE = 1;
/** 最后一行才有省略号 */
public static final int MODE_LAST_LINE = 2;
private static final String ELLIPSIZE = "...";
private ArrayList<String> mTextLines = new ArrayList<String>();
private CharSequence mSrcText = null;
private int mMultilineEllipsizeMode = MODE_LAST_LINE;
private int mMaxLines = 1;
private boolean mNeedIgnoreTextChangeAndSelfInvoke = false;
public EllipsizeEndTextView(Context context) {
super(context);
}
public EllipsizeEndTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EllipsizeEndTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
if (!mNeedIgnoreTextChangeAndSelfInvoke) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
mSrcText = text;
}
}
@Override
public void setMaxLines(int maxlines) {
super.setMaxLines(maxlines);
mMaxLines = maxlines;
}
public int getSupportedMaxLines() {
return mMaxLines;
}
@Override
protected void onDraw(Canvas canvas) {
setVisibleText();
super.onDraw(canvas);
mNeedIgnoreTextChangeAndSelfInvoke = false;
}
private void setVisibleText() {
if (mSrcText == null) {
return;
}
//获得可使用的width get available width
final int aw = getWidth() - getPaddingLeft() - getPaddingRight();
String srcText = mSrcText.toString();
//将原始的字符串先按原始数据中存在的换行符弄成多行字符串
String[] lines = srcText.split("\n");
//Log.i(LOG_TAG, "原始数据有: " + lines.length + " 行 " + Arrays.toString(lines));
int maxLines = getSupportedMaxLines();
//将原始文本分成几行后加入list
mTextLines.clear();
for (int i = 0; i < lines.length; i++) {
mTextLines.add(lines[i]);
}
switch (mMultilineEllipsizeMode) {
case MODE_EACH_LINE:
break;
default:
case MODE_LAST_LINE:
//开始遍历
String eachLine = null;
for (int i = 0; i < mTextLines.size() && i < maxLines - 1; i++) {
eachLine = mTextLines.get(i);
if (getPaint().measureText(eachLine, 0, eachLine.length()) > aw) {
//当前行超过可用宽度
boolean isOut = true;
int end = eachLine.length() - 1;
while (isOut) {
if (getPaint().measureText(eachLine.substring(0, end), 0, end) > aw) {
end--;
} else {
isOut = false;
}
}
mTextLines.set(i, eachLine.substring(0, end)); //当前行设置为裁剪后的
mTextLines.add(i + 1, eachLine.substring(end, eachLine.length())); //将裁剪剩余的部分,加入下一行,刚好接下来发生的遍历就可以处理它,相当于一个递归
}
}
//遍历处理结束,所有的行都是在可用宽度以内的
break;
}
//根据 maxLines 和 结果的行数,决定最小需要多少行
int resultSize = Math.min(maxLines, mTextLines.size());
//对最后一行做处理
String lastLine = mTextLines.get(resultSize - 1);
//最后一行有两种情况需要加...
//1.最后一行数据本身很长,超过了可用宽度,那么裁剪后尾部加上...
//2.最后一行不是很长,并没有超过可用宽度,但是它底下还有行没有显示,因此加上...
if (getPaint().measureText(lastLine, 0, lastLine.length()) > aw || resultSize < mTextLines.size()) {
boolean isOut = true;
int end = lastLine.length();
while (isOut) {
if (getPaint().measureText(lastLine.substring(0, end) + ELLIPSIZE, 0, end + 3) > aw) {
end--;
} else {
isOut = false;
}
}
mTextLines.set(resultSize - 1, lastLine.substring(0, end) + ELLIPSIZE);
}
//开始构建结果
StringBuilder sb = new StringBuilder();
for (int i = 0; i < resultSize ; i++) {
sb.append(mTextLines.get(i));
if (i != resultSize - 1) {
sb.append('\n');
}
}
//构建完成,set
if (sb.toString().equals(getText())) {
return;
} else {
mNeedIgnoreTextChangeAndSelfInvoke = true;
setText(sb.toString());
}
}
/**
* 设置ellipsize mode,暂时不支持
* @deprecated
* */
public void setMultilineEllipsizeMode(int mode) {
mMultilineEllipsizeMode = mode;
}
}
Ответ 14
Чтобы добавить ...
в конец второй строки, сохраните 1 строку, если текст короткий:
android:maxLines="2"
android:ellipsize="end"
Ответ 15
У меня была та же проблема. Я исправил его, просто удалив андроид: ellipsize = "marquee"
Ответ 16
Этот обработал также мой html,
/*
* Copyright (C) 2013 Google Inc.
* Licensed to The Android Open Source Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mail.ui;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.Layout;
import android.text.Layout.Alignment;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;
/**
* A special MultiLine TextView that will apply ellipsize logic to only the last
* line of text, such that the last line may be shorter than any previous lines.
*/
public class EllipsizedMultilineTextView extends TextView {
public static final int ALL_AVAILABLE = -1;
private int mMaxLines;
public EllipsizedMultilineTextView(Context context) {
super(context);
}
public EllipsizedMultilineTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public EllipsizedMultilineTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private final void init(Context context, AttributeSet attrs) {
final TypedArray a = context.obtainStyledAttributes(attrs,
new int[] { android.R.attr.maxLines });
setMaxLines(a.getInt(0, 2));
}
@Override
public void setMaxLines(int maxlines) {
super.setMaxLines(maxlines);
mMaxLines = maxlines;
}
/**
* Ellipsize just the last line of text in this view and set the text to the
* new ellipsized value.
* @param text Text to set and ellipsize
* @param avail available width in pixels for the last line
* @param paint Paint that has the proper properties set to measure the text
* for this view
* @return the {@link CharSequence} that was set on the {@link TextView}
*/
public CharSequence setText(final CharSequence text, int avail) {
if (text == null || text.length() == 0) {
return text;
}
setEllipsize(null);
setText(text);
if (avail == ALL_AVAILABLE) {
return text;
}
Layout layout = getLayout();
if (layout == null) {
final int w = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
layout = new StaticLayout(text, 0, text.length(), getPaint(), w, Alignment.ALIGN_NORMAL,
1.0f, 0f, false);
}
// find the last line of text and chop it according to available space
final int lastLineStart = layout.getLineStart(mMaxLines - 1);
final CharSequence remainder = TextUtils.ellipsize(text.subSequence(lastLineStart,
text.length()), getPaint(), avail, TextUtils.TruncateAt.END);
// assemble just the text portion, without spans
final SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(text.toString(), 0, lastLineStart);
if (!TextUtils.isEmpty(remainder)) {
builder.append(remainder.toString());
}
// Now copy the original spans into the assembled string, modified for any ellipsizing.
//
// Merely assembling the Spanned pieces together would result in duplicate CharacterStyle
// spans in the assembled version if a CharacterStyle spanned across the lastLineStart
// offset.
if (text instanceof Spanned) {
final Spanned s = (Spanned) text;
final Object[] spans = s.getSpans(0, s.length(), Object.class);
final int destLen = builder.length();
for (int i = 0; i < spans.length; i++) {
final Object span = spans[i];
final int start = s.getSpanStart(span);
final int end = s.getSpanEnd(span);
final int flags = s.getSpanFlags(span);
if (start <= destLen) {
builder.setSpan(span, start, Math.min(end, destLen), flags);
}
}
}
setText(builder);
return builder;
}
}
Оригнальный источник ССЫЛКА
Ответ 17
Попробуйте реализовать этот способ
android:maxLines="1"
android:ellipsize="end"
android:scrollHorizontally="true"
scrollHorizontally является ключом к ...
Ответ 18
Верхний ответ здесь от Михея Хейнлайн отлично работает, но еще лучше - это библиотека, которая была построена на ней пользователем aleb, когда он опубликовал в комментариях под ответом Micahs:
Я создал библиотеку Android с этим компонентом и изменил ее способный отображать как можно больше строк текста и эллипсизировать последние один; см. github.com/triposo/barone
Есть еще несколько функций, если вам нужен только TextView, это здесь.
Возможно, это поможет другим найти его быстрее, чем я: -)
Ответ 19
Поздно, но я нашел лицензионный класс Apache от Android, который использовался в приложении для электронной почты: https://android.googlesource.com/platform/packages/apps/UnifiedEmail/+/184ec73/src/com/android/mail/ui/EllipsizedMultilineTextView.java p >
/*
* Copyright (C) 2013 Google Inc.
* Licensed to The Android Open Source Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mail.ui;
import android.content.Context;
import android.text.Layout;
import android.text.Layout.Alignment;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;
/**
* A special MultiLine TextView that will apply ellipsize logic to only the last
* line of text, such that the last line may be shorter than any previous lines.
*/
public class EllipsizedMultilineTextView extends TextView {
public static final int ALL_AVAILABLE = -1;
private int mMaxLines;
public EllipsizedMultilineTextView(Context context) {
this(context, null);
}
public EllipsizedMultilineTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setMaxLines(int maxlines) {
super.setMaxLines(maxlines);
mMaxLines = maxlines;
}
/**
* Ellipsize just the last line of text in this view and set the text to the
* new ellipsized value.
* @param text Text to set and ellipsize
* @param avail available width in pixels for the last line
* @param paint Paint that has the proper properties set to measure the text
* for this view
* @return the {@link CharSequence} that was set on the {@link TextView}
*/
public CharSequence setText(final CharSequence text, int avail) {
if (text == null || text.length() == 0) {
return text;
}
setEllipsize(null);
setText(text);
if (avail == ALL_AVAILABLE) {
return text;
}
Layout layout = getLayout();
if (layout == null) {
final int w = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
layout = new StaticLayout(text, 0, text.length(), getPaint(), w, Alignment.ALIGN_NORMAL,
1.0f, 0f, false);
}
// find the last line of text and chop it according to available space
final int lastLineStart = layout.getLineStart(mMaxLines - 1);
final CharSequence remainder = TextUtils.ellipsize(text.subSequence(lastLineStart,
text.length()), getPaint(), avail, TextUtils.TruncateAt.END);
// assemble just the text portion, without spans
final SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(text.toString(), 0, lastLineStart);
if (!TextUtils.isEmpty(remainder)) {
builder.append(remainder.toString());
}
// Now copy the original spans into the assembled string, modified for any ellipsizing.
//
// Merely assembling the Spanned pieces together would result in duplicate CharacterStyle
// spans in the assembled version if a CharacterStyle spanned across the lastLineStart
// offset.
if (text instanceof Spanned) {
final Spanned s = (Spanned) text;
final Object[] spans = s.getSpans(0, s.length(), Object.class);
final int destLen = builder.length();
for (int i = 0; i < spans.length; i++) {
final Object span = spans[i];
final int start = s.getSpanStart(span);
final int end = s.getSpanEnd(span);
final int flags = s.getSpanFlags(span);
if (start <= destLen) {
builder.setSpan(span, start, Math.min(end, destLen), flags);
}
}
}
setText(builder);
return builder;
}
}
Ответ 20
В моем случае нет необходимости кодировать это на Java. Все работает так, как ожидалось. Нет необходимости в чем-то вроде android:singleLine="false"
.
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="4"
android:text="@string/very_long_text" />
Но, похоже, ошибка в предпросмотре макета Android Studio (v3.0):
Ответ 21
Есть несколько атрибутов, которые вы должны проверить: android: lines, android: minLines, android: maxLines. Чтобы отобразить максимум 4 строки и эллипсировать его, вам просто нужен андроид: maxLines и android: эллипсис:
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLines="4"
android:ellipsize="marquee"
android:text="Hai!"
/>
Ответ 22
Вам нужно включить это в свой текст:
android:singleLine="false"
по умолчанию - true. Вы должны явно установить его false.