How do I make a portion of a Checkbox's text clickable?
Asked Answered
B

11

67

I'm trying to create a link in my textbox's adjacent text. This link however is not a URL, but should act as a button so that I can perform a few tasks in the onItemClick event. I'm basically connecting this to a view that shows our End User License Agreement (hard coded).

How can I accomplish this?

Thanks in advance.

Brotherton answered 18/11, 2011 at 15:13 Comment(0)
D
37

There actually is an elegant solution, using CheckBox and single TextView. Along with a combinations of TextView.setClickable(), Intent Filter, and TextView.setMovementMethod().

You have main view (here, I called it ClickableTextViewExample):

package id.web.freelancer.example;

import android.app.Activity;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.widget.CheckBox;
import android.widget.TextView;

public class ClickableTextViewExampleActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        CheckBox checkbox = (CheckBox)findViewById(R.id.checkBox1);
        TextView textView = (TextView)findViewById(R.id.textView2);

        checkbox.setText("");
        textView.setText(Html.fromHtml("I have read and agree to the " +
                "<a href='id.web.freelancer.example.TCActivity://Kode'>TERMS AND CONDITIONS</a>"));
        textView.setClickable(true);
        textView.setMovementMethod(LinkMovementMethod.getInstance());
    }
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <CheckBox
            android:id="@+id/checkBox1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="CheckBox" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView"
            android:clickable="true" />

    </LinearLayout>

</LinearLayout>

TCActivity.java

package id.web.freelancer.example;

import android.app.Activity;
import android.os.Bundle;

public class TCActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tc);
    }

}

tc.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

        <TextView
        android:id="@+id/tcView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Terms and conditions" />


</LinearLayout>

and the final piece of codes that glue it all, the AndroidManifest.xml:

<activity android:name="TCActivity">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="id.web.freelancer.example.TCActivity" />  
    </intent-filter>            
</activity>

Here comes, the explanations:

textView.setText(Html.fromHtml("I have read and agree to the " +
                     "<a href='id.web.freelancer.example.TCActivity://Kode'>TERMS AND CONDITIONS</a>"));
textView.setClickable(true);
textView.setMovementMethod(LinkMovementMethod.getInstance());

setClickable will allow you to click on textView. But not the HREF link. To do that, you will have to use setMovementMethod() and set it to LinkMovementMethod.

After that, you need to catch the URL. I did this using intent-filter in AndroidManifest.xml

<action android:name="android.intent.action.VIEW" />
<data android:scheme="id.web.freelancer.example.TCActivity" />  

It catch VIEW command and it only filter URL starting with id.web.freelancer.example.TCActivity://

Here's the package for you to try it out and here's the github repository. Hope this helped

Dovetailed answered 18/11, 2011 at 16:26 Comment(10)
Nice solution @silent! :) One always learn something new in here. I think my answer is still much simpler, easier to maintain, and the developer has more control over the result. I wonder, which one is more efficient space/performance wise? Thoughts? Still, well done.Cthrine
I don't really know. Never try to see which one is optimized :D. Though, my solution will make it easy on the layout side. Only one TextView.Dovetailed
Works great and looks smooth. Thanks again.Brotherton
Sorry, but for real elegance you can't go past @Gopinath's three-line solution below - it uses just one control and does all that is required.Nitz
@mike: On the inside, it does the same 3 lines solution. However, my solution doesn't open webpage. It opened another Activity.Dovetailed
add android:gravity="top" to get the checkbox aligned with text.Vambrace
Multiline text gets clipped at the bottom unless you add android:gravity="fill". Also consider adding style="?android:checkboxStyle".Sylvanite
What is ://Kode ?Pryor
You will get warning Scheme matching is case sensitive and should only use lower-case characters when you pass Capital letters in <data>. You can use id.web.freelancer.example.termsactivity and pass same to same in <a href=''>Pryor
I can't use a separate TextView because there is a big spacing between CheckBox and TextView #59580195Clotho
S
76

You may want only part of the text to be a clickable link, while the rest of the checkbox behaves as usual, i.e. you can click the other text to toggle the state.

You can set up your checkbox like so:

CheckBox checkBox = (CheckBox) findViewById(R.id.my_check_box);

