7

I want to make a part of my text in my TextView a clickable Url, I know I can use SpannableString to achieve a clickable part by using something like this

SpannableString mySpannableString = new SpannableString("a string");
ClickableSpan myClickableSpan = new ClickableSpan() {
    @Override
    public void onClick(View textView) {
        // do something
    }
    @Override
    public void updateDrawState(TextPaint ds) {
            super.updateDrawState(ds);
            ds.setUnderlineText(false);
        }
};
mySpannableString.setSpan(myClickableSpan, 2, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

But if I use a string from my localization resources as my SpannableString, then the length of the string could vary, which means the wrong set of characters may be clickable. How would I go about this?

Simon Andersson
  • 751
  • 1
  • 9
  • 29
  • 1
    define your requirement from where to where that you need to make it clickable or whats the expected part to be clickable – Charuක Jan 18 '17 at 15:38
  • [This](http://stackoverflow.com/questions/10696986/how-to-set-the-part-of-the-text-view-is-clickable) is similar question to yours. – sharp Jan 26 '17 at 08:07
  • @FiN Similar, yes, but it deffers from mine since the index of my span varies, where in that case it doesn't – Simon Andersson Jan 26 '17 at 12:07
  • Check my answer to this question: [https://stackoverflow.com/questions/45504255/handle-localized-string-contains-a-link-in-a-single-textview/53724884#53724884](https://stackoverflow.com/questions/45504255/handle-localized-string-contains-a-link-in-a-single-textview/53724884#53724884) – Milan Dec 11 '18 at 13:12
  • Check my answer on the following post: https://stackoverflow.com/questions/45504255/handle-localized-string-contains-a-link-in-a-single-textview/53724884#53724884 – Milan Dec 11 '18 at 13:13

2 Answers2

4

All I've found are two sub-optimal ways. It does still use Spannable, but hear me out, as I'm in the exact situation you speak of.

First, there's the method that you mention, and you can go through a giant switch statement of languages and solve the span for each one. This is horrific, of course, but it is the most accurate way to ensure the right spans are clickable.

E.g.

if (Locale.getDefault().getLanguage().contentEquals("en") {
    // spannable gets substring a,b
else if (Locale.getDefault().getLanguage().contentEquals("fr") {
    // spannable gets substring x,y
}

Again, not good.

Now, if your translators can handle the string broken up into substrings, IE

<string name=suba>Some intro text to</string>
<string name=sublinka>linkable A</string>
<string name=subb>and then some intro text to</string>
<string name=sublinka>linkable B</string>

Then you can programmatically build the Spannable strings like:

TextView textView = (TextView) findViewById(R.id.text_view);

String subA = getString(R.string.suba);
String subLinkA = getString(R.string.sublinka);
String subB = getString(R.string.subb);
String subLinkB = getString(R.string.sublinkb);

SpannableStringBuilder spannableText = new 
SpannableStringBuilder(subA);

    spannableText.append(subLinkA);
    spannableText.setSpan(new ClickableSpan() {
        @Override
        public void onClick(View widget) {
            Toast.makeText(this, "Sub Link A Clicked", Toast.LENGTH_SHORT).show();
        }
    }, spannableText.length() - subLinkA.length(), spannableText.length(), 0);

    spannableText.append(subB);

    spannableText.append(subLinkB);
    spannableText.setSpan(new ClickableSpan() {
        @Override
        public void onClick(View widget) {
            Toast.makeText(this, "Sub Link B Clicked", Toast.LENGTH_SHORT).show();
        }
    }, spannableText.length() - subLinkB.length(), spannableText.length(), 0);

    textView.setMovementMethod(LinkMovementMethod.getInstance());
    textView.setText(spannableText, TextView.BufferType.SPANNABLE);

Like I say, it truly is a "least worse" kind of situation, but I thought I'd at least throw it out there, as I don't think there's any other ways to do this with localization, as the the compiler has no knowledge of how a string is translated.

Wes Winn
  • 551
  • 5
  • 15
1

in your strings.xml do html like this :

english : <![CDATA[Makan <b>daging</b> adalah terlalu banyak karbohidrat]]>

indonesia : <![CDATA[Eating <b>meat</b> is too much carbohydrates]]>

Then in your activity, you can trace the span position and add the ClickableSpan:

class MainActivity: AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        setupView()
    }

    private fun setupView() {
        val htmlSpan = SpannableString(HtmlCompat.fromHtml(
            getString(R.string.click_me_button),
            HtmlCompat.FROM_HTML_MODE_COMPACT
        ))
        val styleSpan = htmlSpan.getSpans(0, htmlSpan.length, StyleSpan::class.java).first()
        val spanStart = htmlSpan.getSpanStart(styleSpan)
        val spanEnd = htmlSpan.getSpanEnd(styleSpan)
        val clickSpan = object:ClickableSpan() {
            override fun onClick(widget: View) {
                Toast.makeText(this@MenghubungkanProdukActivity, "Hehehehe", Toast.LENGTH_LONG).show()
                Timber.d("HEHEHE")
            }
        }
        htmlSpan.setSpan(clickSpan, spanStart, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

        binding.textWarning.text = htmlSpan
        binding.textWarning.movementMethod = LinkMovementMethod.getInstance()
    }
}

Source : Localization Clickable Span Android

zihadrizkyef
  • 1,849
  • 3
  • 23
  • 46