Add "View More" at the end of TextView after 3 lines [duplicate]
Asked Answered
B

3

52

I want to add "More" functionality after three lines of text. The text contains the description which is more than 10 lines. so we have decided to add "More" after three lines of text. Like:

enter image description here

when the text is showing the complete description, then it should show "Less" button at the end of text which again compress the TextView.

Barbrabarbuda answered 30/10, 2013 at 6:29 Comment(3)
Use customized TextView..Hanse
See Aleks' answer here #12712581.Cagliostro
please see this sample may help (It is the extracted solution for my app requirement from previous answers).We can update/enhance the library as per request github.com/arshadbinhamza/ViewMoreAlsup
W
107

Try this may help you working fine with me.

    public class MainActivity extends Activity {

    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
        makeTextViewResizable(tv, 3, "View More", true);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);

        return 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 OnGlobalLayoutListener() {

            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                String text;
                int lineEndIndex;
                ViewTreeObserver obs = tv.getViewTreeObserver();
                obs.removeOnGlobalLayoutListener(this);

                if (maxLine == 0) {
                    lineEndIndex = tv.getLayout().getLineEnd(0);
                    text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + expandText;
                } else if (maxLine > 0 && tv.getLineCount() >= maxLine) {
                    lineEndIndex = tv.getLayout().getLineEnd(maxLine - 1);
                    text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + expandText;
                } else {
                    lineEndIndex = tv.getLayout().getLineEnd(tv.getLayout().getLineCount() - 1);
                    text = tv.getText().subSequence(0, lineEndIndex) + " " + expandText;
                }
                tv.setText(text);
                tv.setMovementMethod(LinkMovementMethod.getInstance());
                tv.setText(
                            addClickablePartTextViewResizable(SpannableString(tv.getText().toString()), tv, lineEndIndex, expandText,
                                    viewMore), 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 ClickableSpan() {

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

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

        }
        return ssb;
    }
}

UPDATE : Remove UnderLine from spaneble text

  1. Create Custom ClickableSpan
public class MySpannable extends ClickableSpan {

    private boolean isUnderline = false;

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

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

    }

    @Override
    public void onClick(View widget) {

    }
}
  1. Change in addClickablePartTextViewResizable() method
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(), BufferType.SPANNABLE);
                        tv.invalidate();
                    if (viewMore) {
                        makeTextViewResizable(tv, -1, "View Less", false);
                    } else {
                        makeTextViewResizable(tv, 3, "View More", true);
                    }
                }
            }, str.indexOf(spanableText), str.indexOf(spanableText) + spanableText.length(), 0);

        }
        return ssb;
    }

OutPut:

enter image description here

enter image description here

Waxy answered 30/10, 2013 at 7:33 Comment(37)
Hi please add the layout(activity_main) too .Philana
This is working pretty good but it ruins my line breaks (\n). Any idea how to keep them?Mima
@biraj: Can you please tell how can i get rid of the underline in the above code. I tried out quite a few solutions mentioned in the below link, but nothing is working :( #4097351Cobia
@sneha What do you mean by rid? Can you please explainWaxy
@BirajZalavadia: Sorry for not being very clear in my question. What i meant was how can i remove the underline that comes below "View More". I was trying out few possible options but was not working for me. But I could finally managed to get it done. :)Cobia
@sneha for that you need to create custom ClickableSpan by extend ClickableSpan.Waxy
@sneha I Update the answer with solution try it and let me know.Waxy
@BirajZalavadia: I wanted to handle click on text once it is expanded. Like if i click on anywhere on text once expanded it should collapse again with "view more". I tried but facing issue with onclick. Any idea how i can achieve this ?Cobia
Hi it is not let me to break lines by \n. I also tried replacing \n with <br> but it is not breaking linesVallecula
Perfect output ,, read more or read less on textview clickable area :)Creosol
\n \n Not working.Harriettharrietta
To solve the line break \n issue, change Spanned strSpanned to String and remove Html.fromHtml when sending in the parameter.Kerplunk
How to change the text color of view more!!!Gel
@BirajZalavadia, your code is working pretty well. ThanksCynical
how to do this if my text view is part of a row in a list view?Nannettenanni
perfect solution :) +1 for answerDairen
Can you please mention how I can change the text color of "View More" and "View Less"Bipod
To change color of "View More" go to class "MySpannable" and add this line to method updateDrawState() ds.setColor(Color.parseColor("#4888F3")); and now send true while calling new MySpannable(true)Inclinometer
getting exception : java.lang.StringIndexOutOfBoundsException: length=129; regionStart=0; regionLength=-30Wilhelm
Am I the only one noticing that the output is plain wrong? The goal is to limit the TextView to 3! Lines. The collapsed output shows 4 lines... this solution is garbageMakebelieve
You are awesome bro, thanks. It would be awesome if you can add "..." before Show more. Thanks.Riti
@BirajZalavadia your answer is very good and I am using it. but when my text has scroll by click on View Less I do not see any text. do you have any solution?Lunkhead
@Lunkhead you can add textview in scrollview.Measurement
@YakubMoriswala yes. I solved my problem with nestedScrollView. thankyouLunkhead
Getting java.lang.NullPointerException all time at first line of else condition.Gushy
working fine...thanksSeidler
@BirajZalavadia Its not work perfectly in RecyclerView for all items. any suggestions?Forehanded
@BirajZalavadia i used your code but nothing is happening , i am getting the whole text in a line , not getting view more option.,please suggest meIniquity
@RajshreeTiwari make sure that textview property "singleLine" is not set to true.Waxy
@BirajZalavadia thank you i fixed that , one more thing i had to ask that how can i change particular "view more" and "view less" color without underline belowIniquity
@RajshreeTiwari I update my answer. Updated "MySpannable" class. look into it You will get ideaWaxy
@BirajZalavadia yes it helped me , thank you ..:)Iniquity
not working in recycler view....Baugher
"View more" is dividing into two lines, "View " is in 3rd line and "more" is in 4th line. How can i make change so that "view more" will be in 3rd line only.Outmoded
not working long discriptionHerdsman
screenshots: collapsed: imgur.com/a/Yhq8rxe, expanded: imgur.com/a/qqyJlGSOkapi
@BirajZalavadia good solution and working perfectly.. but is there any way to do the same with some animation?? ..because this is looking a little awkward while expanding and collapsingFoilsman
T
29

