android elipsize visualização de texto multilinha


163

Preciso elipsizar uma visualização de texto com várias linhas. Meu componente é grande o suficiente para exibir pelo menos 4 linhas com a elipse, mas apenas 2 linhas são exibidas. Tentei alterar o número mínimo e máximo de linhas do componente, mas isso não muda nada.


Você tem um estilo ou tema aplicado ao seu TextView que especifique um tamanho máximo?
Cheryl Simon

você encontrou solução para este problema ou não?
UMAR-MOBITSOLUTIONS

Oi, depois de lutar com o problema de ter as 2 linhas de texto (maxLines = 2) e três pontos no final do texto (elipsize = end), descobri que ele funciona em alguns dispositivos e em outros não (eu tenho ~ 15 dispositivos para testar). Ele funciona normalmente em dispositivos com a resolução mais alta, em seguida, HVGA (320x480px), mas também em alguma HTC com 240x320px ... A única solução é ter um TextView personalizado como mostrado abaixo ...
Sten

Mina funcionou bem depois de retirar "android: textIsSelectable = true"
HannahMitt

Como Robert Nekic disse, houve um bug no Android que foi corrigido: code.google.com/p/android/issues/detail?id=2254 Seria bom saber qual é a primeira versão do Android em que isso foi corrigido.
aleb

Respostas:


140

Aqui está uma solução para o problema. É uma subclasse do TextView que realmente funciona para elipsizar. O código android-textview-multiline-ellipse listado em uma resposta anterior que eu achei buggy em certas circunstâncias, além de estar sob a GPL, que realmente não funciona para a maioria de nós. Sinta-se livre para usar esse código livremente e sem atribuição, ou sob a licença Apache, se você preferir. Observe que há um ouvinte para notificá-lo quando o texto se torna reticulado, o que eu achei bastante útil.

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
    }
}

4
Obrigado por este código, funcionou muito bem. Uma desvantagem para outros usuários: você precisará chamar explicitamente setMaxLines (int) em vez de apenas definir a propriedade em XML.
usar o seguinte código

2
Na verdade, deve ser muito trivial mapear essas variáveis ​​para atributos XML via attrs.xml.
Diego Tori

9
Encontrei um problema se o workingText for chinês. porque o chinês não possui ESPAÇO, então esse código não funciona perfeitamente; modifiquei alguns códigos abaixo, espero que ajude. while (createWorkingLayout (workingText + ELLIPSIS) .getLineCount ()> maxLines) {// int lastSpace = workingText.lastIndexOf (''); // if (lastSpace == -1) {// break; //} // workingText = workingText.substring (0, lastSpace); // // workingText 下 中文 , 的 的 的 逻辑 找 空格 是 的 这里 // 这里 改成 直接 替换 的 的 字符 workingText = workingText.substring (0, workingText.length () - 1 - 1 ); }
Chengbo

21
Adicionar o seguinte ao construtor permitirá definir as linhas máximas via XML: TypedArray a = context.obtainStyledAttributes (attrs, new int [] {android.R.attr.maxLines}); setMaxLines (a.getInt (0, 2));
stealthcopter

29
Criei uma biblioteca Android com esse componente e a alterei para poder mostrar o maior número possível de linhas de texto e elipsizar a última; consulte github.com/triposo/barone Você pode vê-lo em ação em qualquer um dos nossos guias de viagem, ao exibir Sugestões: play.google.com/store/apps/…
aleb 13/04/12

55

No meu aplicativo, tive um problema semelhante: duas linhas de string e, eventualmente, adicione "..." se a string fosse muito longa. Eu usei esse código no arquivo xml na tag textview:

android:maxLines="2"
android:ellipsize="end"
android:singleLine="false"

4
E se ele não funciona com este código (em algumas versões), adicione o "molho especial" Android: scrollHorizontally = "true"
Bart Burg

7
Não funciona se o texto estiver definido com setText(..., TextView.BufferType.SPANNABLE);.
Simas

1
alguma notícia sobre spannable? @Simas. Corri já para o mesmo problema
Alexey Strakh

17

Também encontrei esse problema. Existe um bug antigo que permanece sem resposta: Bug 2254


Eeeks. Você está certo. Eu tentei isso e não consegui fazer reticências de 4 linhas. Quebra sempre na segunda linha.
moraes

3
O bug parece ter sido corrigido no 4.0.4. É resolvido no Jelly Bean, pelo menos.
greg7gkb

1
Você não deve responder a uma pergunta com uma Confirmação
Bart Burg

17

Tente isso, funciona para mim, tenho 4 linhas e adiciona o "..." ao final da última / quarta linha. É o mesmo que a resposta da moral, mas eu tenho singeLine = "false" lá.

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

16
Lysogen, esta solução não funciona realmente, pelo menos não para outras pessoas aqui. Talvez você esteja usando um dispositivo muito diferente de outras pessoas, mas como solução geral, isso não é eficaz.
Micah Hainline

3
sim, não funciona. ainda truncado em duas linhas. isso é frustrante!
Matt K

2
Copio esses códigos em um projeto de demonstração e o tesxtview mostra apenas 2 linhas.
Nguyen Minh Binh

1
Funciona para mim no tablet Ice Cream Sandwich!
Cel

1
Funciona para mim no pão de mel SII. Seria interessante saber mais sobre os dispositivos nos quais isso não funciona.
PCD30 de

14

Consegui esse problema e, finalmente, desenvolvi uma solução curta. Você só precisa elipsize manualmente a linha desejada, seu atributo maxLine cortará seu texto.

Este exemplo recorta seu texto por no máximo 3 linhas

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

            }
        });

