如何设置部分文本视图可点击

Posted

技术标签:

【中文标题】如何设置部分文本视图可点击【英文标题】:How to set the part of the text view is clickable 【发布时间】:2012-05-28 15:30:36 【问题描述】:

我有文本“Android 是一个软件堆栈”。在此文本中,我想设置“stack”文本是可点击的。从某种意义上说,如果您单击它将重定向到新活动(而不是在浏览器中)。

我试过了,但我没有。

【问题讨论】:

“我试过了,但我没有得到”我想知道你尝试了什么以及你失败的地方 【参考方案1】:

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"
/>

【讨论】:

你可以在一个文本视图中设置多个 ClickableSpan 对象吗? 是的,您可以将几个可点击的跨度设置为可跨度字符串。 要使颜色变为蓝色,您可以添加:ForegroundColorSpan fcs = new ForegroundColorSpan(Color.BLUE); ss.setSpan(fcs, 22, 27, Spannable.SPAN_INCLUSIVE_INCLUSIVE); 这发生在我的案例中,将颜色从蓝色变为其他颜色。 设置了可点击范围之后,在setSpan 中设置ForegroundColorSpan。在 Clickable span 之前放置 Foreground,新颜色不会被反射。 感谢这一行 stextView.setMovementMethod(LinkMovementMethod.getInstance()); textView.setHighlightColor(Color.TRANSPARENT);【参考方案2】:

我在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_
    android:layout_
    android:text="Please accept Terms of Service and Privacy Policy"
    android:textColorHighlight="#f00" // background color when pressed
    android:textColorLink="#0f0"
    android:textSize="20sp" />

演示

参考

清除链接高亮选择的解决方案关注https://***.com/a/19445108/5381331

【讨论】:

它运行良好。但是出现了一些奇怪的行为。我有一个以“政策”结尾的文本。 “。”在哪里将被 Clickable spannable 跳过。有没有人有同样的问题?这 ”。”仅当它是文本视图的结束字符时才被跳过。 很好的解决方案,但是如果有字符串的本地化(多语言应用程序),有人需要小心 @BlackBlind 你得到哪个错误?如果导入有问题,请正确导入。你也不应该把函数放在onCreate里面,把它放在外面 在重复链接的情况下,它总是返回第一个,因为此解决方案使用indexOf 谢谢!我的 java 等价物:gist.github.com/daler445/b7106b5c5ed431ecfb206afe98746bbe【参考方案3】:

您可以按照post 中的说明使用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);

Reference

【讨论】:

感谢@ImranRana,支持startIndexendIndex 逻辑。 使用 [] 计算起始索引和结束索引,是完美的方法,但也可以在文本中显示 []。我不想在文本中显示 []。【参考方案4】:

您可以使用示例代码。您想了解有关 ClickableSpan 的详细信息。请检查这个documentaion

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

【讨论】:

【参考方案5】:

我做了这个辅助方法,以防有人需要字符串的开始和结束位置。

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

【讨论】:

这更好,因为它考虑到本地化【参考方案6】:

这是一个 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") )

【讨论】:

【参考方案7】:
 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());

【讨论】:

你能解释一下你的答案吗?有解释的回答总是更好。我还编辑了您的代码 sn-p 格式不正确。 看起来链接将指向网页,而不是 OP 请求的活动。【参考方案8】:

我会建议一种不同的方法,我认为它需要更少的代码并且更“本地化”。

假设您的目标活动名为“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://***.com/a/20647011/1699702):

<TextView
    android:id="@+id/stackView"
    android:layout_
    android:layout_
    android:text="@string/stack_string" />

然后在 TextView 的文本中使用自定义方案和宿主的链接作为(在 String.xml 中):

<string name="stack_string">Android is a Software <a href="myappscheme://stack">stack</a></string>

并使用 setMovementMethod()“激活”链接(在 onCreate() 中用于活动或 onCreateView() 用于片段):

TextView stack = findViewById(R.id.stackView);
stack.setMovementMethod(LinkMovementMethod.getInstance());

这将打开堆栈活动,点击“堆栈”一词。

【讨论】:

【参考方案9】:

Kotlin 版本 Phan Van Linh 的回答。

请注意它有一些小的修改。

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

【讨论】:

【参考方案10】:

你可以用这个方法来设置可点击值

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

【讨论】:

【参考方案11】:

创建优雅的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)

【讨论】:

【参考方案12】:

对于那些在 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);


【讨论】:

【参考方案13】:

对于粗体,

mySpannable.setSpan(new StyleSpan(Typeface.BOLD),termStart,termStop,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

【讨论】:

【参考方案14】:

我编写了一个示例来解决您在 Kotlin 中的问题。

这是代码:

    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_
            android:layout_/>

    <TextView
            android:id="@+id/show_terms"
            android:layout_
            android:layout_
            android:textColorLink="@color/colorPrimary"
            android:layout_toEndOf="@id/check_agree_terms"/>

这就是它的样子

enter image description here

【讨论】:

【参考方案15】:

为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());
    

【讨论】:

【参考方案16】:

这是我的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);




【讨论】:

【参考方案17】:

kotlin 中更通用的答案

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



【讨论】:

【参考方案18】:

提供的解决方案相当不错。但是,我通常使用更简单的解决方案。

这是一个链接实用功能

/**
 * 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)

【讨论】:

【参考方案19】:

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()

kotlinspannable

【讨论】:

【参考方案20】:

这对于文本某些部分的可点击部分非常有帮助。

点是正则表达式中的特殊字符。如果你想扩展点需要将点转义为\\。而不是仅仅将“.”传递给可跨文本方法。或者,您也可以使用正则表达式 [.] 在 Java 中通过点来跨越字符串。

【讨论】:

以上是关于如何设置部分文本视图可点击的主要内容,如果未能解决你的问题,请参考以下文章

如何在 react-native 中使文本的某些部分可点击并且某些部分具有不同的文本颜色

Android:将 onClickListener 设置为 TextView 中的部分文本 - 问题

如何在可点击的文本视图中设置超链接? Android Java [重复]

如何在 uwp 应用程序中仅使部分 gridview 项目可点击?

如何使列表视图的文本视图可点击

使用表格视图标题中的扩展文本扩展标签和视图