How do I tell if my textview has been ellipsized?
Asked Answered
P

19

97

I have a multi-line TextView that has android:ellipsize="end" set. I would like to know, however, if the string I place in there is actually too long (so that I may make sure the full string is shown elsewhere on the page).

I could use TextView.length() and find about what the approximate length of string will fit, but since it's multiple lines, the TextView handles when to wrap, so this won't always work.

Any ideas?

Parotic answered 23/10, 2010 at 20:31 Comment(0)
M
140

You can get the layout of the TextView and check the ellipsis count per line. For an end ellipsis, it is sufficient to check the last line, like this:

Layout l = textview.getLayout();
if (l != null) {
    int lines = l.getLineCount();
    if (lines > 0)
        if (l.getEllipsisCount(lines-1) > 0)
            Log.d(TAG, "Text is ellipsized");
}

This only works after the layout phase, otherwise the returned layout will be null, so call this at an appropriate place in your code.

Mckinleymckinney answered 24/10, 2010 at 14:54 Comment(7)
This fails if you enter a long text with no spaces on the last line of your input, something like "jashdkjashdkjashdkasjhdashdkasjdhkajsdhkasjdhakjshdksjahdaksjhdajkshdkajshdkjashdkjashdja". getEllipsisCount() will return 0 in that case. To fix it, instead of only checking the last line, you must loop through all the lines and check if ( l.getEllipsisCount(n) > 0 ) return true; for all 0 <= n < lines.Tertiary
Because of bug in getEllipsisCount function in API <= 14, it will fail and return 0 if the text contains special chars like '\n'. So you can workaround it by using the support libraries and replacing if (l.getEllipsisCount(lines-1) > 0) with if (l.getEllipsisCount(lines-1) > 0 || TextViewCompat.getMaxLines(this)).Forelli
Ugh, use brackets even for single line statement ifs pleaseEvannia
@r3flssExlUtr, and that is why exactly?Swanson
Could you please elaborate what what you mean by the layout phase. Do you mean during when the layout is inflated and why would the become null later?Basso
@NocTurn The TextView has to have performed its internal text layout already. This usually happens during layout. A safe bet is to wset a listener for onGlobalLayout(see Himanshu Virmani's answer) or onPreDraw. The layout could become null later when there are changes made to the TextView such as a new text being set, which will require a new internal text layout to be generated. Note that this has nothing to do with layout inflation (android.text.Layout vs. the concept of "Layout" as a view hierarchy)Mckinleymckinney
1. given error classifier layout does not have a companion object when using Layout type In Kotlin. 2. getEllipsisCount function returned 0. please help meFunnel
B
39

textView.getLayout is the way to go but the problem with that is that it returns null if layout is not prepared. Use the below solution.

 ViewTreeObserver vto = textview.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
           Layout l = textview.getLayout();
           if ( l != null){
              int lines = l.getLineCount();
              if ( lines > 0)
                  if ( l.getEllipsisCount(lines-1) > 0)
                    Log.d(TAG, "Text is ellipsized");
           }  
        }
    });

Code snippet for removing the listener (source):

mLayout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    public void onGlobalLayout() {
        scrollToGridPos(getCenterPoint(), false);
        mLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);        
    }
});
Bel answered 8/10, 2013 at 7:20 Comment(5)
Don't forget to removeOnGlobalLayoutListener() when you don't need it anymore.Jem
Add this when the ellipsize is found: if (Build.VERSION.SDK_INT < 16) { textview.getViewTreeObserver().removeGlobalOnLayoutListener(this); } else{ textview.getViewTreeObserver().removeOnGlobalLayoutListener(this); }Indecorous
Use view.post() rather than VTO! It's much more simple.Morello
Even more ugh, use brackets even for single line statement ifsEvannia
1. given error classifier layout does not have a companion object when using Layout type In Kotlin. 2. getEllipsisCount function returned 0. please help meFunnel
S
38

I think the easiest solution to this question is the following code:

String text = "some looooong text";
textView.setText(text);
boolean isEllipsize = !((textView.getLayout().getText().toString()).equalsIgnoreCase(text));

This code assumes that in your XML the TextView set a maxLineCount :)

