32

I am trying to set ellipsize of text view. using the following code. I want to add "view more" at the end of truncated string after 3 dots. If this would be possible with same text view that would be great, or "view more" in seperate text view will also work. Max lines allow are 4. I tried to set width of first text view but it left the empty space at end of first 3 lines. Please see the image below.

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <TextView
        android:id="@+id/tvReviewDescription"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:maxLines="4"
        android:text="I tend to shy away from restaurant chains, but wherever I go, PF Chang&apos;s has solidly good food and, like Starbucks, they&apos;re reliable. We were staying in Boston for a week and after a long day and blah blah blah blah... "
        android:textColor="@color/black"
        android:textSize="13dp" 
        android:maxLength="280"
        android:ellipsize="end"/>

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/tvReviewDescription"
        android:layout_alignParentRight="true"
        android:text="@string/label_view_more"
        android:textColor="@color/yellow" />
</RelativeLayout>

enter image description here

Basit ZIa
  • 966
  • 1
  • 9
  • 26
  • will you manually give the content in this .. ? like: "I tend to shy away..." If yes then obviously you have to control your content being exceeded or you will give a listview.. ? – Neha Gupta Sep 30 '13 at 16:43
  • @Neha can you elaborate via code snippet? He wanted to show 4 lines and if it exceeds from 4 lines then truncate text and show 'View More' , give some suggestions to handle multiple screens. How to handle manually? – Nauman Zubair Sep 30 '13 at 17:14
  • What will happen if you click on View More ? does this text Expands or Go to a new Screen? – Jitender Dev Sep 30 '13 at 17:30
  • Did you found any answer @Basit ZIa – Noman Mar 18 '15 at 15:09

17 Answers17

33

Find my answer

   public static void makeTextViewResizable(final TextView tv, final int maxLine, final String expandText, final boolean viewMore) {

    if (tv.getTag() == null) {
        tv.setTag(tv.getText());
    }
    ViewTreeObserver vto = tv.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @SuppressWarnings("deprecation")
        @Override
        public void onGlobalLayout() {

            ViewTreeObserver obs = tv.getViewTreeObserver();
            obs.removeGlobalOnLayoutListener(this);
            if (maxLine == 0) {
                int lineEndIndex = tv.getLayout().getLineEnd(0);
                String text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + expandText;
                tv.setText(text);
                tv.setMovementMethod(LinkMovementMethod.getInstance());
                tv.setText(
                        addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, maxLine, expandText,
                                viewMore), TextView.BufferType.SPANNABLE);
            } else if (maxLine > 0 && tv.getLineCount() >= maxLine) {
                int lineEndIndex = tv.getLayout().getLineEnd(maxLine - 1);
                String text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + expandText;
                tv.setText(text);
                tv.setMovementMethod(LinkMovementMethod.getInstance());
                tv.setText(
                        addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, maxLine, expandText,
                                viewMore), TextView.BufferType.SPANNABLE);
            } else {
                int lineEndIndex = tv.getLayout().getLineEnd(tv.getLayout().getLineCount() - 1);
                String text = tv.getText().subSequence(0, lineEndIndex) + " " + expandText;
                tv.setText(text);
                tv.setMovementMethod(LinkMovementMethod.getInstance());
                tv.setText(
                        addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, lineEndIndex, expandText,
                                viewMore), TextView.BufferType.SPANNABLE);
            }
        }
    });

}

private static SpannableStringBuilder addClickablePartTextViewResizable(final Spanned strSpanned, final TextView tv,
                                                                        final int maxLine, final String spanableText, final boolean viewMore) {
    String str = strSpanned.toString();
    SpannableStringBuilder ssb = new SpannableStringBuilder(strSpanned);

    if (str.contains(spanableText)) {


        ssb.setSpan(new MySpannable(false){
            @Override
            public void onClick(View widget) {
                if (viewMore) {
                    tv.setLayoutParams(tv.getLayoutParams());
                    tv.setText(tv.getTag().toString(), TextView.BufferType.SPANNABLE);
                    tv.invalidate();
                    makeTextViewResizable(tv, -1, "See Less", false);
                } else {
                    tv.setLayoutParams(tv.getLayoutParams());
                    tv.setText(tv.getTag().toString(), TextView.BufferType.SPANNABLE);
                    tv.invalidate();
                    makeTextViewResizable(tv, 3, ".. See More", true);
                }
            }
        }, str.indexOf(spanableText), str.indexOf(spanableText) + spanableText.length(), 0);

    }
    return ssb;

}

