简体   繁体   中英

Handling Multiple ClickableSpan in a TextView

I've been stuck at this problem for long. What I'm having is a simple string "This is a link and this is another link" . I want to have both "link" words click-able, having different URLs to open in browser.

  • The simplest way I can do is to set Click-able Span on both "link" words with different URLs, but the problem I'm facing is finding the start and end positions of the span. The text is dynamic, and I have to programmatically find the positions.
  • One approach would be to find the first occurrence of the word 'link', find the start and end positions and set the span, and then the second occurrence. But that is not reliable. The text may contain more than one kind of repeated words, like "This is a cat link and this is another cat link" . Here I have to link both "cat" and "link" words with different URLs via Click-able Span. How do I go about it?

Try in this manner

String s="Cat link 1 Cat link 2 Cat link 3";
    SpannableString ss = new SpannableString(s);
    String first ="Cat link 1";
    String second ="Cat link 2";
    String third ="Cat link 3";
    int firstIndex = s.toString().indexOf(first);
    int secondIndex = s.toString().indexOf(second);
    ClickableSpan firstwordClick = new ClickableSpan() {
        @Override
        public void onClick(View widget) {
            ///............
        }
    }; 
    ClickableSpan secondwordClick = new ClickableSpan() {
        @Override
        public void onClick(View widget) {
            ///............
        }
    }; 
    ss.setSpan(firstwordClick,firstIndex, firstIndex+first.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    ss.setSpan(secondwordClick,secondIndex, secondIndex+second.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    textView.setLinksClickable(true);
    textView.setMovementMethod(LinkMovementMethod.getInstance());
    textView.setText(ss,BufferType.SPANNABLE);

If you cannot programmatically find the difference in the links, then you cannot expect anything else to be able to do it. As the developer, you need a system.

You need to be able to identify the clickable spans - since the links are unique, the text that identifies them must also be unique. This would be a problem for your users, most likely.

You can get an ordered list of URLs and then if the links are indistinguishable, simply use them in the order you receive. Or, you need to change the rules of creating the links or the order in which they are displayed.

One simple way to do this would be to include an identifier before the link say /*/ then using this find the start and end position for link. Once you have that first replace the identifier with a "" and then click away.

I have string something such "you order with orderId {b1j2gh4b} has been claimed by bla bla sotre with phone number (1234124124)"

I am using these braces so as to find out the index of of orderID and Phone number

 String notificationMessage = mArrListNotification.get(position).getMessage();
            boolean isPhoneNumberAvailable = false, isOrderIdAvailable = false;
            int phoneStartIndex = 0, phoneEndIndex = 0, orderIdStartIndex = 0, orderIdEndIndex = 0;

    //getting index on the basis of braces added to text
            if (notificationMessage.contains("(")) {
                isPhoneNumberAvailable = true;
                phoneStartIndex = notificationMessage.indexOf("(");
                phoneEndIndex = notificationMessage.indexOf(")");
            }
            if (notificationMessage.contains("{")) {

                orderIdStartIndex = notificationMessage.indexOf("{");
                orderIdEndIndex = notificationMessage.indexOf("}");

            }

            // we got the index so remove braces
            notificationMessage = notificationMessage.replace("(", " ");
            notificationMessage = notificationMessage.replace(")", " ");
            notificationMessage = notificationMessage.replace("{", " ");
            notificationMessage = notificationMessage.replace("}", " ");

            viewHolder.txtNotificationMessage.setText(notificationMessage, TextView.BufferType.SPANNABLE);
            Spannable mySpannablePhoneNumber = (Spannable) viewHolder.txtNotificationMessage.getText();
            Spannable mySpannableOrderID = (Spannable) viewHolder.txtNotificationMessage.getText();
            ClickableSpan mySpanPhoneClick = new ClickableSpan() {
                @Override
                public void onClick(View widget) {

                    currentPosition = (Integer) widget.getTag();

                    String message = mArrListNotification.get(currentPosition).getMessage();
                    int startIndex = message.indexOf("(");
                    int endIndex = message.indexOf(")");
                    phoneNumber = message.substring(startIndex + 1, endIndex);

     Log.i("Phone Number", phoneNumber clicked)

                }
            };
            ClickableSpan mySpanOrderClick = new ClickableSpan() {
                @Override
                public void onClick(View widget) {
                    currentPosition = (Integer) widget.getTag();

                    String message = mArrListNotification.get(currentPosition).getMessage();
                    int startIndex = message.indexOf("{");
                    int endIndex = message.indexOf("}");
                    String orderID = message.substring(startIndex + 1, endIndex);
    // Log.i("Order id", orderID  clicked)

                }
            };
            if (isPhoneNumberAvailable) {
                mySpannablePhoneNumber.setSpan(mySpanPhoneClick, phoneStartIndex + 1, phoneEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
            if (isOrderIdAvailable) {
                mySpannableOrderID.setSpan(mySpanOrderClick, orderIdStartIndex + 1, orderIdEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }

I Have Created a generalized function for this purpose hope this helps

fun TextView.makeLinks(vararg links: Triple<String, View.OnClickListener,LinkProperties>) {

    try {
        val spannableString = SpannableString(this.text)
        for (link in links) {
            val clickableSpan = object : ClickableSpan() {
                override fun updateDrawState(textPaint: TextPaint) {
                    super.updateDrawState(textPaint)
                    textPaint.isUnderlineText = link.third.isUnderlineText
                    textPaint.isFakeBoldText = link.third.isBoldText
                    textPaint.color = link.third.color ?: Color.parseColor(DCColorPicker.BLACK)
                }

                override fun onClick(view: View) {
                    Selection.setSelection((view as TextView).text as Spannable, 0)
                    view.invalidate()
                    link.second.onClick(view)
                }
            }
            val startIndexOfLink = this.text.toString().indexOf(link.first)
            spannableString.setSpan(
                clickableSpan, startIndexOfLink, startIndexOfLink + link.first.length,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }
        this.movementMethod = LinkMovementMethod.getInstance() // without LinkMovementMethod, link can not click
        this.setText(spannableString, TextView.BufferType.SPANNABLE)
    } catch (e: Exception) {
        Log.e(TAG, "makeLinks: " + e.message)
    }
}

and you can add N no. of properties here

 data class LinkProperties(val isUnderlineText: Boolean = false, val isBoldText: Boolean = false, val color: Int?=null)

and for calling you can use this function like this

val linkBoldOnly = LinkProperties(isBoldText = true)
val linkBoldUnderline = LinkProperties(isUnderlineText = true, isBoldText = true)

binding.name.makeLinks(
Triple("text", View.OnClickListener { openMore() }, linkBoldUnderline),
Triple(name.toString(), View.OnClickListener { itemClick() }, linkBoldOnly)
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM