如何从字符串资源中获取可点击的超链接,并在AlertDialog中实现?

143

我的目标是让AlertDialog中显示的消息文本中有可点击的超链接。虽然AlertDialog实现可以很好地为任何超链接(在传递给Builder.setMessage的字符串资源中使用<a href="...">进行定义)添加下划线和颜色,但这些链接无法被点击。

我当前正在使用的代码如下:

new AlertDialog.Builder(MainActivity.this).setTitle(
        R.string.Title_About).setMessage(
        getResources().getText(R.string.about))
        .setPositiveButton(android.R.string.ok, null)
        .setIcon(R.drawable.icon).show();

我想避免使用 WebView 来仅显示文本片段。


你好!你真的能够实现声明的结果(“愉快地为任何超链接添加下划线和颜色”)吗?你传递了什么字符串值? - Maksym Gontar
1
是的,关键是将要显示的消息放在字符串资源中,Resources.getText(...)会返回一个保留HTML格式的android.text.Spanned。但是一旦你将其转换为字符串,魔法就消失了。 - Thilo-Alexander Ginkel
19个回答

216

我并不是很喜欢目前最受欢迎的答案,因为它会显著改变对话框中消息的格式。

这是一种解决方案,可以在不改变文本样式的情况下将您的对话文本链接化:

    // Linkify the message
    final SpannableString s = new SpannableString(msg); // msg should have url to enable clicking
    Linkify.addLinks(s, Linkify.ALL);

    final AlertDialog d = new AlertDialog.Builder(activity)
        .setPositiveButton(android.R.string.ok, null)
        .setIcon(R.drawable.icon)
        .setMessage( s )
        .create();

    d.show();

    // Make the textview clickable. Must be called after show()
    ((TextView)d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());

5
欢呼,这对我很有用。它是在一个“DialogFragment”的“onCreateDialog”方法内部工作的。只需在调用“show”以调用“DialogFragment”之后,设置可点击的代码即可在“onStart”中实现。 - PJL
6
这似乎会使整个TextView都可点击,而不仅仅是链接... 有什么解决办法吗? - Kavi
1
我同意这是一个更好的选择,因为原始答案会在视觉上搞乱对话框。 - hcpl
1
应该使用“instanceof TextView”检查findViewById返回的视图,因为无法保证实现不会更改。 - Denis Gladkiy
6
在其他地方指出,如果使用setMessage(R.string.something),则不需要显式地进行链接化。在调用show()之前不需要显式地创建AlertDialog对象(可以在Builder上调用),由于show()返回对话框对象,因此可以将findViewById(android.R.id.message)链接在一起。将所有内容放入try-catch语句块中,以防消息视图不是TextView,这样就可以得到一个简洁的表述。 - Pierre-Luc Paour
显示剩余8条评论

131

如果你的对话框只展示一些文本和网址,解决方案可能更简单。

public static class MyOtherAlertDialog {

 public static AlertDialog create(Context context) {
  final TextView message = new TextView(context);
  // i.e.: R.string.dialog_message =>
            // "Test this dialog following the link to dtmilano.blogspot.com"
  final SpannableString s = 
               new SpannableString(context.getText(R.string.dialog_message));
  Linkify.addLinks(s, Linkify.WEB_URLS);
  message.setText(s);
  message.setMovementMethod(LinkMovementMethod.getInstance());

  return new AlertDialog.Builder(context)
   .setTitle(R.string.dialog_title)
   .setCancelable(true)
   .setIcon(android.R.drawable.ic_dialog_info)
   .setPositiveButton(R.string.dialog_action_dismiss, null)
   .setView(message)
   .create();
 }
}
在这里展示了一个带有可点击链接的警告对话框的截图。请查看此处获取更多信息。 Alert dialog with clickable links

2
你可能想要创建一个布局文件并将其填充,然后将其用作视图。 - Jeffrey Blattman
5
您想将textView的样式设置为与默认使用的样式相匹配,应该如何操作? - android developer
3
那么,我遇到了这个错误:在 Activity 上下文之外调用 startActivity() 需要使用 FLAG_ACTIVITY_NEW_TASK 标记。这真的是你想要的吗? - ViliusK

55

这应该也能使得 <a href> 标签被高亮显示。请注意,我只是在 emmby 的代码上添加了几行。因此要感谢他。

final AlertDialog d = new AlertDialog.Builder(this)
 .setPositiveButton(android.R.string.ok, null)
 .setIcon(R.drawable.icon)
 .setMessage(Html.fromHtml("<a href=\"http://www.google.com\">Check this link out</a>"))
 .create();
d.show();
// Make the textview clickable. Must be called after show()   
    ((TextView)d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());

10
如果您在strings.xml中使用html,就不需要使用Html.fromHtml。setMessage(R.string.cool_link) 可以与 <string name="cool_link"><a href="http://www.google.com">Check this link out</a></string> 一起使用。 - idbrii
2
这是真的。当你结合使用Html.fromHtml和strings.xml中的HTML标签时,它不起作用。 - JerabekJakub
好久不见,fromHtml已经被弃用了,现在怎么办? - Menasheh
你仍然可以使用fromHtml:https://developer.android.com/reference/android/text/Html.html#fromHtml(java.lang.String, int)只需使用Html.fromHtml("带有链接的字符串", Html.FROM_HTML_MODE_LEGACY) - BVB
2
这里setMovementMethod()是重要的部分,否则URL将无法被点击。 - scai
1
我在最后一行遇到空指针异常,错误提示是 ((TextView)d.findViewById(android.R.id.message)) 为空。 - Donald Duck

13

实际上,如果你想简单地使用字符串而不必处理所有的视图,最快的方法是找到消息文本视图并将其链接化:

d.setMessage("Insert your cool string with links and stuff here");
Linkify.addLinks((TextView) d.findViewById(android.R.id.message), Linkify.ALL);

12

顺便说一句,这是我在一段时间后找到的解决方案:

View view = View.inflate(MainActivity.this, R.layout.about, null);
TextView textView = (TextView) view.findViewById(R.id.message);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setText(R.string.Text_About);
new AlertDialog.Builder(MainActivity.this).setTitle(
        R.string.Title_About).setView(view)
        .setPositiveButton(android.R.string.ok, null)
        .setIcon(R.drawable.icon).show();

从Android源代码中借用的相应关于.xml片段如下所示:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scrollView" android:layout_width="fill_parent"
    android:layout_height="wrap_content" android:paddingTop="2dip"
    android:paddingBottom="12dip" android:paddingLeft="14dip"
    android:paddingRight="10dip">
    <TextView android:id="@+id/message" style="?android:attr/textAppearanceMedium"
        android:layout_width="fill_parent" android:layout_height="wrap_content"
        android:padding="5dip" android:linksClickable="true" />
</ScrollView>
重要的部分是将linksClickable设置为true并使用setMovementMethod(LinkMovementMethod.getInstance())方法。

谢谢,这解决了我的问题。在我的情况下,不需要setLinksClickable(true)(我猜它已经是这样了),但setMovementMethod(...)起到了关键作用。 - LarsH

10

Instead of ...

AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
dialogBuilder.setTitle(R.string.my_title);
dialogBuilder.setMessage(R.string.my_text);

我现在使用的是:
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
dialogBuilder.setTitle(R.string.my_title);
TextView textView = new TextView(this);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setText(R.string.my_text);
dialogBuilder.setView(textView);

嘿,你的解决方案有效。不过你知道为什么点击链接时整个文本视图会闪烁吗? - aimango
它不像默认的那样滚动。 - Meow Cat 2012

7

最简单的方法:

final AlertDialog dlg = new AlertDialog.Builder(this)
                .setTitle(R.string.title)
                .setMessage(R.string.message)
                .setNeutralButton(R.string.close_button, null)
                .create();
        dlg.show();
        // Important! android.R.id.message will be available ONLY AFTER show()
        ((TextView)dlg.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());

6

如果给定的字符串包含像 <p>、<a> 等 html 标签,上述所有答案都无法去除这些标签。我尝试了移除所有标签,并且这对我来说很有效。

AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
        builder.setTitle("Title");

        LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(LAYOUT_INFLATER_SERVICE);
        View layout = inflater.inflate(R.layout.custom_dialog, null);

        TextView text = (TextView) layout.findViewById(R.id.text);
        text.setMovementMethod(LinkMovementMethod.getInstance());
        text.setText(Html.fromHtml("<b>Hello World</b> This is a test of the URL <a href=http://www.example.com> Example</a><p><b>This text is bold</b></p><p><em>This text is emphasized</em></p><p><code>This is computer output</code></p><p>This is<sub> subscript</sub> and <sup>superscript</sup></p>";));
        builder.setView(layout);
AlertDialog alert = builder.show();

自定义对话框将会是这样的:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/layout_root"
              android:orientation="horizontal"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:padding="10dp"
              >

    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="fill_parent"
              android:textColor="#FFF"
              />
</LinearLayout>

上述代码将删除所有HTML标记,并将示例显示为可单击的URL,而其他内容则保留指定的HTML格式文本。

5

我对当前的答案并不满意。当你想要在AlertDialog中使用href样式的可点击超链接时,有两个重要的事情:

  1. 将内容设置为View而不是使用setMessage(…),因为只有View才允许可点击的HTML内容
  2. 设置正确的移动方法(setMovementMethod(…))

这里是一个运行良好的最小示例:

strings.xml

<string name="dialogContent">
    Cool Links:\n
    <a href="http://stackoverflow.com">Stackoverflow</a>\n
    <a href="http://android.stackexchange.com">Android Enthusiasts</a>\n
</string>

MyActivity.java

public void showCoolLinks(View view) {
   final TextView textView = new TextView(this);
   textView.setText(R.string.dialogContent);
   textView.setMovementMethod(LinkMovementMethod.getInstance()); // this is important to make the links clickable
   final AlertDialog alertDialog = new AlertDialog.Builder(this)
       .setPositiveButton("OK", null)
       .setView(textView)
       .create();
   alertDialog.show()
}
…

这个程序完美地工作,但是在警报框中的文本从最左边开始,没有任何空白间隔。你可以通过使用textView.setPadding()来轻松调整。 - Michele

5

对于我来说,创建隐私政策对话框的最佳解决方案是:

    private void showPrivacyDialog() {
    if (!PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(PRIVACY_DIALOG_SHOWN, false)) {

        String privacy_pol = "<a href='https://sites.google.com/view/aiqprivacypolicy/home'> Privacy Policy </a>";
        String toc = "<a href='https://sites.google.com/view/aiqprivacypolicy/home'> T&C </a>";
        AlertDialog dialog = new AlertDialog.Builder(this)
                .setMessage(Html.fromHtml("By using this application, you agree to " + privacy_pol + " and " + toc + " of this application."))
                .setPositiveButton("ACCEPT", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit().putBoolean(PRIVACY_DIALOG_SHOWN, true).apply();
                    }
                })
                .setNegativeButton("DECLINE", null)
                .setCancelable(false)
                .create();

        dialog.show();
        TextView textView = dialog.findViewById(android.R.id.message);
        textView.setLinksClickable(true);
        textView.setClickable(true);
        textView.setMovementMethod(LinkMovementMethod.getInstance());
    }
}

请检查工作示例:应用程序链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接