텍스트 보기의 부분을 설정하는 방법은 클릭할 수 있습니다.
"안드로이드는 소프트웨어 스택입니다"라는 텍스트가 있습니다.이 텍스트에서는 "스택" 텍스트를 클릭 가능으로 설정합니다.따라서 해당 항목을 클릭하면 브라우저가 아닌 새 작업으로 리디렉션됩니다.
노력했지만 해결이 안 돼요.
android.text.style.ClickableSpan
문제를 해결할 수 있습니다.
SpannableString ss = new SpannableString("Android is a Software stack");
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View textView) {
startActivity(new Intent(MyActivity.this, NextActivity.class));
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
}
};
ss.setSpan(clickableSpan, 22, 27, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView textView = (TextView) findViewById(R.id.hello);
textView.setText(ss);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.TRANSPARENT);
XML:
<TextView
...
android:textColorLink="@drawable/your_selector"
/>
내부에 여러 링크를 만드는 기능TextView
업데이트 2020 : 이 기능을 통해 동일한 텍스트 링크를 1개 안에 여러 개 지원할 수 있게 되었습니다.TextView
단, 링크를 올바른 순서로 배치하는 것을 잊지 마십시오.
fun TextView.makeLinks(vararg links: Pair<String, View.OnClickListener>) {
val spannableString = SpannableString(this.text)
var startIndexOfLink = -1
for (link in links) {
val clickableSpan = object : ClickableSpan() {
override fun updateDrawState(textPaint: TextPaint) {
// use this to change the link color
textPaint.color = textPaint.linkColor
// toggle below value to enable/disable
// the underline shown below the clickable text
textPaint.isUnderlineText = true
}
override fun onClick(view: View) {
Selection.setSelection((view as TextView).text as Spannable, 0)
view.invalidate()
link.second.onClick(view)
}
}
startIndexOfLink = this.text.toString().indexOf(link.first, startIndexOfLink + 1)
// if(startIndexOfLink == -1) continue // todo if you want to verify your texts contains links text
spannableString.setSpan(
clickableSpan, 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)
}
사용.
my_text_view.makeLinks(
Pair("Terms of Service", View.OnClickListener {
Toast.makeText(applicationContext, "Terms of Service Clicked", Toast.LENGTH_SHORT).show()
}),
Pair("Privacy Policy", View.OnClickListener {
Toast.makeText(applicationContext, "Privacy Policy Clicked", Toast.LENGTH_SHORT).show()
}))
XML
<TextView
android:id="@+id/my_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Please accept Terms of Service and Privacy Policy"
android:textColorHighlight="#f00" // background color when pressed
android:textColorLink="#0f0"
android:textSize="20sp" />
데모
언급
링크를 클리어하기 위한 솔루션http://https://stackoverflow.com/a/19445108/5381331 를 참조해 주세요.
이 게시물에 설명된 대로 ClickableSpan을 사용할 수 있습니다.
샘플 코드:
TextView myTextView = new TextView(this);
String myString = "Some text [clickable]";
int i1 = myString.indexOf("[");
int i2 = myString.indexOf("]");
myTextView.setMovementMethod(LinkMovementMethod.getInstance());
myTextView.setText(myString, BufferType.SPANNABLE);
Spannable mySpannable = (Spannable)myTextView.getText();
ClickableSpan myClickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget) { /* do something */ }
};
mySpannable.setSpan(myClickableSpan, i1, i2 + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
String에서 시작 및 종료 위치가 필요한 경우 도우미 방법을 만들었습니다.
public static TextView createLink(TextView targetTextView, String completeString,
String partToClick, ClickableSpan clickableAction) {
SpannableString spannableString = new SpannableString(completeString);
// make sure the String is exist, if it doesn't exist
// it will throw IndexOutOfBoundException
int startPosition = completeString.indexOf(partToClick);
int endPosition = completeString.lastIndexOf(partToClick) + partToClick.length();
spannableString.setSpan(clickableAction, startPosition, endPosition,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
targetTextView.setText(spannableString);
targetTextView.setMovementMethod(LinkMovementMethod.getInstance());
return targetTextView;
}
사용법은 다음과 같습니다.
private void initSignUp() {
String completeString = "New to Reddit? Sign up here.";
String partToClick = "Sign up";
ClickableTextUtil
.createLink(signUpEditText, completeString, partToClick,
new ClickableSpan() {
@Override
public void onClick(View widget) {
// your action
Toast.makeText(activity, "Start Sign up activity",
Toast.LENGTH_SHORT).show();
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
// this is where you set link color, underline, typeface etc.
int linkColor = ContextCompat.getColor(activity, R.color.blumine);
ds.setColor(linkColor);
ds.setUnderlineText(false);
}
});
}
샘플 코드를 사용할 수 있습니다.ClickableSpan에 대한 자세한 내용을 알아보겠습니다.이 문서를 확인하십시오.
SpannableString myString = new SpannableString("This is example");
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View textView) {
ToastUtil.show(getContext(),"Clicked Smile ");
}
};
//For Click
myString.setSpan(clickableSpan,startIndex,lastIndex,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//For UnderLine
myString.setSpan(new UnderlineSpan(),startIndex,lastIndex,0);
//For Bold
myString.setSpan(new StyleSpan(Typeface.BOLD),startIndex,lastIndex,0);
//Finally you can set to textView.
TextView textView = (TextView) findViewById(R.id.txtSpan);
textView.setText(myString);
textView.setMovementMethod(LinkMovementMethod.getInstance());
여기 a가 있습니다.Kotlin
일부를 만드는 방법TextView
클릭 가능:
private fun makeTextLink(textView: TextView, str: String, underlined: Boolean, color: Int?, action: (() -> Unit)? = null) {
val spannableString = SpannableString(textView.text)
val textColor = color ?: textView.currentTextColor
val clickableSpan = object : ClickableSpan() {
override fun onClick(textView: View) {
action?.invoke()
}
override fun updateDrawState(drawState: TextPaint) {
super.updateDrawState(drawState)
drawState.isUnderlineText = underlined
drawState.color = textColor
}
}
val index = spannableString.indexOf(str)
spannableString.setSpan(clickableSpan, index, index + str.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = spannableString
textView.movementMethod = LinkMovementMethod.getInstance()
textView.highlightColor = Color.TRANSPARENT
}
TextView 내에 여러 링크를 작성하기 위해 여러 번 호출할 수 있습니다.
makeTextLink(myTextView, str, false, Color.RED, action = { Log.d("onClick", "link") })
makeTextLink(myTextView, str1, true, null, action = { Log.d("onClick", "link1") })
t= (TextView) findViewById(R.id.PP1);
t.setText(Html.fromHtml("<bThis is normal text </b>" +
"<a href=\"http://www.xyz-zyyx.com\">This is cliclable text</a> "));
t.setMovementMethod(LinkMovementMethod.getInstance());
코드를 적게 필요로 하고, 보다 「로컬라이제이션 친화적」이라고 하는 다른 어프로치를 제안합니다.
행선지 액티비티가 「ActivityStack」이라고 하는 경우는, AndroidManifest.xml 의 커스텀 스킴(예를 들면 「myappscheme」)을 사용해 액티비티의 의도 필터를 매니페스트로 정의합니다.
<activity
android:name=".ActivityStack">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="stack"/>
<data android:scheme="myappscheme" />
</intent-filter>
</activity>
특별한 태그 없이 TextView를 정의합니다('android:autoLink' 태그를 사용하지 않는 것이 중요합니다.https://stackoverflow.com/a/20647011/1699702):
<TextView
android:id="@+id/stackView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/stack_string" />
다음으로 (String.xml 내의) TextView텍스트에 있는 커스텀스킴과 호스트를 포함한 링크를 사용합니다.
<string name="stack_string">Android is a Software <a href="myappscheme://stack">stack</a></string>
setMovementMethod()를 사용하여 링크를 "활성화"합니다(액티비티의 경우 onCreate() 또는 fragment의 경우 onCreateView()).
TextView stack = findViewById(R.id.stackView);
stack.setMovementMethod(LinkMovementMethod.getInstance());
그러면 "stack" 단어를 누르면 스택 작업이 열립니다.
코틀린 판 반 린의 대답입니다.
약간의 수정이 있었음을 유의하시기 바랍니다.
fun makeLinks(textView: TextView, links: Array<String>, clickableSpans: Array<ClickableSpan>) {
val spannableString = SpannableString(textView.text)
for (i in links.indices) {
val clickableSpan = clickableSpans[i]
val link = links[i]
val startIndexOfLink = textView.text.indexOf(link)
spannableString.setSpan(clickableSpan, startIndexOfLink, startIndexOfLink + link.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
textView.movementMethod = LinkMovementMethod.getInstance()
textView.setText(spannableString, TextView.BufferType.SPANNABLE)
}
fun setupClickableTextView() {
val termsOfServicesClick = object : ClickableSpan() {
override fun onClick(p0: View?) {
Toast.makeText(applicationContext, "ToS clicked", Toast.LENGTH_SHORT).show()
}
}
val privacyPolicyClick = object : ClickableSpan() {
override fun onClick(p0: View?) {
Toast.makeText(applicationContext, "PP clicked", Toast.LENGTH_SHORT).show()
}
}
makeLinks(termsTextView, arrayOf("terms", "privacy policy"), arrayOf(termsOfServicesClick, privacyPolicyClick))
}
이 방법을 사용하여 클릭 가능한 값을 설정할 수 있습니다.
public void setClickableString(String clickableValue, String wholeValue, TextView yourTextView){
String value = wholeValue;
SpannableString spannableString = new SpannableString(value);
int startIndex = value.indexOf(clickableValue);
int endIndex = startIndex + clickableValue.length();
spannableString.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false); // <-- this will remove automatic underline in set span
}
@Override
public void onClick(View widget) {
// do what you want with clickable value
}
}, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
yourTextView.setText(spannableString);
yourTextView.setMovementMethod(LinkMovementMethod.getInstance()); // <-- important, onClick in ClickableSpan won't work without this
}
사용 방법은 다음과 같습니다.
TextView myTextView = findViewById(R.id.myTextView);
setClickableString("stack", "Android is a Software stack", myTextView);
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.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)
}
Boom Java Lovers :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.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))
}))
굵은 글씨,
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"/>
이런 식이다
URL pan 클래스를 사용하여 URL 가져오기
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 is a Software <a href='https://example.com/stack'> Stack </a> and it' <a href='https://example.com/awesome'> 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()
}
은 나의 ★★★★★★★★★★★★★★★★★★★★입니다.MovementMethod
링크/텍스트/이미지 클릭을 검출합니다.은 변형되었다.LinkMovementMethod
.
import android.text.Layout;
import android.text.NoCopySpan;
import android.text.Selection;
import android.text.Spannable;
import android.text.method.ScrollingMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.text.style.URLSpan;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
public class ClickMovementMethod extends ScrollingMovementMethod {
private Object FROM_BELOW = new NoCopySpan.Concrete();
private static final int CLICK = 1;
private static final int UP = 2;
private static final int DOWN = 3;
private Listener listener;
public void setListener(Listener listener) {
this.listener = listener;
}
@Override
public boolean canSelectArbitrarily() {
return true;
}
@Override
protected boolean handleMovementKey(TextView widget, Spannable buffer, int keyCode,
int movementMetaState, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) {
if (event.getAction() == KeyEvent.ACTION_DOWN &&
event.getRepeatCount() == 0 && action(CLICK, widget, buffer)) {
return true;
}
}
break;
}
return super.handleMovementKey(widget, buffer, keyCode, movementMetaState, event);
}
@Override
protected boolean up(TextView widget, Spannable buffer) {
if (action(UP, widget, buffer)) {
return true;
}
return super.up(widget, buffer);
}
@Override
protected boolean down(TextView widget, Spannable buffer) {
if (action(DOWN, widget, buffer)) {
return true;
}
return super.down(widget, buffer);
}
@Override
protected boolean left(TextView widget, Spannable buffer) {
if (action(UP, widget, buffer)) {
return true;
}
return super.left(widget, buffer);
}
@Override
protected boolean right(TextView widget, Spannable buffer) {
if (action(DOWN, widget, buffer)) {
return true;
}
return super.right(widget, buffer);
}
private boolean action(int what, TextView widget, Spannable buffer) {
Layout layout = widget.getLayout();
int padding = widget.getTotalPaddingTop() +
widget.getTotalPaddingBottom();
int areatop = widget.getScrollY();
int areabot = areatop + widget.getHeight() - padding;
int linetop = layout.getLineForVertical(areatop);
int linebot = layout.getLineForVertical(areabot);
int first = layout.getLineStart(linetop);
int last = layout.getLineEnd(linebot);
ClickableSpan[] candidates = buffer.getSpans(first, last, URLSpan.class);
int a = Selection.getSelectionStart(buffer);
int b = Selection.getSelectionEnd(buffer);
int selStart = Math.min(a, b);
int selEnd = Math.max(a, b);
if (selStart < 0) {
if (buffer.getSpanStart(FROM_BELOW) >= 0) {
selStart = selEnd = buffer.length();
}
}
if (selStart > last)
selStart = selEnd = Integer.MAX_VALUE;
if (selEnd < first)
selStart = selEnd = -1;
switch (what) {
case CLICK:
if (selStart == selEnd) {
return false;
}
if (listener != null) {
URLSpan[] link = buffer.getSpans(selStart, selEnd, URLSpan.class);
if (link.length >= 1) {
listener.onClick(link[0].getURL());
} else {
ImageSpan[] image = buffer.getSpans(selStart, selEnd, ImageSpan.class);
if (image.length >= 1) {
listener.onImageClicked(image[0].getSource());
} else {
listener.onTextClicked();
}
}
}
break;
case UP:
int beststart, bestend;
beststart = -1;
bestend = -1;
for (int i = 0; i < candidates.length; i++) {
int end = buffer.getSpanEnd(candidates[i]);
if (end < selEnd || selStart == selEnd) {
if (end > bestend) {
beststart = buffer.getSpanStart(candidates[i]);
bestend = end;
}
}
}
if (beststart >= 0) {
Selection.setSelection(buffer, bestend, beststart);
return true;
}
break;
case DOWN:
beststart = Integer.MAX_VALUE;
bestend = Integer.MAX_VALUE;
for (int i = 0; i < candidates.length; i++) {
int start = buffer.getSpanStart(candidates[i]);
if (start > selStart || selStart == selEnd) {
if (start < beststart) {
beststart = start;
bestend = buffer.getSpanEnd(candidates[i]);
}
}
}
if (bestend < Integer.MAX_VALUE) {
Selection.setSelection(buffer, beststart, bestend);
return true;
}
break;
}
return false;
}
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer,
MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
if (action == MotionEvent.ACTION_UP) {
if (listener != null) {
if (link.length >= 1) {
listener.onClick(link[0].getURL());
} else {
ImageSpan[] image = buffer.getSpans(off, off, ImageSpan.class);
if (image.length >= 1) {
listener.onImageClicked(image[0].getSource());
} else if (Selection.getSelectionStart(buffer) == Selection.getSelectionEnd(buffer)) {
listener.onTextClicked();
}
}
}
}
if (action == MotionEvent.ACTION_DOWN && link.length != 0) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
return true;
}
if (link.length == 0) {
Selection.removeSelection(buffer);
}
}
return super.onTouchEvent(widget, buffer, event);
}
@Override
public void initialize(TextView widget, Spannable text) {
Selection.removeSelection(text);
text.removeSpan(FROM_BELOW);
}
@Override
public void onTakeFocus(TextView view, Spannable text, int dir) {
Selection.removeSelection(text);
if ((dir & View.FOCUS_BACKWARD) != 0) {
text.setSpan(FROM_BELOW, 0, 0, Spannable.SPAN_POINT_POINT);
} else {
text.removeSpan(FROM_BELOW);
}
}
public interface Listener {
void onClick(String clicked);
void onTextClicked();
void onImageClicked(String source);
}
}
코틀린어로 된 보다 일반적인 대답
fun setClickableText(view: TextView, firstSpan: String, secondSpan: String) {
val context = view.context
val builder = SpannableStringBuilder()
val unClickableSpan = SpannableString(firstSpan)
val span = SpannableString(" "+secondSpan)
builder.append(unClickableSpan);
val clickableSpan: ClickableSpan = object : ClickableSpan() {
override fun onClick(textView: View) {
val intent = Intent(context, HomeActivity::class.java)
context.startActivity(intent)
}
override fun updateDrawState(ds: TextPaint) {
super.updateDrawState(ds)
ds.isUnderlineText = true
ds.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC));
}
}
builder.append(span);
builder.setSpan(clickableSpan, firstSpan.length, firstSpan.length+secondSpan.length+1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
view.setText(builder,TextView.BufferType.SPANNABLE)
view.setMovementMethod(LinkMovementMethod.getInstance());
}
제공된 솔루션은 꽤 괜찮은 것입니다.하지만, 저는 일반적으로 더 간단한 해결책을 사용합니다.
linkify 유틸리티 함수는 다음과 같습니다.
/**
* Method is used to Linkify words in a TextView
*
* @param textView TextView who's text you want to change
* @param textToLink The text to turn into a link
* @param url The url you want to send the user to
*/
fun linkify(textView: TextView, textToLink: String, url: String) {
val pattern = Pattern.compile(textToLink)
Linkify.addLinks(textView, pattern, url, { _, _, _ -> true })
{ _, _ -> "" }
}
이 기능을 사용하는 것은 매우 간단합니다.여기 예가 있습니다.
// terms and privacy
val tvTerms = findViewById<TextView>(R.id.tv_terms)
val tvPrivacy = findViewById<TextView>(R.id.tv_privacy)
Utils.linkify(tvTerms, resources.getString(R.string.terms),
Constants.TERMS_URL)
Utils.linkify(tvPrivacy, resources.getString(R.string.privacy),
Constants.PRIVACY_URL)
Kotlin의 복잡하지만 범용적인 솔루션
/*
* Receive Pair of Text and Action and set it clickable and appearing as link
* */
fun TextView.setClickableText(vararg textToSpanAndClickAction: Pair<String, (String) -> Unit>) {
val builder = SpannableStringBuilder(text.toString())
textToSpanAndClickAction.forEach { argPair ->
val clickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
argPair.second.invoke(argPair.first)
}
}
this.text.toString().let { fullText ->
val indexOfFirst = fullText.indexOf(argPair.first)
val indexOfLast = indexOfFirst + argPair.first.length
if (indexOfFirst < 0){
//No match found
return
}else{
builder.setSpan(
clickableSpan,
indexOfFirst,
indexOfLast,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
}
this.text = builder
this.movementMethod = LinkMovementMethod.getInstance()
}
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);
}
}
}
.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" />
데모
텍스트의 일부에서 클릭할 수 있는 부분에 매우 도움이 됩니다.
점은 정규 표현에서 특수 문자입니다.를 넓히려면 .\\
넘어가는 게 .
를text . spanable text로 지정합니다.정규 인 '아까보다'를 .[.]
자바 스트링
언급URL : https://stackoverflow.com/questions/10696986/how-to-set-the-part-of-the-text-view-is-clickable
'programing' 카테고리의 다른 글
원자 속성과 비원자 속성의 차이점은 무엇입니까? (0) | 2023.04.22 |
---|---|
Git - 모든 위치에서 node_modules 폴더 무시 (0) | 2023.04.22 |
Excel의 모든 셀에 공통 접두사 추가 (0) | 2023.04.22 |
Xcode 6에서 생성되지 않은 목표 C 헤더로 빠르게 이동 (0) | 2023.04.22 |
Bash 루프의 카운터 증분이 작동하지 않음 (0) | 2023.04.22 |