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

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

我试过了,但我没有。

【问题讨论】:

  • “我试过了,但我没有得到”我想知道你尝试了什么以及你失败的地方

标签: android string textview clickablespan


【解决方案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_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" />

演示

参考

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

【讨论】:

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

      然后在 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_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"/>
                  

                  这就是它的样子

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

                            【讨论】:

                              【解决方案20】:

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

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

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2017-02-01
                                • 2014-03-26
                                • 2014-07-08
                                相关资源
                                最近更新 更多