Another class:-

import android.graphics.Color;
import android.text.TextPaint;
import android.text.style.ClickableSpan;
import android.view.View;

public class MySpannable extends ClickableSpan {

private boolean isUnderline = true;

/**
 * Constructor
 */
public MySpannable(boolean isUnderline) {
    this.isUnderline = isUnderline;
}

@Override
public void updateDrawState(TextPaint ds) {
    ds.setUnderlineText(isUnderline);
    ds.setColor(Color.parseColor("#1b76d3"));
}

@Override
public void onClick(View widget) {


 }
}

Last step to call it:

DetailTv.setText(discription);
makeTextViewResizable(DetailTv, 3, "See More", true);
Akanksha Rathore
  • 3,603
  • 3
  • 31
  • 47
  • 2
    Item of recycle view is refresh on scroll. which is causing problem – Tarun Sharma Apr 12 '18 at 11:42
  • 1
    have anyone used this in Recyclerview? – Dilip Jul 18 '18 at 12:38
  • @akansha-rathore your code works properly in api 24+ but for lower version is not working properly i followed all the steps – abhishek Sep 13 '19 at 11:18
  • I'm not sure but this answer doesn't handle links, Emojis, or hashtags. In my case, I can't cut in middle of URL or hashtag: ```www.google.de = www.go see more``` or ```#germany = #germ see more``` – A. Amini Jul 30 '20 at 13:01
27

Simpler than the accepted answer:

public static final int MAX_LINES = 3;
public static final String TWO_SPACES = " "; 

String myReallyLongText = "Bacon ipsum dolor amet porchetta venison ham fatback alcatra tri-tip, turducken strip steak sausage rump burgdoggen pork loin. Spare ribs filet mignon salami, strip steak ball tip shank frankfurter corned beef venison. Pig pork belly pork chop andouille. Porchetta pork belly ground round, filet mignon bresaola chuck swine shoulder leberkas jerky boudin. Landjaeger pork chop corned beef, tri-tip brisket rump pastrami flank."

