如何在XML中使用数据绑定将资源中的字符串与动态变量组合?

209

我有一个TextView,其中有一个硬编码的字符串,我有一个动态变量,想将其放在该字符串的末尾。这是我的代码:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_marginLeft="16dp"
    android:layout_marginRight="16dp">
    <TextView
        android:id="@+id/PeopleName"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/Generic_Text"+"@{ Profile.name }" />


</LinearLayout>

我遇到了问题,与 android:text="@string/Generic_Text"+"@{ Profile.name }" 有关。 Generic_Text 为 " My Name is ",然后Profile.name 是动态的,在不同的个人资料中会有所更改。我希望整个 TextView 输出为My Name is {Profile.name}。任何帮助都将不胜感激。


你正在使用数据绑定吗? - malmling
是的,我正在使用数据绑定 - Ignacio Perez
请将您当前的Java代码添加进来。 - malmling
13个回答

431

你甚至可以更加简单地完成这个任务:

android:text= "@{@string/generic_text(profile.name)}"

你的字符串应该像这样:

<string name="generic_text">My Name is %s</string>

编辑:

  1. 当然,您可以使用尽可能多的变量:

android:text= "@{@string/generic_text(profile.firstName, profile.secondName)}"

<string name="generic_text">My Name is %1$s %2$s</string>
  • 它之所以能够工作,是因为它采用了数据绑定的设计。更多信息请参阅文档:https://developer.android.com/topic/libraries/data-binding/expressions#resources


  • 1
    但如果数据尚未准备好(例如,尚未从网络加载),则会显示“我的名字为空”。 - user924
    6
    在这种情况下,请先检查它。android:text="@{profile == null ? @string/loading : @string/generic_text(profile.firstName, profile.secondName)}" - Roman_D
    3
    最好的答案 :) - Heriberto Rivera
    Android Studio没有提供智能感知功能,这是正常的还是我的有问题? - walox

    236

    你可以这样做:

    android:text= "@{String.format(@string/Generic_Text, Profile.name)}"
    

    如果您在 Generic_Text 字符串中使用字符串格式化,例如在结尾处使用 %s


    非常感谢,我刚刚把它放进去了,现在它可以正常工作了。 - Ignacio Perez
    6
    这不应该被接受作为答案,因为它实际上并没有使用数据绑定功能来操作文本。 - Darwind
    1
    同意 @Darwind 的看法,请查看我的答案,其中包含一个可行的绑定适配器实现。 - juanagui
    @IgorGanapolsky 这个答案已经超过3年了,但以前肯定是有效的 - 你还记得你尝试时出了什么问题吗? - C0D3LIC1OU5
    Android Studio没有提供智能感知功能,这是正常的还是我的有问题? - walox

    92

    连接字符串的多种方法

    1. 使用字符串资源(推荐,因为具有本地化功能

    android:text="@{@string/generic_name(user.name)}"

    只需创建如下所示的字符串资源。

    <string name="generic_name">Hello %s</string>
    

    2. 硬编码拼接

    android:text="@{`Hello ` + user.name}"/>
    

    当你需要像电话号码这样的硬编码附加时,这很有用,例如使用 +。

    3. 使用 String 的 concat 方法

    android:text="@{user.firstName.concat(@string/space).concat(user.lastName)}"
    

    strings.xml文件中,space被用作html实体。这是因为XML不能直接接受Html实体或特殊字符。(链接Html实体)

    <string name="space">\u0020</string>
    

    4. 使用 String.format() 方法

    android:text= "@{String.format(@string/Hello, user.name)}"
    
    你需要在布局中导入String类,例如这个类型。
    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
        <data>
            <import type="String" />
        </data>
        <TextView
            android:text= "@{String.format(@string/Hello, user.name)}"
            ... >
        </TextView>
    </layout>
    

    5. 通过字符串资源连接两个字符串。

    android:text="@{@string/generic_name(user.firstName,user.lastName)}"
    

    在此情况下,将字符串资源放入 strings.xml 文件中。

    <string name="generic_name">%1$s, %2$s</string>
    

    有很多其他方法,选择你需要的一个。


    我得到了错误提示:“找不到标识符格式”。 - Andrew

    12

    strings.xml: <string name="my_string">你好,%s</string>

    view.xml: android:text="@{@string/my_string(name)}"


    作为支持信息,需要在gradle中启用数据绑定,并将layout标签作为view.xml的根标签。 - Sagar Patel

    9

    使用绑定适配器(Binding Adapter)

    此示例使用Kotlin编写,并考虑到绑定变量可能为空:

    @BindingAdapter("my_name")
    fun TextView.setMyName(name: String?) {
        this.text =
            if (name.isNullOrEmpty()) "" else "${this.context.getString(R.string.Generic_Text)} $name"
    }
    

    那么在你的XML中使用绑定适配器,而不是android:text属性。

    app:my_name="@{Profile.name}"
    

    一直在遇到在绑定中调用导入函数的很多麻烦,这个答案对我来说非常完美,并允许我重复使用我的扩展。谢谢! - Danny Buonocore
    由于String类已经提供了基本的操作,因此该解决方案仅适用于基于您的要求进行高级字符串操作。但是,这并不是不正确的。 - sud007

    8

    2019年更新,Android Studio升级至3.4版,Android Gradle插件升级至3.4版

    不再需要手动导入

    <import type="java.lang.String" />" 
    

    针对字符串操作,请查看此答案


    7

    如果您想在XML中键入文本,则可以使用``引号。

    android:text="@{`Device Name`}"
    

    如果你需要将字符串或变量连接起来,你可以使用Concat函数

    android:text="@{`Device Name`.concat(android.os.Build.MANUFACTURER)}"
    

    如果你想要连接字符串资源而不是变量,可以这样做:
    android:text="@{@string/app_name.concat(`Device Name`)}"
    

    5
    您还可以使用格式化程序将字符串资源设置为其他字符串资源的参数,如下所示:
    <string name="first_param_text">Hello</string>
    <string name="second_param_text">World</string>
    <string name="formatted_text">%s lovely %s</string>
    

    android:text="@{String.format(@string/formatted_text, @string/first_param_text, @string/second_param_text)}"
    

    “Hello lovely World”将出现在视图中。


    2

    对我而言,只需使用“+”运算符即可:

    android:text= "@{@string/Generic_Text +' '+ Profile.name)}"
    

    String.xml文件将会是:

    <string name="Generic_Text">Hello</string>
    

    1

    如果您无法更改资源字符串以包含末尾的%s(例如,因为它在其他地方使用时没有后缀):

    android:text="@{@string/Generic_Text.concat(Profile.name)}"
    

    如果Profile.name不能为空,那就足够了。但是,如果出现null,程序将会崩溃。你需要添加另一层保护措施:
    android:text="@{@string/Generic_Text.concat(Objects.toString(Profile.name))}"
    

    (需要使用<import type="java.util.Objects"/>才能正常工作。)
    再次强调:只有在资源字符串在其他地方被使用时,所有这些额外的工作才是值得的。第二个原因是当您想将null处理为“空字符串”而不是“null”字面值时。

    2
    如果字符串资源在其他地方使用,为什么不创建一个新资源呢? - Jarett Millard

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