我有一段文字:“Android 是一个软件堆栈”。在这段文字中,我想将“堆栈”设置为可点击的文本。因此,如果您单击该文本,它将会重定向到一个新活动(不是浏览器中打开)。
我尝试了但是没有得到解决方案。
对于那些正在寻找 Kotlin 解决方案的人,以下是对我有用的内容:
private fun setupTermsAndConditions() {
val termsAndConditions = resources.getString(R.string.terms_and_conditions)
val spannableString = SpannableString(termsAndConditions)
val clickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
if (checkForWifiAndMobileInternet()) {
// binding.viewModel!!.openTermsAndConditions()
showToast("Good, open the link!!!")
} else {
showToast("Cannot open this file because of internet connection!")
}
}
override fun updateDrawState(textPaint : TextPaint) {
super.updateDrawState(textPaint)
textPaint.color = resources.getColor(R.color.colorGrey)
textPaint.isFakeBoldText = true
}
}
spannableString.setSpan(clickableSpan, 34, 86, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
binding.tvTermsAndConditions.text = spannableString
binding.tvTermsAndConditions.movementMethod = LinkMovementMethod.getInstance()
binding.tvTermsAndConditions.setHighlightColor(Color.TRANSPARENT);
}
对于 Kotlin,请使用此扩展
fun TextView.makeLinks(vararg links: Pair<String, View.OnClickListener>) {
val spannableString = SpannableString(this.text)
for (link in links) {
val clickableSpan = object : ClickableSpan() {
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
)
spannableString.setSpan(
ForegroundColorSpan(Color.parseColor("#46C2CC")),
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)
}
像这样调用
binding.agreeText.makeLinks(Pair(getString(R.string.terms_conditionsClick),View.OnClickListener {
startActivity(TermsAndConditionActivity.getIntent(this))
}))
嘭!献给Java爱好者的内容 :D 我们可以根据自己的需求进行修改:
List<Pair<String, View.OnClickListener>> pairsList = new ArrayList<>();
pairsList.add(new Pair<>("38,50", v -> {
Intent intent = new Intent(SignUpActivity.this, WebActivity.class);
intent.putExtra("which", "tos");
startActivity(intent);
}));
pairsList.add(new Pair<>("81,95", v -> {
Intent intent = new Intent(SignUpActivity.this, WebActivity.class);
intent.putExtra("which", "policy");
startActivity(intent);
}));
makeLinks(pairsList); // Method calling
private void makeLinks(List<Pair<String, View.OnClickListener>> pairsList) {
SpannableString ss = new SpannableString(By signing up, I’m agree to PAKRISM’s Terms of Use and confirms that I have read Privacy Policy);
for (Pair<String, View.OnClickListener> pair : pairsList) {
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View textView) {
//Toast.makeText(MyApplication.getAppContext(), "Clicked!", Toast.LENGTH_SHORT).show();
pair.second.onClick(textView);
}
@Override
public void updateDrawState(TextPaint ds) {
ds.linkColor = ContextCompat.getColor(SignUpActivity.this, R.color.primary_main);
ds.setUnderlineText(true);
super.updateDrawState(ds);
}
};
String[] indexes = pair.first.split(",");
ss.setSpan(clickableSpan, Integer.parseInt(indexes[0]), Integer.parseInt(indexes[1]), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
TextView tv = findViewById(R.id.txtView);
tv.setText(ss);
tv.setMovementMethod(LinkMovementMethod.getInstance());
}
使用扩展方法以优雅的 Kotlin
方式创建:
fun TextView.setClickableText(text: Spanned,
clickableText: String,
@ColorInt clickableColor: Int,
clickListener: () -> Unit) {
val spannableString = SpannableString(text)
val startingPosition: Int = text.indexOf(clickableText)
if (startingPosition > -1) {
val clickableSpan: ClickableSpan = object : ClickableSpan() {
override fun onClick(textView: View) {
clickListener()
}
override fun updateDrawState(textPaint: TextPaint) {
super.updateDrawState(textPaint)
textPaint.isUnderlineText = false
}
}
val endingPosition: Int = startingPosition + clickableText.length
spannableString.setSpan(clickableSpan, startingPosition,
endingPosition, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannableString.setSpan(ForegroundColorSpan(clickableColor), startingPosition,
endingPosition, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT
}
setText(spannableString)
}
val spans: Array<URLSpan> = result.getSpans(0, result.length, URLSpan::class.java)
fun TextView.createClickable(string: String, action:(String)->Unit ) {
text = HtmlCompat.fromHtml(string, HtmlCompat.FROM_HTML_MODE_LEGACY)
val result = SpannableString(text)
val spans = result.getSpans(0, result.length, URLSpan::class.java)
for (span in spans) {
val link:Pair<String, View.OnClickListener> = Pair(span.url, View.OnClickListener {
action(span.url)
})
val start = result.getSpanStart(span)
val end = result.getSpanEnd(span)
val flags = result.getSpanFlags(span)
result.removeSpan(span)
val clickableSpan: ClickableSpan = object : ClickableSpan() {
override fun onClick(textView: View) {
textView.invalidate()
link.second.onClick(textView)
}
override fun updateDrawState(textPaint: TextPaint) {
super.updateDrawState(textPaint)
textPaint.isUnderlineText = false
}
}
result.setSpan(clickableSpan, start, end, flags)
this.movementMethod = LinkMovementMethod.getInstance()
this.setText(result, TextView.BufferType.SPANNABLE)
}
}
示例文本: Android是一个软件堆栈,非常棒
将可点击的文本包装在锚点标签内
例如:Android是一个软件<a href='https://example.com/stack'>堆栈</a>,非常<a href='https://example.com/awesome'>棒</a>。
val str = "Android is a Software <a href='https://example.com/stack'> Stack </a> and it' <a href='https://example.com/awesome'> Awesome </a>."
textView.createClickable(str) {
when(it) {
"https://example.com/stack"->{
startActivity(Intent(this,StackActivity::class.java))
}
"https://example.com/awesom"->{
startActivity(Intent(this,AwesomeActivity::class.java))
}
}
}
这里有一个Kotlin解决方案,更适合本地化:
data class LinkedText(@StringRes val textRes: Int, val clickListener: View.OnClickListener? = null)
fun TextView.setPartiallyLinkedText(vararg texts: LinkedText) {
this.text = texts.joinToString(" ") { context.getString(it.textRes) }
val spannableString = SpannableString(this.text)
var startIndexOfLink = -1
texts.forEach { text ->
val string = context.getString(text.textRes)
if (text.clickListener != null) {
val clickableSpan = object : ClickableSpan() {
override fun updateDrawState(textPaint: TextPaint) {
textPaint.color = textPaint.linkColor
textPaint.isUnderlineText = true
}
override fun onClick(view: View) {
Selection.setSelection((view as TextView).text as Spannable, 0)
view.invalidate()
text.clickListener.onClick(view)
}
}
startIndexOfLink = this.text.toString().indexOf(string, startIndexOfLink + 1)
spannableString.setSpan(
clickableSpan, startIndexOfLink, startIndexOfLink + string.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
this.movementMethod = LinkMovementMethod.getInstance()
this.setText(spannableString, TextView.BufferType.SPANNABLE)
}
并像这样使用它:
textView.setPartiallyLinkedText(
LinkedText(R.string.not_linked_text),
LinkedText(R.string.linked_text) {
Toast.makeText(context, "You clicked", Toast.LENGTH_LONG).show()
},
)
textView.makeTextClickable(
contentText = "Hello world",
clickableText = "world",
isBoldText = true,
onClick = {
openPageInBrowser(BuildConfig.PRIVACY_POLICY_URL)
}
)
internal fun TextView.makeTextClickable(
contentText: String,
clickableText: String,
onClick: () -> Unit,
isUnderlineText: Boolean = false,
isBoldText: Boolean = false
) {
val spannableString = SpannableString(contentText)
val clickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
onClick()
}
override fun updateDrawState(ds: TextPaint) {
super.updateDrawState(ds)
ds.isUnderlineText = isUnderlineText
ds.isFakeBoldText = isBoldText
}
}
val clickableTextStartIndex = text.indexOf(clickableText)
val clickableTextEndIndex = clickableTextStartIndex + clickableText.length
spannableString.setSpan(
clickableSpan,
clickableTextStartIndex,
clickableTextEndIndex,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
this.text = spannableString
this.movementMethod = LinkMovementMethod.getInstance()
}
加粗文字:
mySpannable.setSpan(new StyleSpan(Typeface.BOLD),termStart,termStop,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
val completeText = getString(R.string.terms_description)
val textToFind = getString(R.string.show_terms)
val spannableString: Spannable = SpannableString(completeText)
val startFocus = completeText.indexOf(textToFind)
val endFocus = startFocus + textToFind.length
spannableString.setSpan(object: ClickableSpan() {
override fun onClick(p0: View) {
showMessage()
}
}, startFocus, endFocus, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
show_terms.text = spannableString
show_terms.movementMethod = LinkMovementMethod.getInstance();
show_terms.highlightColor = Color.TRANSPARENT;
这是XML
<CheckBox
android:id="@+id/check_agree_terms"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/show_terms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColorLink="@color/colorPrimary"
android:layout_toEndOf="@id/check_agree_terms"/>
这是它的外观
Java解决方案(2022年更新)
特点:
我在daler445的代码基础上进行了改进,以允许为重复单词添加多个可点击命令。
在Java类中:
public class MainActivity extends AppCompatActivity {
SharedPreferences sp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sp = getSharedPreferences("MyUserPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
TextView fulltext = (TextView) findViewById(R.id.fulltext);
//replace setText("") to setText("Android is a Software stack") for his case
fulltext.setText("Search [1] this full [1] text with repeated strings. Search [2] Search [3] full [2] full [3]");
List<Pair<String, View.OnClickListener>> links = new ArrayList<>();
//replace "Search" with "stack" for his case
String stringtohyperlink = "Search";
String stringtohyperlink2 = "full";
links.add(new Pair<>(stringtohyperlink, new View.OnClickListener() {
@Override
public void onClick(View v) {
String position = sp.getString("position","0");
if (position.equals("1")) {
Toast.makeText(MainActivity.this, "Search 1 has been Clicked.", Toast.LENGTH_SHORT).show();
editor.putString("position","0");
editor.apply();
}
if (position.equals("2")) {
Toast.makeText(MainActivity.this, "Search 2 has been Clicked.", Toast.LENGTH_SHORT).show();
editor.putString("position","0");
editor.apply();
}
if (position.equals("3")) {
Toast.makeText(MainActivity.this, "Search 3 has been Clicked.", Toast.LENGTH_SHORT).show();
editor.putString("position","0");
editor.apply();
}
}
}));
links.add(new Pair<>(stringtohyperlink2, new View.OnClickListener() {
@Override
public void onClick(View v) {
String position = sp.getString("position","0");
if (position.equals("1")) {
Toast.makeText(MainActivity.this, "full 1 has been Clicked.", Toast.LENGTH_SHORT).show();
editor.putString("position","0");
editor.apply();
}
if (position.equals("2")) {
Toast.makeText(MainActivity.this, "full 2 has been Clicked.", Toast.LENGTH_SHORT).show();
editor.putString("position","0");
editor.apply();
}
if (position.equals("3")) {
Toast.makeText(MainActivity.this, "full 3 has been Clicked.", Toast.LENGTH_SHORT).show();
editor.putString("position","0");
editor.apply();
}
}
}));
makeLinks(fulltext, links);
}
public void makeLinks(TextView textView, List<Pair<String, View.OnClickListener>> links) {
SpannableString spannableString = new SpannableString(textView.getText().toString());
int startIndexState = -1;
SharedPreferences.Editor editor = sp.edit();
for (Pair<String, View.OnClickListener> link : links) {
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
editor.putString("position","1");
editor.apply();
widget.invalidate();
assert link.second != null;
link.second.onClick(widget);
}
};
ClickableSpan clickableSpan2 = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
editor.putString("position","2");
editor.apply();
widget.invalidate();
assert link.second != null;
link.second.onClick(widget);
}
};
ClickableSpan clickableSpan3 = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
editor.putString("position","3");
editor.apply();
widget.invalidate();
assert link.second != null;
link.second.onClick(widget);
}
};
//... This only allows for 3 repeated words
//... Add more of it, if there are more repeated words.
assert link.first != null;
int startIndexOfLink = textView.getText().toString().indexOf(link.first, startIndexState + 1);
spannableString.setSpan(clickableSpan, startIndexOfLink, startIndexOfLink + link.first.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
int startIndexOfLink2 = textView.getText().toString().indexOf(link.first, startIndexOfLink + 1);
if (startIndexOfLink2 != -1) {
spannableString.setSpan(clickableSpan2, startIndexOfLink2, startIndexOfLink2 + link.first.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
}
int startIndexOfLink3 = textView.getText().toString().indexOf(link.first, startIndexOfLink2 + 1);
if (startIndexOfLink3 != -1) {
spannableString.setSpan(clickableSpan3, startIndexOfLink3, startIndexOfLink3 + link.first.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
}
//... This only allows for 3 repeated words
//... Add more of it, if there are more repeated words.
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setText(spannableString, TextView.BufferType.SPANNABLE);
}
}
}
At .xml
<TextView
android:id="@+id/fulltext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
演示