ClickableSpan clickableSpan = new ClickableSpan() {
    @Override
    public void onClick(View widget) {
        // Prevent CheckBox state from being toggled when link is clicked
        widget.cancelPendingInputEvents();
        // Do action for link text...
    }
    @Override
    public void updateDrawState(TextPaint ds) {
        super.updateDrawState(ds);
        // Show links with underlines (optional)
        ds.setUnderlineText(true);
    }
};

SpannableString linkText = new SpannableString("Link text");
linkText.setSpan(clickableSpan, 0, linkText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
CharSequence cs = TextUtils.expandTemplate(
    "CheckBox text with link: ^1 , and after link", linkText);

checkBox.setText(cs);
// Finally, make links clickable
checkBox.setMovementMethod(LinkMovementMethod.getInstance());
Soutache answered 7/11, 2017 at 20:29 Comment(10)
Awesome thanks Daniel this is exactly what I was looking for! Is there a way to accomplish cancelPendingInputEvents() on API <19?Owe
This should be the accepted answer. widget.cancelPendingInputEvents() is doing the magic!!!Joplin
"ss" is undefined?Lole
Updated variable names in example.Soutache
Works fine, but even when clicking the ClickableSpan, the pressed stated is selected for the CheckBox drawable as well, even if it's not toggled. Any suggestion on how to avoid this?Berga
@Berga I haven't found a solution for the state change, but at least you can save it and restore it after the click https://mcmap.net/q/296839/-clicking-on-clickablespan-in-checkbox-changes-its-stateCalliper
@Daniel Schuler, i am not able to set 2 underlined text here, please suggest me how to do thatPrytaneum
@Prytaneum If you mean two different links, see definition of TextUtils.expandTemplate() with ^1, ^2, etc. to insert link text. Not sure how you'd tell them apart in onClick(), however.Soutache
@Berga my solution was to remove the ripple effect to the Checkbox using code at this link gist.github.com/Jeevuz/b775e0d5f0dabdcaefe4 It's not the perfect solution, but it works well since checkboxes already have their "check/uncheck" animationAbrasion
This should be the accepted answer. Also, here's the kotlin version of @Mattia Ruggiero's method that removes the ripple: checkBox.apply { background = (background as? RippleDrawable)?.findDrawableByLayerId(0) }Rupert
I
53

The following code worked for me on KitKat. I am yet to test on below versions of Android.

String checkBoxText = "I agree to all the <a href='http://www.redbus.in/mob/mTerms.aspx' > Terms and Conditions</a>";

checkBoxView.setText(Html.fromHtml(checkBoxText));
checkBoxView.setMovementMethod(LinkMovementMethod.getInstance());
Immotile answered 11/2, 2014 at 12:5 Comment(5)
can anyone try this on lower versions? this solution is perfect!Huai
@Immotile Yep, I can confirm that this works all the way back to Android 2.2.2 (Froyo, Samsung GT-7510). I love this, the solution is so simple and elegant, it truly is (as X.X_Mass_Developer says) perfect!Nitz
added this to my app. it's trying to launch an activity with the name of your URL string.Association
I'm getting a bug with this: pressing the lower part of the checkbox launches the URL instead of toggling the checkWood
@Wood This is a problem with LinkMovementMethod, you can use this to solve it: github.com/saket/Better-Link-Movement-MethodGutty
D
37

There actually is an elegant solution, using CheckBox and single TextView. Along with a combinations of TextView.setClickable(), Intent Filter, and TextView.setMovementMethod().

You have main view (here, I called it ClickableTextViewExample):

package id.web.freelancer.example;

import android.app.Activity;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.widget.CheckBox;
import android.widget.TextView;

public class ClickableTextViewExampleActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        CheckBox checkbox = (CheckBox)findViewById(R.id.checkBox1);
        TextView textView = (TextView)findViewById(R.id.textView2);

        checkbox.setText("");
        textView.setText(Html.fromHtml("I have read and agree to the " +
                "<a href='id.web.freelancer.example.TCActivity://Kode'>TERMS AND CONDITIONS</a>"));
        textView.setClickable(true);
        textView.setMovementMethod(LinkMovementMethod.getInstance());
    }
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <CheckBox
            android:id="@+id/checkBox1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="CheckBox" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView"
            android:clickable="true" />

    </LinearLayout>

