如何使Checkbox文本的一部分可单击?

Pau*_*ulG 42 checkbox android hyperlink

我正在尝试在文本框的相邻文本中创建一个链接.但是,此链接不是URL,而应充当按钮,以便我可以在onItemClick事件中执行一些任务.我基本上将此连接到显示我们的最终用户许可协议(硬编码)的视图.

我怎么能做到这一点?

提前致谢.

Gop*_*ath 46

以下代码在KitKat上为我工作.我还没有测试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());
Run Code Online (Sandbox Code Playgroud)

  • 我有一个错误:按下复选框的下半部分启动URL而不是切换支票 (7认同)
  • @Xiao这是LinkMovementMethod的问题,你可以用这个来解决它:https://github.com/saket/Better-Link-Movement-Method (3认同)
  • @Gopinath是的,我可以确认这可以一直追溯到Android 2.2.2(Froyo,三星GT-7510)。我喜欢这个,解决方案是如此简单而优雅,它确实是(如X.X_Mass_Developer所说的)完美! (2认同)

ari*_*ayu 32

实际上有一个优雅的解决方案,使用CheckBox和单一TextView.与TextView.setClickable()Intent Filter和TextView.setMovementMethod().的组合一起使用.

你有主视图(在这里,我称之为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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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);
    }

}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

以及将它全部粘合在一起的最后一段代码,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>
Run Code Online (Sandbox Code Playgroud)

来了,解释:

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());
Run Code Online (Sandbox Code Playgroud)

setClickable将允许您点击textView.但不是HREF链接.为此,您必须使用setMovementMethod()并将其设置为LinkMovementMethod.

之后,您需要捕获URL.我intent-filterAndroidManifest.xml中使用了它

<action android:name="android.intent.action.VIEW" />
<data android:scheme="id.web.freelancer.example.TCActivity" />  
Run Code Online (Sandbox Code Playgroud)

它捕获VIEW命令,它只过滤以URL开头的URL id.web.freelancer.example.TCActivity://

这是你试用它的包,这里是github存储库.希望这有帮助


Dan*_*ler 29

您可能只希望文本的一部分是可点击的链接,而复选框的其余部分表现得像往常一样,即您可以单击其他文本来切换状态.

您可以设置您的复选框,如下所示:

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());
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案.widget.cancelPendingInputEvents()正在做魔术! (8认同)
  • 工作正常,但即使单击ClickableSpan,也会为CheckBox drawable选择按下的声明,即使它没有切换.有关如何避免这种情况的任何建议? (2认同)

Phi*_*hil 7

Daniel Schuler 的回答的Kotlin 版本(通过扩展

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
}
Run Code Online (Sandbox Code Playgroud)

用法 :

yourCheckBox.addClickableLink(
    fullText = "This link must be clickable",
    linkText = SpannableString("This link")
) {
    // Do whatever you want when onClick()
}
Run Code Online (Sandbox Code Playgroud)

  • 我尝试了整个,它使整个复选框可点击,并且也忽略了可跨越的东西 (2认同)

Flo*_*Flo 5

我遇到了同样的问题,希望在复选框的文本中有多个可点击的链接,而不会失去单击文本中任何位置(没有URL)的选项/取消选中复选框的功能.

与此问题的其他答案的不同之处在于,使用此解决方案,您可以在复选框文本中包含多个可单击链接,并且这些链接不必位于文本的末尾.

布局看起来类似于ariefbayu的回答:

<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>
Run Code Online (Sandbox Code Playgroud)

我现在以编程方式设置文本.我要显示的文字是:

"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>"
Run Code Online (Sandbox Code Playgroud)

由于它包含HTML,我首先将其转换为Spanned.为了使链接可单击,我还将TextView的移动方法设置为LinkMovementMethod:

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

这里有更棘手的部分.到目前为止,按TabView时没有选中CheckBox.为了实现这一点,我在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;
    }
});
Run Code Online (Sandbox Code Playgroud)

最后,正如您可能已经注意到的那样,还没有方法getOffsetForPosition(...).如果你的目标是API级别14+,你可以简单地使用getOffsetForPosition(),正如Dheeraj VS所指出的那样.当我针对API级别8+时,我使用了我在此处找到的实现:确定在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);
}
Run Code Online (Sandbox Code Playgroud)


Fra*_*con 5

要求:

只有部分文本是可点击的链接,而复选框的其余部分则表现正常:

  1. 单击链接时防止切换复选框状态
  2. 删除 CheckBox 的涟漪效应

这是Kotlin版本:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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()
        }
    })
}
Run Code Online (Sandbox Code Playgroud)