textView.setText(myReallyLongText);
textView.post(new Runnable() {
                @Override
                public void run() {
                    // Past the maximum number of lines we want to display.
                    if (textView.getLineCount() > MAX_LINES) {
                        int lastCharShown = textView.getLayout().getLineVisibleEnd(MAX_LINES - 1);

                        textView.setMaxLines(MAX_LINES);

                        String moreString = context.getString(R.string.more);
                        String suffix = TWO_SPACES + moreString;
                        
                        // 3 is a "magic number" but it's just basically the length of the ellipsis we're going to insert
                        String actionDisplayText = myReallyLongText.substring(0, lastCharShown - suffix.length() - 3) + "..." + suffix;

                        SpannableString truncatedSpannableString = new SpannableString(actionDisplayText);
                        int startIndex = actionDisplayText.indexOf(moreString);
                        truncatedSpannableString.setSpan(new ForegroundColorSpan(context.getColor(android.R.color.blue)), startIndex, startIndex + moreString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                        textView.setText(truncatedSpannableString);
                    }
                }
            });

If you want the "View More" part of your Text to be clickable (but not the entire TextView), utilize ClickableSpan as outlined here in this StackOverflow for How to set the part of the text view is clickable. I would caution you to be aware of the UX implications of this, as normally you truncate your text because you have a lot of it and you don't have much space, so your font size is already probably small. Having a tiny target for users to click to navigate to the full text might not be the best or most accessible user experience, especially if your users are elderly or have mobility issues that make hitting a small part of the screen difficult. Generally I would suggest making your entire TextView clickable rather than a small portion of it for this reason.

As an alternative, you can do as I did and turn this into a custom view. Here's the class; you can modify as you desire using the ClickableSpan code, but since I have not compiled this project in a long, long time I don't wish to make changes that I then need to verify are safe to publish. I welcome an edit if someone wants to tackle that.

public class TruncatingTextView extends AppCompatTextView {
    private static final String TWO_SPACES = "  ";

    private int truncateAfter = Integer.MAX_VALUE;

    private String suffix;
    private final RelativeSizeSpan truncateTextSpan = new RelativeSizeSpan(0.75f);
    private ForegroundColorSpan viewMoreTextSpan;
    private final String moreString = getContext().getString(R.string.more);

    private final String ellipsis = getContext().getString(R.string.ellipsis);

    public TruncatingTextView(Context context) {
        super(context);
        init();
    }

    public TruncatingTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TruncatingTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        viewMoreTextSpan = new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.upsell_blue));
    }

    public void setText(CharSequence fullText, @Nullable CharSequence afterTruncation, int truncateAfterLineCount) {
        this.suffix = TWO_SPACES + moreString;

        if (!TextUtils.isEmpty(afterTruncation)) {
            suffix += TWO_SPACES + afterTruncation;
        }

        if (this.truncateAfter != truncateAfterLineCount) {
            this.truncateAfter = truncateAfterLineCount;
            setMaxLines(truncateAfter);
        }

        setText(fullText);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        if (getLayout() != null && getLayout().getLineCount() > truncateAfter) {
            int lastCharToShowOfFullTextAfterTruncation = getLayout().getLineVisibleEnd(truncateAfter - 1) - suffix.length() - ellipsis.length();

            int startIndexOfMoreString = lastCharToShowOfFullTextAfterTruncation + TWO_SPACES.length() + 1;

            SpannableString truncatedSpannableString = new SpannableString(getText().subSequence(0, lastCharToShowOfFullTextAfterTruncation) + ellipsis + suffix);
            truncatedSpannableString.setSpan(truncateTextSpan, startIndexOfMoreString, truncatedSpannableString.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
            truncatedSpannableString.setSpan(viewMoreTextSpan, startIndexOfMoreString, startIndexOfMoreString + moreString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            setText(truncatedSpannableString);
        }
    }
}
Chantell Osejo
  • 1,456
  • 15
  • 25
14

This can be achieved during Runtime , all you need to do is check the length of string and add Underlined View More at the end of string like this.

I have used length '20' as an example , you can change according to your requirement.

final TextView result = (TextView) findViewById(R.id.textview);

String text = "I tend to shy away from restaurant chains, but wherever I go, PF Chang&apos;s has solidly good food and, like Starbucks, they&apos;re reliable. We were staying in Boston for a week and after a long day and blah blah blah blah...";

if (text.length()>20) {
    text=text.substring(0,20)+"...";
    result.setText(Html.fromHtml(text+"<font color='red'> <u>View More</u></font>"));       

}
Gokhan Arik
  • 2,626
  • 2
  • 24
  • 50
Jitender Dev
  • 6,907
  • 2
  • 24
  • 35
  • 3
    if you chnage the device orientation it may be possible that the full text can be displayed, and therefore the "View more" is not neccesary anymore. I think your solution is neither "font size" nor screen resolution independent. – jmhostalet Aug 06 '14 at 20:50
  • Thank you for sharing code snippet. I need to know how to perform click event of View More text? – Sneha Patel Oct 04 '16 at 11:32
  • Although I think this isn't a good solution, because it is dependent on a specific screen size etc, setting the event listens is easy, since this is aa TextView, jus let the users big fingers click anywhere on the TextView. – Brill Pappin Jun 21 '17 at 22:08
  • Thank you for your answer.Improving on it I have posted answer with click event listeners on View More and Show Less text – Android Developer Oct 14 '17 at 05:53
6

This will have ellipsize effect.

set Boolean isCheck= true;

put this in the xml:

<TextView
        android:id="@+id/txt_id"
        android:maxLines="2"
        android:ellipsize="end"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

and the code:

txt_id= (TextView)findViewById(R.id.txt_id);
txt_id.setText("data");
txt_id.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View view) {
                   if (isCheck) {
                      txt_id.setMaxLines(10);
                      isCheck = false;
                   } else {
                      txt_id.setMaxLines(2);
                      isCheck = true;
           }
       }
  }
4

Check out my library: https://github.com/AhmMhd/SeeMoreTextView-Android