</LinearLayout>

TCActivity.java

package id.web.freelancer.example;

import android.app.Activity;
import android.os.Bundle;

public class TCActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tc);
    }

}

tc.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

        <TextView
        android:id="@+id/tcView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Terms and conditions" />


</LinearLayout>

and the final piece of codes that glue it all, the AndroidManifest.xml:

<activity android:name="TCActivity">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="id.web.freelancer.example.TCActivity" />  
    </intent-filter>            
</activity>

Here comes, the explanations:

textView.setText(Html.fromHtml("I have read and agree to the " +
                     "<a href='id.web.freelancer.example.TCActivity://Kode'>TERMS AND CONDITIONS</a>"));
textView.setClickable(true);
textView.setMovementMethod(LinkMovementMethod.getInstance());

setClickable will allow you to click on textView. But not the HREF link. To do that, you will have to use setMovementMethod() and set it to LinkMovementMethod.

After that, you need to catch the URL. I did this using intent-filter in AndroidManifest.xml

<action android:name="android.intent.action.VIEW" />
<data android:scheme="id.web.freelancer.example.TCActivity" />  

It catch VIEW command and it only filter URL starting with id.web.freelancer.example.TCActivity://

Here's the package for you to try it out and here's the github repository. Hope this helped

Dovetailed answered 18/11, 2011 at 16:26 Comment(10)
Nice solution @silent! :) One always learn something new in here. I think my answer is still much simpler, easier to maintain, and the developer has more control over the result. I wonder, which one is more efficient space/performance wise? Thoughts? Still, well done.Cthrine
I don't really know. Never try to see which one is optimized :D. Though, my solution will make it easy on the layout side. Only one TextView.Dovetailed
Works great and looks smooth. Thanks again.Brotherton
Sorry, but for real elegance you can't go past @Gopinath's three-line solution below - it uses just one control and does all that is required.Nitz
@mike: On the inside, it does the same 3 lines solution. However, my solution doesn't open webpage. It opened another Activity.Dovetailed
add android:gravity="top" to get the checkbox aligned with text.Vambrace
Multiline text gets clipped at the bottom unless you add android:gravity="fill". Also consider adding style="?android:checkboxStyle".Sylvanite
What is ://Kode ?Pryor
You will get warning Scheme matching is case sensitive and should only use lower-case characters when you pass Capital letters in <data>. You can use id.web.freelancer.example.termsactivity and pass same to same in <a href=''>Pryor
I can't use a separate TextView because there is a big spacing between CheckBox and TextView #59580195Clotho
S
12

Kotlin version (through an extension) of Daniel Schuler's answer :

