2

I am developing an Android application. In my app, I need to implement, readmore feature to TextView like in Facebook post. My app also has newfeeds feature like Facebook. I am implementing for each TextView in the RecyclerView Adapter. But it is giving me error.

In the RecyclerAdapter I am setting the Readmore feature with Custom Event Listener like this. This is happening in onBindViewHolder of Adapter.

   viewHolder.tvTitle.setText(post.getTitle());
    listener.ReadMoreListener(viewHolder.tvTitle, 3, "Read more", true);

This is the event listener interface

public interface AdapterListener{

        public void ReadMoreListener(TextView textView, int maxLine, String expandText, boolean viewMore);
    }

This is how I implement event and readmore feature to each TextView in Fragment.

In onCreateView of Activity, I set up event like this.

adapter.setAdapterListener(new MemeListAdapter.AdapterListener() {

            @Override
            public void ReadMoreListener(TextView textView, int maxLine, String expandText, boolean viewMore) {
                makeTextViewResizable(textView, maxLine, expandText, viewMore);
            }
        });

These are the required methods to implement readmore feature in Fragment.

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;//This line is throwing error.
                    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 ClickableSpan() {

                @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, "View Less", false);
                    } else {
                        tv.setLayoutParams(tv.getLayoutParams());
                        tv.setText(tv.getTag().toString(), TextView.BufferType.SPANNABLE);
                        tv.invalidate();
                        makeTextViewResizable(tv, 3, "View More", true);
                    }

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

        }
        return ssb;

    }

When I run my code, it is giving me this error:

enter image description here

I commented "this line is throwing error" in the code to where the error is thrown. I referenced on this link - Add "View More" at the end of TextView after 3 lines. What is the cause of the error? I know it is null exception error. How can I fix it?

I found out that tv.getLayout() always return null. How can I set Layout to TextView not to return null?

halfer
  • 19,824
  • 17
  • 99
  • 186
Wai Yan Hein
  • 13,651
  • 35
  • 180
  • 372

1 Answers1

0

The easiest answer for this is to set the maximum line of the textview programmatically. For example, assume you have a tvReadMore textview below your tvTitle textview. First check if the text for your tvTitle textview is more enough to display the tvReadMore. if so, set the maxLine to Integer.MAX_VALUE. Take a look at the following sample code

if(post.getTitle.length() > 50) { //assuming you want to show the [Read more] for more than 50 characters
   if(ViewHolder.tvReadMore.getText().toString().equalsIgnoreCase("Read More")) { //you should set the text of tvReadMore textview to [Read more] in the beginning
       viewHoler.tvTitle.setMaxLines(Integer.MAX_VALUE); //set the maximum line to Maximum integer
       viewHolder.tvReadMore.setText("Show Less")
   } else {
        viewHoler.tvTitle.setMaxLines(3); //set the maximum line to 3
        viewHolder.tvReadMore.setText("Read more")
   }
} else { //if there are no enough characters to show the read more text
   viewHolder.tvTitle.setVisibility(View.GONE); //hide the read more textview
}

I have seen a lot of work arounds to implement the read more feature for textview but, in my opinion, this is the easiest and simplest way so far. The nice thing about this is, it works for both simple textview and a textview in a recyclerview. if you want to add three dots at the end of your text, when there are more lines hidden, you can add

 android::ellipsize(end)

to your tvTitle textview.

Hagos Alema
  • 321
  • 2
  • 10