12

Combinei as soluções de Micah Hainline, Alex Băluț e Paul Imhoff para criar uma multilinha elipsizante TextViewque também suporta Spannedtexto.

Você só precisa definir android:ellipsizee 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);
        }
    }
}

Fonte completa: EllipsizingTextView.java


2
Obrigado, a única solução de trabalho (testou vários). Gostaria de saber que sua classificação foi 1.
CoolMind 02/12/2015

1
Posso confirmar que é a única solução limpa e funcional, basta usar a classe EllipsizingTextView e esquecer os hacks.
javaxian

7

No meu caso, não há necessidade de codificar isso em Java. Tudo funciona como esperado. Não há necessidade de algo parecido 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" />

Mas parece haver um bug na visualização de layout do Android Studio (v3.0): visualização de layout

Dado o Android 7.1.1 no meu dispositivo, isso está funcionando: captura de tela do dispositivo


Eu também tive esse problema. A visualização do Android estava errada. No dispositivo está tudo bem
AndEngine

4

Com base nas soluções de Micah Hainline e dos comentários de alebs, vim com a seguinte abordagem que trabalha com textos em escala, de modo que, por exemplo, myTextView.setText(Html.fromHtml("<b>Testheader</b> - Testcontent"));funciona! Observe que isso funciona apenas Spannedno momento. Talvez pudesse ser modificado para trabalhar com Stringe de Spannedqualquer maneira.

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
      }
}

4

Para aqueles que estão interessados, aqui está uma porta C # Xamarin.Android da adorável solução de 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 
        { 
        }
    }
}

Incrível, eu estava prestes a encontrar a biblioteca original e a traduzi-la, obrigado!
precisa saber é o seguinte

2

Para adicionar ...no final da segunda linha, salvando 1 linha se o texto for curto:

android:maxLines="2"
android:ellipsize="end"

1

Basta adicionar código à sua atividade

Textview.setEllipsize(TextUtils.TruncateAt.END)

isso adicionará reticências no final da visualização de texto


parece muito mais simples do que estender a classe TextView
Malachiasz

Isso funciona para mim e é a resposta mais simples de todas.
Ricardo

isso é o mesmo que definir ellipsize attr em xml.
Shubham Naik

1

Estenda o TextView e substitua estes métodos:

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();
    }

}

1

Esta é a minha solução. você pode baixar a demo no meu github. https://github.com/krossford/KrossLib/tree/master/android-project

Esta captura de tela foi uma demonstração que maxLines = 4, acho que funciona bem.

insira a descrição da imagem aqui

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;
    }
}

0

Eu tive o mesmo problema. Corrigi-o apenas excluindo android: ellipsize = "marquee"


Isso funciona, embora deva ser mencionado que isso não adicionará mais as reticências ao final da última linha, mas, na minha opinião, essa ainda é uma boa solução para o problema. Além disso, você precisará usar o android: maxLines = "4" para limitar a 4 linhas.
Pzanno 25/05

A pergunta do OP era especificamente para reticências no final, portanto não funcionaria.
Matt K

0

Código funcionou muito bem! Você pode sobrecarregar o método onSizeChanged, caso não seja necessário alterar apenas o texto.

@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
    isStale = true;
    super.onSizeChanged(w, h, oldw, oldh);
}

0

Este também lidou com meu 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;
    }
}

Origem original LINK


0

A resposta principal aqui do Micah Hainline funciona muito bem, mas ainda melhor é a biblioteca que foi criada a partir dele pelo usuário aleb, conforme ele postou nos comentários em Micahs answer:

Eu criei uma biblioteca Android com esse componente e mudei para poder mostrar o maior número possível de linhas de texto e colocar em elipse a última; consulte github.com/triposo/barone

Existem mais alguns recursos, se você precisar apenas do TextView, ele está aqui .

Talvez isso ajude outras pessoas a encontrá-lo mais rápido do que eu :-)


0

É tarde, mas eu encontrei uma classe licenciada Apache do Android, usada no aplicativo de email padrão: https://android.googlesource.com/platform/packages/apps/UnifiedEmail/+/184ec73/src/com/android/ mail / ui / EllipsizedMultilineTextView.java

/*
 * 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;
    }
}

-2

Existem alguns atributos que você deve verificar: android: lines, android: minLines, android: maxLines. Para exibir no máximo 4 linhas e reticê-lo, você só precisa de android: maxLines e android: ellipsize:

<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:maxLines="4"
    android:ellipsize="marquee"
    android:text="Hai!"
    />

Como usar estes? O texto ainda será truncado para duas linhas seja qual for o valor que eu dou-lhes
Arutha

Como você está definindo isso? Acho que está faltando alguma coisa, mas é difícil adivinhar.
Moraes

1
Usei as mesmas configurações, mas o meu texto é sempre Trunked a duas linhas
Arutha

Essa resposta nem funciona ... Você deve tentar suas respostas antes de publicá-las.
Kevin Parker

4
@ Kevin: Esta resposta é muito antiga. Você deve experimentá-lo com uma versão do Android a partir de 29 de janeiro de 2010 antes de dizer que não funciona e fazer o voto negativo.
Moraes

-14

Você precisa incluir isso em sua visualização de texto:

android:singleLine="false"

por padrão, é verdadeiro. Você precisa defini-lo explicitamente.


Sério, Tente este se você não acho que funciona faz para mim:code <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:maxLines="4" android:ellipsize="marquee" android:singleLine="false" android:text="Hai make this a very long string that wraps at least 4 lines!" />
lysogen

3
android: singleLine é definitivamente FALSO por padrão
Jason Robinson
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.