<com.abdulhakeem.seemoretextview.SeeMoreTextView
    android:id="@+id/textview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
/>

usage:

TextView seemoreTv = (TextView) findViewById(R.id.textview)

seemoreTv.setContent("some really long text here.")

it also works great on RecyclerView.

Simone
  • 425
  • 1
  • 6
  • 17
3

In Kotlin adapter you can write like this to "View More" on TextView

if (News[position].description.length > 150) {
  holder.desc.text = Html.fromHtml(News[position].description.substring(0, 150) + "..." + "<font color='blue'> <u>View More</u></font>")
} else {
   holder.desc.text = News[position].description
}
holder.desc.setOnClickListener {
   if (holder.desc.text.toString().endsWith("View More")) {
      holder.desc.text = News[position].description
   } else {
      if (News[position].description.length > 150) {
        holder.desc.text = Html.fromHtml(News[position].description.substring(0, 150) + "..." + "<font color='blue'> <u>View More</u></font>")
      } else holder.desc.text = News[position].description
   }
}
Aftab Alam
  • 1,969
  • 17
  • 17
2

This solution is a bit easier to implement in code. It doesn't support on-the-fly changes well, but can easily be modified to do so.

public class ExpandableTextView extends TextView {

    private final String readMoreText = "...read more";
    private final int readMoreColor = Color.parseColor("#4A0281");

    private int _maxLines = 4;
    private CharSequence originalText;

    public ExpandableTextView(Context context) {
        super(context);
        init(context);
    }

    public ExpandableTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public ExpandableTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context) {

        ViewTreeObserver vto = getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {

                ViewTreeObserver obs = getViewTreeObserver();
                obs.removeGlobalOnLayoutListener(this);

                truncateText();
            }
        });
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        super.setText(text, type);

        if (originalText == null) {
            originalText = text;
        }
    }

    @Override
    public int getMaxLines() {
        return _maxLines;
    }

    @Override
    public void setMaxLines(int maxLines) {
        _maxLines = maxLines;
    }

    public void truncateText() {

        int maxLines = _maxLines;
        String text = getText().toString();

        if (getLineCount() >= maxLines) {

            int lineEndIndex = getLayout().getLineEnd(maxLines - 1);

            String truncatedText = getText().subSequence(0, lineEndIndex - readMoreText.length() + 1) + readMoreText;

            Spannable spannable = new SpannableString(truncatedText);
            spannable.setSpan(new ForegroundColorSpan(readMoreColor), truncatedText.length() - readMoreText.length(), truncatedText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

            setText(spannable, TextView.BufferType.SPANNABLE);

            super.setMaxLines(_maxLines);
        }
    }

    public void expandText() {
        setText(originalText);
        super.setMaxLines(1000);
    }

    public void reset() {
        originalText = null;
    }
}
miken.mkndev
  • 1,821
  • 3
  • 25
  • 39
2