Stopper answered 6/11, 2015 at 11:47 Comment(7)
lazy but effective :-DBurbank
Your test will fails if any sort capitalization is set. Add toLowerCase() calls to both strings.Essonite
If your textView.getLayout() returns null then you should use: final ViewTreeObserver vto = tv.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { tv.getViewTreeObserver().removeOnGlobalLayoutListener(this); Layout layout = tv.getLayout(); if(!((layout.getText().toString()).equalsIgnoreCase(text))){ // ellipsized }else{ // NOT ellipsized }}});Woolard
Need to do it after textView have render the text. textView.post(() -> {//do it here})Vulgarian
Strange that the length of both objects is the same yet they are different to what is visible.Handcar
@SarithNOB 's suggestion made this solution work for me.Groundling
I've found this is a good solution if you have singleLine="true" in your xmlTearjerker
A
9

This worked to me:

textView.post(new Runnable() {
                        @Override
                        public void run() {
                            if (textView.getLineCount() > 1) {
                                //do something
                            }
                        }
                    });
Aftonag answered 27/7, 2018 at 17:5 Comment(1)
saved my day, because ViewTreeObserver doesn't work well within RecyclerViewDickey
F
9

The most eloquent solution I have found (in Kotlin) is to create an extension function on TextView

fun TextView.isEllipsized() = layout.text.toString() != text.toString()

This is great because it doesn't require knowing what the full string is or worrying about how many lines the TextView is using.

TextView.text is the full text that it's trying to show, whereas TextView.layout.text is what's actually shown on the screen so if they are different it must be getting ellipsized

To use it:

if (my_text_view.isEllipsized()) {
    ...
}
Forespent answered 7/1, 2021 at 22:14 Comment(5)
Get NPE on layout. Can you please tell me a solution?Alinaaline
@NimishaV I would guess your calling isEllipsized before the layout is complete or after the textview is removed. See this question: #16559448 I suggest opening a new question with the code that reproduces the error if you want help with it.Forespent
I got a solution for this error.Alinaaline
Don't, like I did, try to be smart and use text.length in this case, because it does not work. Numbers are always the same.Ilk
this solution worked for me, in my opinion this solution is goodMulry
P
6

public int getEllipsisCount (int line):

Returns the number of characters to be ellipsized away, or 0 if no ellipsis is to take place.

So, simply call :

int lineCount = textview1.getLineCount();

if(textview1.getLayout().getEllipsisCount(lineCount) > 0) {
   // Do anything here..
}

Since the getLayout() cant be called before the layout is set, use this:

ViewTreeObserver vto = textview.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
       Layout l = textview.getLayout();
       if ( l != null){
          int lines = l.getLineCount();
          if ( lines > 0)
              if ( l.getEllipsisCount(lines-1) > 0)
                Log.d(TAG, "Text is ellipsized");
       }  
    }
});

And finally do not forget to remove removeOnGlobalLayoutListener when you need it nomore.

Playa answered 21/2, 2014 at 7:32 Comment(3)
You are free to edit the answer with the correct one. @FrancescoDonzelloPlaya
Might the code not being superb but the approach is it for sure. This solved my problem on determining if the text has been elippsized. This approach is very useful when you do add views to the window through the window manager.Windhoek
Sadly, it seems like if the ellipsis is occurring right in an empty line ("\n") it is returning 0 too, which is correct since it is happening in the 0 index character. So in that case it doesn't seem to be a way to tell...Shutin
K
3
lateinit var toggleMoreButton: Runnable
toggleMoreButton = Runnable {
  if(reviewTextView.layout == null) { // wait while layout become available
       reviewTextView.post(toggleMoreButton) 
       return@Runnable
  }
  readMoreButton.visibility = if(reviewTextView.layout.text.toString() != comment) View.VISIBLE else View.GONE
}
reviewTextView.post(toggleMoreButton)

It is some typical case:

  1. comment in 'reviewTextView'
  2. comment can collapsed by some criteria
  3. if comment collapsed you show button 'readMoreButton'
Knownothing answered 22/12, 2018 at 5:32 Comment(2)
This answer may be made more useful with a brief text explanation of the codeSatan
I must say it was short precise and it was written in kotlin.Animism
T
2

The Kotlin way:

textView.post {
   if (textView.lineCount > MAX_LINES_COLLAPSED) {
   // text is not fully displayed
   }
}

Actually View.post() is executed after the view has been rendered and will run the function provided

Thebaid answered 24/9, 2019 at 11:36 Comment(0)
C
2

Simple Kotlin method. Allows android:ellipsize and android:maxLines to be used

fun isEllipsized(textView: TextView, text: String?) = textView.layout.text.toString() != text
Cermet answered 1/9, 2020 at 18:2 Comment(1)
Hello, I understand you have converted it to kotlin, but there seems to already be an answer doing exactly the same thing you did in java. Next time, maybe you can edit the existing answer or maybe suggest an edit in the comment to the answer instead of adding a entirely new answer. Happy coding !!Giantess
M
2

Solution with kotlin extensions:

infoText.afterLayoutConfiguration {
    val hasEllipsize = infoText.hasEllipsize()
    ...
}

Extensions:

/**
 * Function for detect when layout completely configure.
 */
fun View.afterLayoutConfiguration(func: () -> Unit) {
    viewTreeObserver?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            viewTreeObserver?.removeOnGlobalLayoutListener(this)
            func()
        }
    })
}

fun TextView.hasEllipsize(): Boolean = layout.getEllipsisCount(lineCount - 1) > 0
Maliamalice answered 20/5, 2021 at 10:26 Comment(0)
V
1

it is working for me

