saket / Better-Link-Movement-Method

Attempts to improve how clickable links are detected, highlighted and handled in TextView
Apache License 2.0
780 stars 78 forks source link

Return a Spannable String #20

Closed rayliverified closed 6 years ago

rayliverified commented 6 years ago

Can BetterLinkMovementMethod return a SpannableString? I need to get the source span and then add additional manipulations. Thanks for this great library!

saket commented 6 years ago

Hey Ray, what's your usecase?

rayliverified commented 6 years ago

Hi Saket, thanks for the reply! I would like to use this library in conjunction with FastTextView (https://github.com/lsjwzh/FastTextView) which I am using for it's custom ellipsis feature.

Currently, I'm trying to append "...Read More" to the end of 3 lines and it has been very difficult. The only solution I've found that works in RecyclerView and without delays is FastTextView. What FastTextView does is it emulates the Android TextView and performs view drawing operations itself. It does not extend TextView which is why your library doesn't work with it. Instead, if this library could return a span, I'd be able to pass that into FastTextView.

Since you've had a lot of experience working with TextViews, do you have any suggestions about how to append "...Read More" to the end of text? The only way I've found is, unfortunately, to create your own TextView.

saket commented 6 years ago

No problem!

BetterLinkMovementMethod only handles touch events for ClickableSpans that are already set on a TextView. It does not store or modify the text in any manner. Can I see your usage of this library?

rayliverified commented 6 years ago

I'm using Better Link Movement Method in my RecyclerView Viewholder to display a post. It works really amazingly!

val linkMovementMethod = BetterLinkMovementMethod.linkify(Linkify.WEB_URLS, itemView.text_description)
        itemView.text_description.setOnTouchListener(object: View.OnTouchListener {
            @SuppressLint("ClickableViewAccessibility")
            override fun onTouch(v: View?, event: MotionEvent?): Boolean {
                val handledByMovementMethod = linkMovementMethod.onTouchEvent(itemView.text_description, itemView.text_description.text as Spannable, event)
                handledByMovementMethod || itemView.onTouchEvent(event)
                return true
            }
        })

This is the new version where I would like a spannable string returned.

        val overflowText = mContext.getString(R.string.post_continue_reading)
        val spannableString = SpannableString(item.body)
        itemView.text_description.text = spannableString
        val spannableOverflow = EllipsisSpan(overflowText, ContextCompat.getColor(mContext, R.color.textHint))
        itemView.text_description.customEllipsisSpan = spannableOverflow

Instead of SpannableString(item.body), I think it'd work if I pass a Spannable with LinkMovementMethod?

saket commented 6 years ago

I'm sorry, I'm finding it difficult to understand your usecase. I think you're confusing spans with MovementMethod. A MM is only responsible for handling touch events on a TextView. If you want to add a "Read more" span, you can get the text from your TextView directly.

Here's what I'd do:

val text = itemView.text_description.text

itemView.text_description.text = Truss()
  .append(text)
  .pushSpan(spannableOverflow)
  .append(overflowText)
  .popSpan()
  .build()

Truss: https://gist.github.com/JakeWharton/11274467

You could also look at 3rd party libraries:

rayliverified commented 6 years ago

Hi @saket, thank you for the recommendations. You are right that I may be confusing spans and movement methods.

Here's my understanding. BetterLinkMovementMethod applies the LinkMovementMethod internally and then adds it's own improved touch events.

But doesn't LinkMovementMethod detects links in the text and then turns those links into Clickable spans? So this library actually has access to the spanned text?

I'm thinking that I want to apply my own spans to the text so would it be possible for this library to return the text after it has modified it?

Sorry if I wasn't clear.

saket commented 6 years ago

Ah, I get your question now.

But doesn't LinkMovementMethod detects links in the text and then turns those links into Clickable spans?

No, that is done by Linkify. If you weren't using BetterLinkMovementMethod, you'd normally be doing this:

TextView textView = (TextView) findViewById(R.id.text1);
textView.setMovementMethod(BetterLinkMovementMethod.newInstance());
Linkify.addLinks(textView, Linkify.PHONE_NUMBERS);

Linkify.addLinks() gets the text from the TextView, finds all links, adds ClickableSpans to the text and sets it back on the TextView. Once the spans are set by Linkify, you can get the modified text directly from TextView.getText().

BetterLinkMovementMethod just offers a convenience function called linkify() so that setMovementMethod() and Linkify.addLinks() can be combined using one function call:

TextView textView = (TextView) findViewById(R.id.text1);
BetterLinkMovementMethod.linkify(Linkify.PHONE_NUMBERS, textView);

I now realize that I should have never offered this function as it's sort of a hidden behavior and confuses users.

Does this answer your question? TL;DR: Just get the spanned text from your TextView.

rayliverified commented 6 years ago

Ah! Thanks for the detailed explanation. This answers my question and has been extremely helpful. I very much appreciate you taking the time to explain this!

I now realize that I should have never offered this function as it's sort of a hidden behavior and confuses users.

I don't think this behavior is confusing, I just didn't know enough to understand how it works. I think it's great to have the linkify function because it is very convenient to call a library on a single line. Thank you for the help and the great work on this library!

saket commented 6 years ago

Glad I could help :)