In the above solution, Expanded text (see more/less) showing, if the length of the text is less than max lines. In this class, I remove this error. You just need to put this class into your code and use it in XML. You can easily modify it according to your requirements (color of expanded text, font style, etc.)

      public class ExpandableTextView extends AppCompatTextView {

    private static Context context;
    private TextView textView;
    private int maxLine = 3;
    private boolean isViewMore = true;

    public ExpandableTextView(Context context) {
        super(context);
        ExpandableTextView.context = context;
        textView = this;
        initViews();
    }

    public ExpandableTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        ExpandableTextView.context = context;
        textView = this;
        initViews();
    }

    public ExpandableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        ExpandableTextView.context = context;
        textView = this;
        initViews();
    }

    public void initViews() {
        if (textView.getText().toString().isEmpty()) {
            return;
        }
        if (textView.getTag() == null) {
            textView.setTag(textView.getText());
        }
        textView.setTypeface(Typeface.createFromAsset(context.getAssets(), "GothamBook.ttf"));
        ViewTreeObserver vto = textView.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                String text, expandText = "See ";
                int lineEndIndex;
                ViewTreeObserver obs = textView.getViewTreeObserver();
                obs.removeGlobalOnLayoutListener(this);
                int lineCount = textView.getLayout().getLineCount();

                expandText += isViewMore ? "More" : "Less";
                if (lineCount <= maxLine) {
                    lineEndIndex = textView.getLayout().getLineEnd(textView.getLayout().getLineCount() - 1);
                    text = textView.getText().subSequence(0, lineEndIndex).toString();
                } else if (isViewMore && maxLine > 0 && textView.getLineCount() >= maxLine) {
                    lineEndIndex = textView.getLayout().getLineEnd(maxLine - 1);
                    text = textView.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + expandText;
                } else {
                    lineEndIndex = textView.getLayout().getLineEnd(textView.getLayout().getLineCount() - 1);
                    text = textView.getText().subSequence(0, lineEndIndex) + " " + expandText;
                }
                textView.setText(text);
                textView.setMovementMethod(LinkMovementMethod.getInstance());
                if (lineCount > maxLine)
                    textView.setText(addClickablePartTextViewResizable(expandText),
                            BufferType.SPANNABLE);
                textView.setSelected(true);
            }
        });
    }

    private SpannableStringBuilder addClickablePartTextViewResizable(final String expandText) {
        String string = textView.getText().toString();
        SpannableStringBuilder expandedStringBuilder = new SpannableStringBuilder(string);
        if (string.contains(expandText)) {
            expandedStringBuilder.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View widget) {
                    textView.setLayoutParams(textView.getLayoutParams());
                    textView.setText(textView.getTag().toString(), BufferType.SPANNABLE);
                    textView.invalidate();
                    maxLine = isViewMore ? -1 : 3;
                    isViewMore = !isViewMore;
                    initViews();
                }

                @Override
                public void updateDrawState(@NonNull TextPaint ds) {
                    ds.setUnderlineText(true);
                    ds.setColor(context.getResources().getColor(R.color.red));
                    ds.setTypeface(Typeface.createFromAsset(context.getAssets(), "GothamMedium.ttf"));
                }
            }, string.indexOf(expandText), string.length(), 0);
        }
        return expandedStringBuilder;
    }
}

If you set dynamic data you need to call initViews() after setting the text into the text view.

tvDescription.setText(sessionModel.getDescription());
tvDescription.initViews();
Nitish Nanda
  • 21
  • 1
  • 4
  • 1
    used this inside a recycler view and it is working perfectly. thanks bro – Erick Apr 08 '21 at 11:36
  • In recycler view it works only first time. but later when scrolldown and scroll up second time then it doesn't works.. – Aditya Patil Jul 01 '21 at 13:11
  • @AdityaPatil As you see all the variables used in this class are local. So they reset when you scroll recycler view as it recreates all the views. You have to manage the **isViewMore** variable from the list data. – Nitish Nanda Nov 20 '21 at 10:37
1

Thanks to Jitender's answer.Improving on it I have done the below implementation based on length of text.This might not be ideal solution if you want View More option exactly after specified number of lines but assuming that there are around 50 characters in single line the solution will work well if you are adjustable with number of lines.Below solution will add View More option if text length is greater than 150 and will ellipsize the text to 150 characters.On clicking on View More it will show complete text with Show Less option and on clicking on Show Less again it will ellipsize the text to 150 characters.No separate view is required.Also it works well with recyclerview item's textview.