if (l != null) {
    int lines = l.getLineCount();
     if (lines > 0) {
     for (int i = 0; i < lines; i++) {
     if (l.getEllipsisCount(i) > 0) {
      ellipsize = true;
      break;
     }
    }
   }
  }
Vermin answered 29/8, 2016 at 3:58 Comment(0)
A
1

If your textview contains multiple paragraphs, using getEllipsisCount will not work for empty lines within it. getEllipsisCount for the last line of any paragraph will return 0.

Artemus answered 19/4, 2021 at 20:41 Comment(0)
C
1

After researching I found the best way for me in Kotlin

To get the ellipsize status the textView must be rendered first, so we have to set the text first, then check the ellipsize logic inside textView.post scope

textView.text = "your text"
textView.post {
    var ellipsized: Boolean = textView.layout.text.toString()).equalsIgnoreCase("your text"))

    if(ellipsized){
        //your logic goes below
      
    }
}
Claresta answered 30/1, 2023 at 6:45 Comment(0)
C
0

Really work so, for example, to pass full data to dialog from item of RecyclerView:

holder.subInfo.post(new Runnable() {
                @Override
                public void run() {
                    Layout l = holder.subInfo.getLayout();
                    if (l != null) {
                        final int count = l.getLineCount();
                        if (count >= 3) {
                            holder.subInfo.setOnClickListener(new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {
                                    final int c = holder.subInfo.getLineCount();
                                    if (c >= 3) {
                                        onClickToShowInfoDialog.showDialog(holder.title.getText().toString(), holder.subInfo.getText().toString());
                                    }
                                }
                            });
                        }
                    }
                }
            });
Coralyn answered 13/5, 2018 at 16:38 Comment(0)
F
0

Combining @Thorstenvv awnser with @Tiano fix, here is the Kotlin version :

val layout = textView.layout ?: return@doOnLayout
val lines = layout.lineCount
val hasLine = lines > 0
val hasEllipsis = ((lines - 1) downTo 0).any { layout.getEllipsisCount(it) > 0 }
if (hasLine && hasEllipsis) {
    // Text is ellipsized
}
Faenza answered 29/10, 2019 at 14:50 Comment(0)
S
0

In Kotlin, you can use the below code.

var str= "Kotlin is one of the best languages."
textView.text=str
textView.post {
val isEllipsize: Boolean = !textView.layout.text.toString().equals(str)

if (isEllipsize) {
     holder.itemView.tv_viewMore.visibility = View.VISIBLE
} else {
     holder.itemView.tv_viewMore.visibility = View.GONE
}    
}
Siderostat answered 20/6, 2020 at 12:44 Comment(0)
A
0

This is simple library for creating textview expandable. Like Continue or Less. This library extended version TextView. Easy to use.

implementation 'com.github.mahimrocky:ShowMoreText:1.0.2'

Like this, 1 https://raw.githubusercontent.com/mahimrocky/ShowMoreText/master/screenshot1.png

2 https://raw.githubusercontent.com/mahimrocky/ShowMoreText/master/screenshot2.png

 <com.skyhope.showmoretextview.ShowMoreTextView
    android:id="@+id/text_view_show_more"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/text"
    />

In Activity you can use like:

ShowMoreTextView textView = findViewById(R.id.text_view_show_more);

//You have to use following one of method    

// For using character length
textView.setShowingChar(numberOfCharacter);
//number of line you want to short
textView.setShowingLine(numberOfLine);
Auspex answered 4/2, 2021 at 1:31 Comment(0)
M
-1

Using getEllipsisCount won't work with text that has empty lines within it. I used the following code to make it work :

message.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {

            if(m.isEllipsized == -1) {
                Layout l = message.getLayout();
                if (message.getLineCount() > 5) {
                    m.isEllipsized = 1;
                    message.setMaxLines(5);
                    return false;
                } else {
                    m.isEllipsized = 0;
                }
            }
            return true;
        }
    });

Make sure not to set a maxLineCount in your XML. Then you can check for the lineCount in your code and if it is greater than a certain number, you can return false to cancel the drawing of the TextView and set the line count as well as a flag to save whether the text view is too long or not. The text view will draw again with the correct line count and you will know whether its ellipsized or not with the flag.

You can then use the isEllipsized flag to do whatever you require.

Michail answered 14/2, 2015 at 17:18 Comment(0)
M
-1

create a method inside your TextViewUtils class

public static boolean isEllipsized(String newValue, String oldValue) {
    return !((newValue).equals(oldValue));
}

call this method when it's required eg:

        if (TextViewUtils.isEllipsized(textviewDescription.getLayout().getText().toString(), yourModelObject.getDescription()))
            holder.viewMore.setVisibility(View.VISIBLE);//show view more option
        else
            holder.viewMore.setVisibility(View.GONE);//hide 

but textView.getLayout() can't call before the view(layout) set.

Mull answered 28/3, 2017 at 9:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.