fun CheckBox.addClickableLink(fullText: String, linkText: SpannableString, callback: () -> Unit) {
    val clickableSpan = object : ClickableSpan() {
        override fun onClick(widget: View) {
            widget.cancelPendingInputEvents() // Prevent CheckBox state from being toggled when link is clicked
            callback.invoke()
        }

        override fun updateDrawState(ds: TextPaint) {
            super.updateDrawState(ds)
            ds.isUnderlineText = true // Show links with underlines
        }
    }
    linkText.setSpan(clickableSpan, 0, linkText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
    val fullTextWithTemplate = fullText.replace(linkText.toString(), "^1", false)
    val cs = TextUtils.expandTemplate(fullTextWithTemplate, linkText)

    text = cs
    movementMethod = LinkMovementMethod.getInstance() // Make link clickable
}

Usage :

yourCheckBox.addClickableLink(
    fullText = "This link must be clickable",
    linkText = SpannableString("This link")
) {
    // Do whatever you want when onClick()
}
Streetcar answered 13/6, 2019 at 9:33 Comment(2)
Man. This is fantastic Extension!Leveridge
I tried this whole and it makes the whole checkbox clickable and ignores the spannable stuff as wellZionism
T
6

I had the same problem and wanted to have more than one clickable links in the text of a checkbox without loosing the ability to click anywhere in the text (where there is no URL) to select/deselect the checkbox.

The difference to the other answers to this question is that with this solution you can have multiple clickable links in the checkbox text and those links don't have to be at the end of the text.

The layout looks similar to the one in ariefbayu's answer:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="16dp">

    <CheckBox
        android:id="@+id/tosCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:checked="false" />

    <TextView
        android:id="@+id/tosTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/tosCheckBox"
        android:layout_centerVertical="true"
        android:clickable="true" />

</RelativeLayout>

I now set the text programmatically. The text I want to display is:

"I have read and accepted the <a href='https://www.anyurl.com/privacy'>privacy statement</a> and <a href='https://www.anyurl.com/tos'>terms of service.</a>"

As it contains HTML, I first convert it to a Spanned. To make the links clickable, I additionally set the movement method of the TextView to LinkMovementMethod:

mTosTextView = (TextView) findViewById(R.id.tosTextView);
mTosTextView.setText(Html.fromHtml(getString(R.string.TOSInfo)));
mTosTextView.setMovementMethod(LinkMovementMethod.getInstance());

And here comes the more tricky part. So far, the CheckBox does not get selected when pressing the TextView. To achive this, I added a touch handler to the TextView:

mTosCheckBox = (CheckBox) findViewById(R.id.tosCheckBox);
mTosTextView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        CharSequence text = mTosTextView.getText();

        // find out which character was touched
        int offset = getOffsetForPosition(mTosTextView, event.getX(), event.getY());

        // check if this character contains a URL
        URLSpan[] types = ((Spanned)text).getSpans(offset, offset, URLSpan.class);

        if (types.length > 0) {
            // a link was clicked, so don't handle the event
            Log.d("Some tag", "link clicked: " + types[0].getURL());
            return false;
        }

        // no link was touched, so handle the touch to change 
        // the pressed state of the CheckBox
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mTosCheckBox.setPressed(true);
                break;

            case MotionEvent.ACTION_UP:
                mTosCheckBox.setChecked(!mTosCheckBox.isChecked());
                mTosCheckBox.setPressed(false);
                break;

            default:
                mTosCheckBox.setPressed(false);
                break;
        }
        return true;
    }
});

Finally, as you probably noticed, there is no method getOffsetForPosition(...) yet. If you're targeting API level 14+, you can simply use getOffsetForPosition(), as pointed out by Dheeraj V.S.. As I target API level 8+, I used an implementation that I found here: Determining which word is clicked in an android textview.

public int getOffsetForPosition(TextView textView, float x, float y) {
    if (textView.getLayout() == null) {
        return -1;
    }
    final int line = getLineAtCoordinate(textView, y);
    final int offset = getOffsetAtCoordinate(textView, line, x);
    return offset;
}

private int getOffsetAtCoordinate(TextView textView2, int line, float x) {
    x = convertToLocalHorizontalCoordinate(textView2, x);
    return textView2.getLayout().getOffsetForHorizontal(line, x);
}

private float convertToLocalHorizontalCoordinate(TextView textView2, float x) {
    x -= textView2.getTotalPaddingLeft();
    // Clamp the position to inside of the view.
    x = Math.max(0.0f, x);
    x = Math.min(textView2.getWidth() - textView2.getTotalPaddingRight() - 1, x);
    x += textView2.getScrollX();
    return x;
}

private int getLineAtCoordinate(TextView textView2, float y) {
    y -= textView2.getTotalPaddingTop();
    // Clamp the position to inside of the view.
    y = Math.max(0.0f, y);
    y = Math.min(textView2.getHeight() - textView2.getTotalPaddingBottom() - 1, y);
    y += textView2.getScrollY();
    return textView2.getLayout().getLineForVertical((int) y);
}
Th answered 17/7, 2013 at 9:13 Comment(0)
D
5

Requirements:

only part of the text to be a clickable link, while the rest of the Checkbox behaves as usual:

  1. Prevent CheckBox state from being toggled when link is clicked
  2. Remove ripple effect from CheckBox

Here is the Kotlin version:

interface HtmlAnchorClickListener {
    fun onHyperLinkClicked(name: String)
}

