我想在文本框旁边的文本中创建一个链接。该链接不是URL,而是应该充当按钮,以便我可以在onItemClick事件中执行一些任务。我基本上将其连接到显示我们最终用户许可协议(硬编码)的视图。
我怎样才能做到这一点?
提前致谢。
你可能只想让文本的一部分成为可点击的链接,而复选框的其他部分则像往常一样工作,即你可以单击其他文本来切换状态。
你可以这样设置你的复选框:
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());
以下的代码在我的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());
实际上有一种优雅的解决方案,使用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());
}
}
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>
最后一个将所有代码拼合起来的部分是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>
以下是解释:
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
函数可以让你点击textView
,但不能点击HREF链接。要实现这个功能,你需要使用setMovementMethod()
函数,并将其设置为LinkMovementMethod
。
之后,你需要捕获URL。我使用AndroidManifest.xml中的intent-filter
来实现这一点。
<action android:name="android.intent.action.VIEW" />
<data android:scheme="id.web.freelancer.example.TCActivity" />
它捕获 VIEW 命令,仅过滤以
id.web.freelancer.example.TCActivity://
开头的 URL。
这里提供了一个包供您尝试,这是 github 存储库。希望这可以帮到您。
android:gravity="fill"
。同时考虑添加 style="?android:checkboxStyle"
。 - gladed<data>
中传递大写字母时,您将收到警告“方案匹配区分大小写,应仅使用小写字符”。您可以使用id.web.freelancer.example.termsactivity
并将其传递给<a href=''>
中的相同内容。 - Pratik Butani使用 Kotlin (通过扩展函数)翻译自 Daniel Schuler 的回答:
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
}
使用方法:
yourCheckBox.addClickableLink(
fullText = "This link must be clickable",
linkText = SpannableString("This link")
) {
// Do whatever you want when onClick()
}
<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 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>"
由于它包含HTML,我首先将其转换为Spanned。为了使链接可点击,我还将TextView的movement method设置为LinkMovementMethod:
mTosTextView = (TextView) findViewById(R.id.tosTextView);
mTosTextView.setText(Html.fromHtml(getString(R.string.TOSInfo)));
mTosTextView.setMovementMethod(LinkMovementMethod.getInstance());
接下来是比较棘手的部分。目前,当按下TextView时,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;
}
});
最后,您可能已经注意到,还没有getOffsetForPosition(...)
方法。 如果您的目标是API级别14+,则可以简单地使用getOffsetForPosition()
,正如Dheeraj V.S.指出的那样。。由于我的目标是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);
}
要求:
只有部分文本作为可点击链接,而复选框的其余部分则像通常一样工作:
这是 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
}
}
}
使用方法:
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()
}
})
}
CheckBox
,并在其旁边添加两个 TextView
。第一个视图是不可点击的,并带有像“我已阅读并同意”这样的文本。第二个是可点击的,并带有像“条款和条件”这样的文本。将这两个 TextView
并排放置,不要留有任何间距。请注意第一个视图末尾的额外空间,以实现自然文本对齐。这样你就可以根据自己的喜好来设计这两个文本了。<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>
onClick()
处理程序。Voilá。public class SignUpActivity extends Activity {
public void onClick(View v) {
...
}
}
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();
}
}
这里是一个简单的代码片段,用于在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()
编程愉快:)
请试试这个
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());
checkBox.apply { background = (background as? RippleDrawable)?.findDrawableByLayerId(0) }
- Hampel Előd