Add "View More" at the end of TextView after 3 lines

AndroidTextview

Android Problem Overview


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.

Android Solutions


Solution 1 - Android

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

Solution 2 - Android

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

Solution 3 - Android

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

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKanikaView Question on Stackoverflow
Solution 1 - AndroidBiraj ZalavadiaView Answer on Stackoverflow
Solution 2 - Androidpeter.bartosView Answer on Stackoverflow
Solution 3 - AndroidBhavikaView Answer on Stackoverflow