if(inputText.length()>150)
        {
            String text=inputText.substring(0,150)+"...";
            final String fulltext=inputText;

            final SpannableString ss = new SpannableString(text+"View More");

            ClickableSpan span1 = new ClickableSpan() {
                @Override
                public void onClick(View textView) {
                    // do some thing
                    SpannableString ss1 = new SpannableString(fulltext+"Show Less");
                    ClickableSpan span2 = new ClickableSpan() {
                        @Override
                        public void onClick(View textView) {
                            // do some thing
                            textView.setText(ss);
                            textView.setMovementMethod(LinkMovementMethod.getInstance());

                        }
                    };
                    ss1.setSpan(span2, fulltext.length(), ss1.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    ss1.setSpan(new ForegroundColorSpan(Color.BLUE), fulltext.length(), ss1.length(),
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);


                    textView.setText(ss1);
                    textView.setMovementMethod(LinkMovementMethod.getInstance());
                }
            };
            ss.setSpan(span1, 153, 162, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            ss.setSpan(new ForegroundColorSpan(Color.BLUE), 153,162,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

            textView.setText(ss);
            textView.setMovementMethod(LinkMovementMethod.getInstance());
        }
else
        {
            textView.setText(inputText);
        }
Android Developer
  • 9,157
  • 18
  • 82
  • 139
1

Try using this library :)

//Add this dependency into App Gradle

implementation 'com.borjabravo:readmoretextview:2.1.0'

Usage:

<com.borjabravo.readmoretextview.ReadMoreTextView
   android:id="@+id/text2"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_marginTop="@dimen/activity_vertical_margin"
   android:text="@string/DemoText"
   app:colorClickableText="#3F51B5"/>

Check This Link: https://github.com/bravoborja/ReadMoreTextView

0

You can use below code for this;

 holder.tvMoreInfo.setText(horizontalList.get(position));

        holder.tvMoreInfo.post(new Runnable() {
            @Override
            public void run() {
                int lineCount = holder.tvMoreInfo.getLineCount();
                if (lineCount<3)
                {

                }else
                {
                    makeTextViewResizable(holder.tvMoreInfo, 3, "...More", true);
                }
            }
        });



 public static void makeTextViewResizable(final TextView tv, final int maxLine, final String expandText, final boolean viewMore) {

    if (tv.getTag() == null) {
        tv.setTag(tv.getText());
    }
    ViewTreeObserver vto = tv.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @SuppressWarnings("deprecation")
        @Override
        public void onGlobalLayout() {
            String text;
            int lineEndIndex;
            ViewTreeObserver obs = tv.getViewTreeObserver();
            obs.removeGlobalOnLayoutListener(this);
            if (maxLine == 0) {
                lineEndIndex = tv.getLayout().getLineEnd(0);
                text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + "<font color=\"#F15d36\">" + expandText + "</font>";
            } else if (maxLine > 0 && tv.getLineCount() >= maxLine) {
                lineEndIndex = tv.getLayout().getLineEnd(maxLine - 1);
                text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + "<font color=\"#F15d36\">" + expandText + "</font>";
            } else {
                lineEndIndex = tv.getLayout().getLineEnd(tv.getLayout().getLineCount() - 1);
                text = tv.getText().subSequence(0, lineEndIndex) + " " + "<font color=\"#F15d36\">" + expandText + "</font>";
            }
            tv.setText(Html.fromHtml(text));
            tv.setMovementMethod(LinkMovementMethod.getInstance());

            tv.setText(
                    addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, lineEndIndex, expandText,
                            viewMore), TextView.BufferType.SPANNABLE);
        }
    });
}

private static SpannableStringBuilder addClickablePartTextViewResizable(final Spanned strSpanned, final TextView tv,
                                                                        final int maxLine, final String spanableText, final boolean viewMore) {
    String str = strSpanned.toString();
    SpannableStringBuilder ssb = new SpannableStringBuilder(strSpanned);

    if (str.contains(spanableText)) {
        ssb.setSpan(new MySpannable(false) {

            @Override
            public void onClick(View widget) {
                tv.setLayoutParams(tv.getLayoutParams());
                tv.setText(tv.getTag().toString(), TextView.BufferType.SPANNABLE);
                tv.invalidate();
                if (viewMore) {
                    makeTextViewResizable(tv, -1, "...Less", false);
                } else {
                    makeTextViewResizable(tv, 3, "...More", true);
                }

            }
        }, str.indexOf(spanableText), str.indexOf(spanableText) + spanableText.length(), 0);

    }
    return ssb;

}
Vishal Vaishnav
  • 3,346
  • 3
  • 26
  • 57
0

It might be late but its the easiest and tested way to handle this issue in recyclerview .

first check the length of textview and set view more if require


if (inventory.getDescription().length()>90) {
  inventoryDescription.setText(Html.fromHtml(inventory.getDescription().substring(0,90)+"..."+"<font color='blue'> <u>View More</u></font>"));
 }
 else inventoryDescription.setText(inventory.getDescription());

and in textview click listener