fun addClickableSpan(linkableTextView: TextView?, htmlString: String, listener: HtmlAnchorClickListener) {
    linkableTextView?.let {
        val sequence = HtmlCompat.fromHtml(htmlString, HtmlCompat.FROM_HTML_MODE_LEGACY)
        Log.d("addClickableSpan", "sequence = $sequence")
        val spannableString = SpannableStringBuilder(sequence)
        val urls = spannableString.getSpans(0, sequence.length, URLSpan::class.java)
        urls.forEach { span ->
            with(spannableString) {
                val start = getSpanStart(span)
                val end = getSpanEnd(span)
                val flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                val linkColor = linkableTextView.context.getColor(R.color.light_blue)

                val clickable = object : ClickableSpan() {

                    override fun onClick(view: View) {
                        // Prevent CheckBox state from being toggled when link is clicked
                        linkableTextView.cancelPendingInputEvents()
                        removeRippleEffectFromCheckBox(linkableTextView)
                        listener.onHyperLinkClicked(span.url)
                    }

                    override fun updateDrawState(textPaint: TextPaint) {
                        textPaint.color = linkColor
                        textPaint.isUnderlineText = true
                    }
                }
                setSpan(clickable, start, end, flags)
                setSpan(ForegroundColorSpan(linkColor), start, end, flags)
                removeSpan(span)
            }

            with(it) {
                text = spannableString
                linksClickable = true
                movementMethod = LinkMovementMethod.getInstance()
            }
        }
    }
}

fun removeRippleEffectFromCheckBox(textView: TextView) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        var drawable = textView.background
        if (drawable is RippleDrawable) {
            drawable = drawable.findDrawableByLayerId(0)
            textView.background = drawable
        }
    }
}

Usage:

private fun setUpTermsOfUseHyperLink() {
    val checkBoxText =
        "I agree to all the <a href='http://www.redbus.in/mob/mTerms.aspx' > Terms and Conditions</a>"

    addClickableSpan(cbAccept, checkBoxText, object : HtmlAnchorClickListener {
        override fun onHyperLinkClicked(name: String) {
            Toast.makeText(context!!, name, Toast.LENGTH_LONG).show()
        }
    })
}
Dumpcart answered 20/3, 2020 at 11:7 Comment(1)
This is inaccessible. Talkback users won't be able to toggle the checkbox, the link will open each time instead. Note, I haven't checked the other solutions yet. I suspect this problem may apply to them too.Igor
C
3

Create a CheckBox with no text and add two TextViews next to it. The first is a non-clickable view with text like "I have read and agree to the ". The second is a clickable view with text like "TERMS AND CONDITIONS". Place the TextViews side by side without any margin. Notice the extra space in the end of the first view for natural text alignment. This way you could style both texts as you like.

Sample xml code:

<RelativeLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <CheckBox
        android:id="@+id/terms_check"
        android:text=""
        android:layout_marginLeft="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/terms_text"
        android:layout_toRightOf="@id/terms_check"
        android:text="I have read and agree to the "
        android:layout_marginLeft="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/terms_link"
        android:layout_toRightOf="@id/terms_text"
        android:text="TERMS AND CONDITIONS"
        android:textColor="#00f"
        android:onClick="onClick"
        android:clickable="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>


Then add an onClick() handler in the code. Voilá.

public class SignUpActivity extends Activity {

    public void onClick(View v) {
        ...
    }  
}
Cthrine answered 18/11, 2011 at 15:22 Comment(10)
That's a good idea. But ideally only a portion of the adjacent text should be clickable. Ex: I have read and agree to the TERMS AND CONDITIONS".Brotherton
Howabout adding two TextViews side by side: non-clicable "I have read and agree to the " and right next to it a clicakble "TERMS AND CONDITIONS". Notice the extra space in the end of the first view for natural text alignment. This way you could style both texts as you like.Cthrine
Thanks Jarno, any idea on how I would set up my XML to do this?Brotherton
Just updated it :) Though I don't have a compiler next to me so I cannot verify but should be okay.Cthrine
Thanks for the sample code. It's a great workaround and definitely does what I want it to do, but the alignment just doesn't look right. I guess I'll fiddle with the XML and see what I can do. I'm assuming there's no way to insert a link into the CheckBox's text? Thanks again.Brotherton
Yeah, you need to deal with the margins yourself. That's pretty app specific anyway.Cthrine
Well, you could inherit your own class from CheckBox and try that way OR download Android sources and modify CheckBox class directly. I'd say both would take way too much time though :)Cthrine
Yeah, for such a simple task that would be overkill.Brotherton
@PaulG, posted an elegant solution for you.Dovetailed
This is a nightmare for i18n, though.Hygrometry
G
1

