【问题标题】:Multiple Clickable links in TextView on AndroidAndroid 上 TextView 中的多个可点击链接
【发布时间】:2015-04-27 12:16:45
【问题描述】:

我正在尝试在文本视图中添加多个链接,类似于 Google 和 Flipboard 在下面所做的,屏幕截图中显示了他们的 条款和条件隐私政策下面:

到目前为止,我偶然发现了这种方法

textView.setText(Html.fromHtml(myHtml); textView.setMovementMethod(LinkMovementMethod.getInstance());

myHtml 是一个 href。

但它并没有给我我需要的控制权,例如启动片段等。

知道他们是如何在下面的两个示例中实现这一点的吗?

【问题讨论】:

    标签: java android android-layout textview


    【解决方案1】:

    我认为我分享这个有点晚了,但我使用SpannableStringBuilder 也达到了同样的效果。

    只需初始化您要添加 2 个或更多侦听器的 TextView,然后将其传递给我创建的以下方法:

    private void customTextView(TextView view) {
            SpannableStringBuilder spanTxt = new SpannableStringBuilder(
                    "I agree to the ");
            spanTxt.append("Term of services");
            spanTxt.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View widget) {
                    Toast.makeText(getApplicationContext(), "Terms of services Clicked",
                            Toast.LENGTH_SHORT).show();
                }
            }, spanTxt.length() - "Term of services".length(), spanTxt.length(), 0);
            spanTxt.append(" and");
            spanTxt.setSpan(new ForegroundColorSpan(Color.BLACK), 32, spanTxt.length(), 0);
            spanTxt.append(" Privacy Policy");
            spanTxt.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View widget) {
                    Toast.makeText(getApplicationContext(), "Privacy Policy Clicked",
                            Toast.LENGTH_SHORT).show();
                }
            }, spanTxt.length() - " Privacy Policy".length(), spanTxt.length(), 0);
            view.setMovementMethod(LinkMovementMethod.getInstance());
            view.setText(spanTxt, BufferType.SPANNABLE);
        } 
    

    在您的 XML 中,使用 android:textColorLink 添加您选择的自定义链接颜色。像这样:

       <TextView
         android:id="@+id/textView1"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="TextView"
         android:textColorLink="#C36241" />  //#C36241 - Rust
    

    看起来像这样:

    希望它可以帮助某人。 :)

    【讨论】:

    • 如果字符串不需要翻译,这很好用
    • 也可以与本地化一起使用:多语言只需将字符串传递到使用文本的位置。我刚刚完成了:)
    • 是的,它会起作用的。我的最后一条评论是针对 RTL 语言的。
    • 应用这个后选框不起作用,有什么想法吗?
    • @BeeingJk 你想滚动文本吗?
    【解决方案2】:

    您可以使用Linkify (android.text.Spannable,java.util.regex.Pattern,java.lang.String )

    String termsAndConditions = getResources().getString(R.string.terms_and_conditions);
    String privacyPolicy = getResources().getString(R.string.privacy_policy);
    
    legalDescription.setText(
        String.format(
            getResources().getString(R.string.message),
            termsAndConditions,
            privacyPolicy)
    );
    legalDescription.setMovementMethod(LinkMovementMethod.getInstance());
    
    Pattern termsAndConditionsMatcher = Pattern.compile(termsAndConditions);
    Linkify.addLinks(legalDescription, termsAndConditionsMatcher, "terms:");
    
    Pattern privacyPolicyMatcher = Pattern.compile(privacyPolicy);
    Linkify.addLinks(legalDescription, privacyPolicyMatcher, "privacy:");
    

    然后您可以使用该方案来启动一个活动,例如通过在 AndroidManifest 中添加该方案:

    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="terms" />
        <data android:scheme="privacy" />
    </intent-filter>
    

    如果您想执行自定义操作,可以将 Intent-filter 设置为您当前的活动,这将有一个 singleTop 启动模式。

    这将导致onNewIntent 在可以进行自定义操作的地方被触发:

    @Override
    protected void onNewIntent(final Intent intent) {
     ...
      if (intent.getScheme().equals(..)) {
        ..
      }
    }
    

    【讨论】:

    • 这很有帮助,但是如果我不想开始活动,我可以通过链接的侦听器处理点击吗?
    • 重要部分:Linkify.addLinks 必须在 tvTermsOfUse.setMovementMethod 之前完成,否则将不起作用。 XML 中不需要其他设置。
    • linkify 使链接显示为带有下划线如何删除它,以反映为flipboard this
    • 带有此自定义方案的 ACTION.view 意图只会在应用程序内,甚至其他人可以添加意图过滤器并利用它(我希望这不会容易出错)@harrane ??跨度>
    • @harrane 是否可以为此 Linkify 文本设置一个网址,以便在单击该文本时打开一个网页???
    【解决方案3】:

    这是我的解决方案:

    首先我们需要在 TextView 中有可点击的链接:

    1. 这是我在 xml 布局中的 TextView,不要添加任何链接处理 参数。

      <TextView
              android:id="@+id/sign_up_privacy"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="@string/terms_and_privacy"/>
      
    2. 在字符串文件中,我添加了带有 html 标签的资源文本

      <string name="terms_and_privacy">By signing up you agree to our <a href="terms:">Terms of Service</a> and <a href="privacy:">Privacy Policy.</a></string>
      
    3. 在 onCreateView 中将 LinkMovementMethod 设置为 TextView

      TextView privacyTextView = (TextView) root.findViewById(R.id.sign_up_privacy);
      privacyTextView.setMovementMethod(LinkMovementMethod.getInstance());
      

    现在 TextView 链接可以点击了。

    其次,我们需要处理这些点击:

    1. 在我的清单文件中,我为“条款”和“隐私”添加了意图过滤器 和单实例启动模式

      <activity
                  android:name=".MyActivity"
                  android:launchMode="singleInstance">
                  <intent-filter>
                      <category android:name="android.intent.category.DEFAULT"/>
                      <action android:name="android.intent.action.VIEW"/>
      
                      <data android:scheme="terms"/>
                      <data android:scheme="privacy"/>
                  </intent-filter>
              </activity>
      
    2. 在 MyActivity 中,覆盖 onNewIntent 以获取隐私和条款 意图

      @Override
          protected void onNewIntent(Intent intent)
          {
              if (intent.getScheme().equalsIgnoreCase(getString("terms")))
              {
                  //handle terms clicked
              }
              else if (intent.getScheme().equalsIgnoreCase(getString("privacy")))
              {
                  //handle privacy clicked
              }
              else
              {
                  super.onNewIntent(intent);
              }
          }
      

    【讨论】:

    • 它不工作。日志有消息 ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=terms: (has extras) }
    • @sonnv1368,您是否在 Activity 中覆盖了 onNewIntent(Intent)?
    • 无论如何我建议使用 SpannableString 作为更好的解决方案:stackoverflow.com/a/30365618/1729952
    • @Ahmod Baraka:谢谢。
    • 如果您只想打开一个新活动,则此答案的改进代码是将清单 设置为您要打开的目标 。这样可以避免对当前 Activity 使用“singleInstance”,这可能会导致一些严重的问题。
    【解决方案4】:

    最好的方法是使用字符串文件

    在string.xml中

    <string name="agree_to_terms">I agree to the %1$s and %2$s</string>
    <string name="terms_of_service">Terms of Service</string>
    <string name="privacy_policy">Privacy Policy</string>
    

    在 onCreateView 调用下面的方法

    private void setTermsAndCondition() {
        String termsOfServicee = getString(R.string.terms_of_service);
        String privacyPolicy = getString(R.string.privacy_policy);
        String completeString = getString(R.string.agree_to_terms, termsOfServicee,privacyPolicy);
        int startIndex = completeString.indexOf(termsOfServicee);
        int endIndex = startIndex + termsOfServicee.length();
        int startIndex2 = completeString.indexOf(privacyPolicy);
        int endIndex2 = startIndex2 + privacyPolicy.length();
        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(completeString);
        ClickableSpan clickOnTerms = new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(this, "click on terms of service", Toast.LENGTH_SHORT).show();
            }
    
            @Override
            public void updateDrawState(TextPaint ds) {
                ds.setColor(ContextCompat.getColor(this, R.color.blue));
    
            }
        };
        ClickableSpan clickOnPrivacy = new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(this, "click on privacy policy", Toast.LENGTH_SHORT).show();
            }
    
            @Override
            public void updateDrawState(TextPaint ds) {
                ds.setColor(ContextCompat.getColor(this, R.color.blue));
    
            }
        };
        spannableStringBuilder.setSpan(clickOnTerms, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        spannableStringBuilder.setSpan(clickOnPrivacy, startIndex2, endIndex2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        yourTextView.setText(spannableStringBuilder);
        yourTextView.setMovementMethod(LinkMovementMethod.getInstance());
    }
    

    【讨论】:

      【解决方案5】:

      使用Textoo,可以这样实现:

      res/values/strings.xml:

      <resources>
           <string name="str_terms_and_privacy">By signing up you agree to our <a href="terms:">Terms of Service</a> and <a href="privacy:">Privacy Policy.</a></string>
      </resources>
      

      res/layout/myActivity.xml:

      <TextView
          android:id="@+id/view_terms_and_privacy"
          android:text="@string/str_terms_and_privacy"
          />
      

      java/myPackage/MyActivity.java:

      public class MyActivity extends Activity {
          ...
          protected void onCreate(Bundle savedInstanceState) {
              ...
              TextView termsAndPrivacy = Textoo
                  .config((TextView) findViewById(R.id.view_terms_and_privacy))
                  .addLinksHandler(new LinksHandler() {
                      @Override
                      public boolean onClick(View view, String url) {
                          if ("terms:".equals(url)) {
                              // Handle terms click
                              return true;  // event handled
                          } else if ("privacy:".equals(url)) {
                              // Handle privacy click
                              return true;  // event handled
                          } else {
                              return false;  // event not handled.  continue default processing i.e. launch web browser and display the link
                          }
                      }
                  })
                  .apply();
              ...
          }
          ...
      }
      

      这种方法的优点是:

      • 将文本外部化为字符串资源。更轻松地本地化您的应用。
      • 可以直接使用LinksHandler处理点击事件,无需额外定义intent filter
      • 更简单、更易读的代码

      【讨论】:

        【解决方案6】:

        这对我有用:

        在 xml 中:

        <TextView
            android:id="@+id/tv_by_continuing_str"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="8dp"
            android:textSize="15sp"
            tools:text="Test msg 1 2, 3"
            android:textColor="@color/translucent_less_white3"
            android:textColorLink="@color/white"
            android:gravity="center|bottom"
            android:layout_above="@+id/btn_privacy_continue" />
        

        在strings.xml中

        < string name="by_continuing_str2">< ! [ CDATA[By continuing to use this app, you agree to our <a href="https://go.test.com" style="color:gray"/> Privacy Statement </a> and <a href="https://go.test.com" style="color:gray"/>Services Agreement.]]>< / string>
        

        在活动中:

        TextView tv_by_continuing = (TextView) findViewById(R.id.tv_by_continuing);
        tv_by_continuing.setText(Html.fromHtml(getString(R.string.by_continuing_str2)));
        tv_by_continuing.setMovementMethod(LinkMovementMethod.getInstance());
        

        【讨论】:

          【解决方案7】:
          
          private fun customTextView(view: TextView) {
          
                  //I agree to the Terms of service & Privacy Policy,
                  val spanTxt = SpannableStringBuilder(
                      "I agree to the ")
                  spanTxt.append("Terms of service")
                  spanTxt.setSpan(object : ClickableSpan() {
                      override fun onClick(widget: View) {
                       
                          toast("Terms of service link clicked")
          
                      }
                  }, spanTxt.length - "Term of services".length, spanTxt.length, 0)
                  spanTxt.append(" and")
                  spanTxt.append(" Privacy Policy")
                  spanTxt.setSpan(object : ClickableSpan() {
                      override fun onClick(widget: View) {
          
                          toast("Privacy Policy link clicked")
          
                      }
                  }, spanTxt.length - " Privacy Policy".length, spanTxt.length, 0)
                  view.movementMethod = LinkMovementMethod.getInstance()
                  view.setText(spanTxt, TextView.BufferType.SPANNABLE)
              }
          

          【讨论】:

            【解决方案8】:

            一个简单的解决方法是使用多个标签。每个链接一个,文本一个。

            [TermsOfServiceLabel][andLabel][PrivacyPolicy]
            

            还是必须只使用一个标签?

            【讨论】:

            • 多个标签是为了什么?文本视图?你能详细说明一下吗?
            • 如果我理解正确,您希望在一个 TextView 中有多个链接,但不知道该怎么做?拆分 TextView 会为每个 TextView 留下一个链接,这应该不是问题。
            • 我显然可以使用 3 个不同的文本视图,但还有其他事情需要考虑,例如不同设备上的间距,这就是为什么我试图看看是否有办法通过单视图。
            • 好吧,如果你想用一个TextView你可以忽略我的回答!
            猜你喜欢
            • 1970-01-01
            • 2012-03-13
            • 1970-01-01
            • 2016-04-07
            • 1970-01-01
            • 2022-11-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多