inventoryDescription.setOnClickListener(new View.OnClickListener() {
            @Override
    public void onClick(View v) {

   if (inventoryDescription.getText().toString().endsWith("View More")) {
       inventoryDescription.setText(inventory.getDescription());
      } 
    else {

   if (inventory.getDescription().length()>90) {
 inventoryDescription.setText(Html.fromHtml(inventory.getDescription().substring(0,90)+"..."+"<font color='blue'> <u>View More</u></font>"));

   }
   else inventoryDescription.setText(inventory.getDescription());
    }

            }
        });

Arslan Ali
  • 17
  • 6
0

I have written a blog post about how I did it in our app. It is based on some of the other solutions here, it can display read more... / read less, and it also works great in RecyclerViews, because the text is calculated on the main thread immediately.

Here's the blog post.

And here's the code, which is also linked in the post.

Daniel Zolnai
  • 16,487
  • 7
  • 59
  • 71
0

I faced some problems with specified solutions, but this library working perfectly with recycler view and solved my problems.

Sample code:

  <io.github.giangpham96.expandabletextview.ExpandableTextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/purple_100"
    android:padding="16dp"
    android:maxLines="10"
    app:expandAction="More"
    app:limitedMaxLines="2"
    app:expandActionColor="@color/blue_500"
    app:originalText="@string/long_text" />

Screenshot

Yasin Ege
  • 605
  • 4
  • 14
0

Kotlin Extension method based on Abdul Hakim's Library

    fun TextView.setSeeMoreOrLessView(msg: String, maxLengthToShowSeeMore: Int){
if (msg.length <= maxLengthToShowSeeMore) {
    text = msg
    return
}
val seeMoreText = "...See more"
val seeLessText = "...See less"
val spannableTextSeeMore = SpannableString("${msg.take(maxLengthToShowSeeMore)}$seeMoreText")
val spannableTextSeeLess = SpannableString("$msg$seeLessText")

val clickableSpan = object : ClickableSpan(){
    override fun onClick(widget: View) {
        //change spannable string
        val currentTag = tag as? String?
        if (currentTag?.equals(seeMoreText) == true){
            text = spannableTextSeeLess
            tag = seeLessText
        } else {
            text = spannableTextSeeMore
            tag = seeMoreText
        }
    }
    override fun updateDrawState(ds: TextPaint) {
        super.updateDrawState(ds)
        ds.isUnderlineText = false
    }
}

spannableTextSeeMore.setSpan(
    clickableSpan,
    maxLengthToShowSeeMore,
    maxLengthToShowSeeMore+seeMoreText.length,
    0
)

spannableTextSeeLess.setSpan(
    clickableSpan,
    msg.length,
    msg.length+seeLessText.length,
    0
)

text = spannableTextSeeMore // default
tag = seeMoreText
movementMethod = LinkMovementMethod() }
0

Add these line of code to and a read more text view just below the text view with ellipse

                var isExpanded = false
                val textView = itemView.commentTextTV
                val readMoreTextView = itemView.readMoreTextView

                item.commentText?.let {
                    itemView.commentTextTV.text = it
                }


                readMoreTextView.setOnClickListener {
                    if (isExpanded) {
                        // Collapse the text view to the original max lines
                        textView.maxLines = 8
                        readMoreTextView.setText(R.string.read_more)
                    } else {
                        // Expand the text view to show all the text
                        textView.maxLines = Int.MAX_VALUE
                        readMoreTextView.setText(R.string.read_less)
                    }
                    isExpanded = !isExpanded
                }

                // Check if the text view is ellipsized (truncated) and show "Read More" if necessary
                textView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
                    override fun onGlobalLayout() {
                        // Remove the global layout listener to avoid multiple calls
                        textView.viewTreeObserver.removeOnGlobalLayoutListener(this)

                        // Get the layout of the text view
                        val layout = textView.layout

                        // If the layout is ellipsized (truncated), show "Read More"
                        if (layout != null) {
                            val lines = layout.lineCount
                            val ellipsisCount = layout.getEllipsisCount(lines - 1)
                            if (ellipsisCount > 0) {
                                readMoreTextView.visibility = View.VISIBLE
                            } else {
                                readMoreTextView.visibility = View.GONE
                            }
                        }
                    }
                })
-2

Instead of using android:layout_alignParentLeft="true" in first textview use android:layout_toLeftOf="@+id/textView1"

This should take care of the overlapping text

Ajit Pratap Singh
  • 1,299
  • 12
  • 24