安卓 TextView:不要使用 setText 连接显示的文本

208

我正在使用以下方式通过setText()设置文本。

prodNameView.setText("" + name);

prodOriginalPriceView.setText("" + String.format(getString(R.string.string_product_rate_with_ruppe_sign), "" + new BigDecimal(price).setScale(2, RoundingMode.UP)));

第一个是简单的使用,第二个是设置带格式的文本。

Android Studio非常有趣,我使用菜单Analyze -> Code Cleanup,并获得了对上述两行代码的建议。

enter image description here

不要将要显示的文本与setText拼接在一起。而是应该使用带占位符的资源字符串。 less... (Ctrl+F1)

在调用TextView#setText:时:

  • 永远不要调用Number#toString()来格式化数字;它无法正确处理小数分隔符和特定于语言环境的数字。考虑改用String#format以及适当的格式规范(%d或%f)。
  • 不要将字符串字面值(例如“Hello”)传递给显示文本。硬编码的文本无法正确翻译成其他语言。请考虑使用Android资源字符串。
  • 不要通过连接文本块来构建消息。这种消息无法正确翻译。

针对此问题,我应该怎么做?请问有人能帮忙解释这是什么意思以及我应该怎么做吗?


2
这意味着你应该只传递一个 StringsetText() 方法中。例如:使用 setText(name) 而不是 setText("" + name)。因为如果你连接文本,它将不会像使用硬编码文本一样被翻译为消息通知。 - Neo
但如果“name”为“NULL”,它将会给出“NPE”。 - Pratik Butani
在使用 setText() 函数之前,检查 name 是否为空。 - Neo
4
在字符串资源中不应该与某些值拼接字符串,而是应该使用占位符。在你的string.xml文件中,可以这样做:<string name="string_product_rate_with_ruppe_sign">Something %1$d</string>然后在你的Java代码中可以这样写:prodOriginalPriceView.setText(getString(R.string.string_product_rate_with_ruppe_sign), price);(你也可以在xml文件中进行格式化:[http://developer.android.com/guide/topics/resources/string-resource.html]) - CodeBreakers
14个回答

419
资源有一个重载版本的getString方法,它接受一个类型为Object的可变参数varargs:getString(int, java.lang.Object...)。如果你在strings.xml中正确设置了字符串,并使用了正确的占位符,你可以使用这个版本来获取格式化后的最终字符串。例如:
<string name="welcome_message">Hello, %1$s! You have %2$d new messages.</string>

使用`getString(R.string.welcome_message, "Test", 0);`。
Android将返回一个带有字符串的结果。
 "Hello Test! you have 0 new messages"

关于 setText("" + name);

你的第一个例子,prodNameView.setText("" + name); 对我来说没有任何意义。TextView 能够处理空值。如果 name 是空值,将不会绘制任何文本。


1
假设您在 BigDecimal 上调用 float value:请在 strings.xml 中添加“%1$f”到您的字符串,然后调用“setText(getString(R.string.string_product_rate_with_ruppe_sign, new BigDecimal(price).setScale(2, RoundingMode.UP).floatValue()));”。 - Blackbelt
2
我想展示一个整数。字符串表示 $s,十进制数表示 $d。那么整数代表什么? - reegan29
1
如果您指的是“占位符”,则可以使用“%1$d”。@reegan29 - Blackbelt
5
这是API列表,列出各种格式类型的相关信息:https://developer.android.com/reference/java/util/Formatter#syntax。 - Brent Sandstrom
1
这个答案非常好。如果有人对%1$s和%2$d感到困惑,请看下面Rissmon Suresh的答案。他很好地解释了那部分内容。 - Tim
显示剩余2条评论

60

在接受的答案中不要混淆%1$s%2$d。以下是一些额外信息。

  • 格式说明符可以具有以下语法:

%[argument_index$]format_specifier

  1. 可选的参数索引以数字形式出现,在“%”后以“$”结尾,并选择参数列表中指定的参数。第一个参数由"1$"引用,第二个参数由"2$"引用,依此类推。
  2. 必需的格式说明符是一个字符,指示应如何格式化参数。对于给定参数的有效转换集取决于参数的数据类型。

例子

我们将创建以下格式化字符串,其中灰色部分是通过编程插入的。

Hello Test! you have 0 new messages

您的字符串资源

< string name="welcome_messages">Hello, %1$s! You have %2$d new messages< /string >

按照以下方式进行字符串替换

getString(R.string.welcome_message, "Test", 0);

注意:

  • %1$s 将被字符串 "Test" 替换
  • %2$d 将被字符串 "0" 替换

22

我遇到了相同的代码检查错误信息,并以这种方式解决了它。

起初我的代码是:

private void displayQuantity(int quantity) {
    TextView quantityTextView = (TextView) findViewById(R.id.quantity_text_view);
    quantityTextView.setText("" + quantity);
}

我收到了以下错误信息

Do not concatenate text displayed with setText. Use resource string with placeholders.

所以,我把这段内容添加到strings.xml中。

<string name="blank">%d</string>

这是我的初始  ,用于占位符以容纳我的数字(数量)。

注意:我之前已定义了变量quantity,这是我想要添加到字符串中的内容。因此,我的代码如下:

private void displayQuantity(int quantity) {
    TextView quantityTextView = (TextView) findViewById(R.id.quantity_text_view);
    quantityTextView.setText(getString(R.string.blank, quantity));
}

之后,我的错误消失了。应用程序的行为没有改变,我的数量继续按照我现在想要的方式显示,而且没有任何代码问题。


19

不要在 setText() 方法中连接文本,将任何想要连接的内容都连接到一个 String 中,然后将这个字符串值放入你的 setText() 方法。

例如:正确的方法

int min = 120;
int sec = 200;
int hrs = 2;

String minutes = String.format("%02d", mins);
String seconds = String.format("%02d", secs);
String newTime = hrs+":"+minutes+":"+seconds;

text.setText(minutes);

不要在setText()中使用拼接字符串,例如:


text.setText(hrs+":"+String.format("%02d", mins)+":"+String.format("%02d", secs));

8
为什么?一个有哪些优势而另一个没有? - Fureeish
3
这是一种廉价的作弊行为。使用'@SuppressLint("SetTextI18n")'会更容易。 - The incredible Jan

11

你应该查看这个线程,并像他那样使用一个占位符(未经测试)

<string name="string_product_rate_with_ruppe_sign">Price : %1$d</string>

String text = String.format(getString(R.string.string_product_rate_with_ruppe_sign),new BigDecimal(price).setScale(2, RoundingMode.UP));
prodOriginalPriceView.setText(text);

8

别生气,这太简单了。

String firstname = firstname.getText().toString();
String result = "hi "+ firstname +" Welcome Here";
            mytextview.setText(result);

6
问题是由于您在每个字符串的开头添加了""。lint将扫描传递给setText的参数并生成警告,对于您的情况,以下警告相关:

不要通过连接文本块建立消息。此类消息无法正确翻译。

由于您正在使用""连接每个字符串,请移除此连接,因为您传递的参数已经是文本。此外,如果需要,在其他地方可以使用.toString()而不是将您的字符串与""连接起来。

6

我使用String.format进行了修复。

之前:

textViewAddress.setText("Address"+address+"\n"+"nCountry"+"\n"+"City"+"city"+"\n"+"State"+"state")

之后:

textViewAddress.setText(
     String.format("Address:%s\nCountry:%s\nCity:%s\nState:%s", address, country, city, state));
       

2
你可以使用这个,对我有效。
title.setText(MessageFormat.format("{0} {1}", itemList.get(position).getOppName(), itemList.get(position).getBatchNum()));

1
如果您的情况与我类似,无法访问资源并且正在使用Kotlin,则可以执行以下操作:
val v = "hello"

"placeholder $v placeholder".let {
                    textView.text = it
                }

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