I didn't like the solution with the checkBox + textView as your custom view will extend a ViewGroup and not CheckBox thus forcing you to wrap CheckBox behavior.

It was important to me that the custom CheckBox can be used in xml exactly like a regular one.

The acceptable behavior for me was that this CheckBox will only be toggled when you press on it's box and not on it's text.

So I've extended CheckBox, and in order to achieve this behavior I've played with the whole touch mechanism, the full code is below, and an explanation right after it for anyone who like to know how it works.

public class CheckBoxWithLinks extends CheckBox {


public CheckBoxWithLinks(Context context) {
    super(context);
}

public CheckBoxWithLinks(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CheckBoxWithLinks(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
public boolean performClick() {
    if ( !onTextClick)
        return super.performClick();
    return false;
}

private boolean onTextClick = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
    onTextClick = !isLeftDrawableClick(event) && !isRightDrawableClick(event);
    return super.onTouchEvent(event);
}

private boolean isRightDrawableClick(MotionEvent event) {
    return event.getX() >= getRight() - getTotalPaddingRight();
}

private boolean isLeftDrawableClick(MotionEvent event) {
    return event.getX() <= getTotalPaddingLeft();
}
}

it relays on the fact that performClick method is call internally by the TextView mechanism that CheckBox extends, the ClickableSpan is also called by the TextView Mechanism. so what happens is that when you touch your CheckBox's text it will call both.

So What I've done is detect if the click was in the text area, if so we will disable the perfomClick thus disabling the toggle. but the clickable span will still be called.

Usage:

You still need to add a clickable span and setMovementMethod as before, just like a regular TextView.

Glynn answered 19/6, 2016 at 10:8 Comment(0)
G
0

If you look for a solution with the URL, i suggest you to use follow solution. With CheckBox and TextView.

    final TextView tvTerms = (TextView)findViewById(R.id.tvTerms);

    Pattern pattern = Pattern.compile(getString(R.string.terms_and_conds));
    TransformFilter transFilter = new TransformFilter() {
    @Override
    public String transformUrl(Matcher match, String url) {
        return "";
    }}; 
    Linkify.addLinks(tvTerms, pattern, Constants.URL_TERMS_AND_CONDS, null, transFilter);

where URL_TERMS_AND_CONDS = "yourUrl.com"; and R.string.terms_and_conds = id to the resource with the clickable string.

Gilemette answered 5/2, 2013 at 15:27 Comment(0)
S
0

Here is a simple code snippet to make checkbox spannable string clickable in kotlin:

        val myText = ... // your string.
        val spannableStr = SpannableString(myText)

        val clickableText1 = object : ClickableSpan() {
            override fun onClick(widget: View) {
                widget.cancelPendingInputEvents()
                doMyWorkHere()
            }

            override fun updateDrawState(text: TextPaint) {
                super.updateDrawState(text)
                text.color = Color.RED
                text.isUnderlineText = true
            }
        }

        val clickableText2 = object : ClickableSpan() {
            override fun onClick(widget: View) {
                widget.cancelPendingInputEvents()
                doMySecondWork()
            }

            override fun updateDrawState(textPaint: TextPaint) {
                super.updateDrawState(textPaint)
                textPaint.color = COLOR.BLUE
                textPaint.isUnderlineText = false
            }
        }

        spannableStr1.setSpan(clickableText1, 10, 20, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        spannableStr2.setSpan(clickableText2, 30, 40, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

        myCheckBox.text = spannableStr
        myCheckBox.movementMethod = LinkMovementMethod.getInstance()

Happy Coding :)

Sulphuryl answered 29/8, 2021 at 17:9 Comment(0)
L
0

Please try this one

String checkBoxText = "I agree to the Yunolearning <a href='https://blog.google/intl/en-in/' > Blogs</a> and <a href='https://about.google/stories/' > Stories</a>";

MaterialCheckBox singleCheckbox = new MaterialCheckBox(this);
singleCheckbox.setTag(formField.getName());
singleCheckbox.setText(Html.fromHtml(checkBoxText));
singleCheckbox.setMovementMethod(LinkMovementMethod.getInstance());
Litigation answered 6/1, 2022 at 9:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.