You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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方法如果接收普通字符串,内部会再次尝试解析占位符(它支持直接传入带占位符的字符串并后续填充参数)。而你的格式化结果里有%符号(来自原字符串的%%转义),它会把%当成占位符起始,导致解析错误,最终把原占位符和填充后的内容都显示出来。

有两种解决办法:

  1. 直接传递格式化后的字符串,避免二次解析
    把setMessage的参数转为Spanned类型,让AlertDialog直接显示内容而不解析占位符:

    // Android N及以上建议用Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY)
    builder.setMessage(Html.fromHtml(getString(R.string.end_test_msg, score)))
    
  2. 让AlertDialog自己处理占位符填充
    不要提前用getString格式化,直接传入字符串资源ID和参数,让setMessage内部完成格式化:

    builder.setMessage(R.string.end_test_msg, score)
    

    这种方式更简洁,也是Android推荐的写法,AlertDialog.Builder的setMessage重载方法支持直接接收资源ID和可变参数完成占位符填充。

内容的提问来源于stack exchange,提问作者geardan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 00:25:31