Here is a simple custom ExpandableTextView. Instead using See More text, it uses Compound Drawable on the Bottom:

public class ExpandableTextView extends TextView implements OnClickListener
{   
    private static final int MAX_LINES = 5;
    private int currentMaxLines = Integer.MAX_VALUE;

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

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

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter)
    {
        /* If text longer than MAX_LINES set DrawableBottom - I'm using '...' icon */
        post(new Runnable()
        {
            public void run()
            {
                if (getLineCount()>MAX_LINES)
                    setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, R.drawable.icon_more_text);
                else
                    setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);

                setMaxLines(MAX_LINES);             
            }
        });
    }


    @Override
    public void setMaxLines(int maxLines)
    {
        currentMaxLines = maxLines;
        super.setMaxLines(maxLines);
    }

    /* Custom method because standard getMaxLines() requires API > 16 */
    public int getMyMaxLines()
    {
        return currentMaxLines;
    }

    @Override
    public void onClick(View v)
    {
        /* Toggle between expanded collapsed states */
        if (getMyMaxLines() == Integer.MAX_VALUE)
            setMaxLines(MAX_LINES);
        else
            setMaxLines(Integer.MAX_VALUE);
    }

}
Trainor answered 30/3, 2014 at 23:2 Comment(8)
This is the best answer I have ever found. Really really. But can I have the code for R.drawable.icon_more_text. What is that? XML or Photo? Please.Filial
Where does StylishTextView come from?Dried
@WaiYanHein: it can be any small picture, both xml or png. I used an icon with three dots.Trainor
@caffinatedmonkey I just updated it to simple TextView.Trainor
Awesome Solution Bro. This works even in Scrolling..Thanks a tonLustral
@AnishKumar and why downvoting? My answer is even 1 year older than the linked snippet :)Trainor
@AnishKumar It seems like some kind of time paradox to reference something 3 years old in that time 4 years ago. And it's different.Trainor
I just wasted my three hours and then you save me broVaporish
V
8

This Will break Line if there is \r\n or \n in string

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 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(tv.getText()
                                .toString(), tv, maxLine, expandText,
                                viewMore), 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(tv.getText()
                                .toString(), tv, maxLine, expandText,
                                viewMore), 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(tv.getText()
                                .toString(), tv, lineEndIndex, expandText,
                                viewMore), BufferType.SPANNABLE);
            }
        }
    });

}

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

    if (strSpanned.contains(spanableText)) {
        ssb.setSpan(
                new ClickableSpan() {

                    @Override
                    public void onClick(View widget) {

                        if (viewMore) {
                            tv.setLayoutParams(tv.getLayoutParams());
                            tv.setText(tv.getTag().toString(),
                                    BufferType.SPANNABLE);
                            tv.invalidate();
                            makeTextViewResizable(tv, -5, "...Read Less",
                                    false);
                            tv.setTextColor(Color.BLACK);
                        } else {
                            tv.setLayoutParams(tv.getLayoutParams());
                            tv.setText(tv.getTag().toString(),
                                    BufferType.SPANNABLE);
                            tv.invalidate();
                            makeTextViewResizable(tv, 5, "...Read More",
                                    true);
                            tv.setTextColor(Color.BLACK);
                        }

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

    }
    return ssb;

}
Vallecula answered 4/6, 2015 at 6:7 Comment(4)
this is best and it worked out to be very well with special characters in itCladding
how to do this if my text view is part of a row in a list view?Nannettenanni
i didn't understand which one we should use? i have TextView and String =Description; how can i apply your method to it?Cogwheel
Get crashed when used in recyclerview.Monetary

© 2022 - 2024 — McMap. All rights reserved.