Android字符串格式化异常:Dialog中同时显示占位符与正确内容
问题描述
我在strings.xml中定义了如下字符串:
<string name="end_test_msg">Your score is: %1$s%%</string>
将其用于DialogFragment的AlertDialog.Builder.setMessage参数中(score是由浮点数或整数转换而来的字符串),相关代码如下:
class EndTestDialogFragment(percent: Double) : DialogFragment() { // 将百分比转为字符串,若是整数则去掉".00" private val score: String = when(percent.compareTo(percent.toInt()) == 0) { true -> percent.toInt().toString() false -> String.format("%.2f", percent) } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return activity?.let { val builder = AlertDialog.Builder(it) builder.setTitle(getString(R.string.end_test_title)) builder.setMessage(getString(R.string.end_test_msg, score)) .setPositiveButton(getString(R.string.ok), DialogInterface.OnClickListener { dialog, id -> this.activity?.finish()// 返回主页面 }) // 创建AlertDialog并返回 builder.setCancelable(false) builder.create() } ?: throw IllegalStateException("Activity cannot be null") } }
预期显示效果为Your score is: 50%,但对话框实际显示Your score is: %1$s%% 50%,无论score是整数转换还是格式化后的浮点数都会出现此问题。该DialogFragment定义在使用它的Activity内部,而在Activity的TextView中使用完全相同的写法却能正常显示,不会残留占位符。我是Android/Kotlin新手,请问问题出在哪里?
问题原因及解决办法
问题出在**AlertDialog.Builder.setMessage的参数处理逻辑**:
- 调用
builder.setMessage(getString(R.string.end_test_msg, score))时,getString已经完成了字符串格式化,生成了类似Your score is: 50%的结果。 - 但
AlertDialog.Builder的setMessage方法如果接收普通字符串,内部会再次尝试解析占位符(它支持直接传入带占位符的字符串并后续填充参数)。而你的格式化结果里有%符号(来自原字符串的%%转义),它会把%当成占位符起始,导致解析错误,最终把原占位符和填充后的内容都显示出来。
有两种解决办法:
直接传递格式化后的字符串,避免二次解析
把setMessage的参数转为Spanned类型,让AlertDialog直接显示内容而不解析占位符:// Android N及以上建议用Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY) builder.setMessage(Html.fromHtml(getString(R.string.end_test_msg, score)))让
AlertDialog自己处理占位符填充
不要提前用getString格式化,直接传入字符串资源ID和参数,让setMessage内部完成格式化:builder.setMessage(R.string.end_test_msg, score)这种方式更简洁,也是Android推荐的写法,
AlertDialog.Builder的setMessage重载方法支持直接接收资源ID和可变参数完成占位符填充。
内容的提问来源于stack exchange,提问作者geardan
相关产品推荐
相关产品推荐

