🎃

ViewModelからstringResの文字列を参照したくなったら

に公開

結論

ViewModelにはInt型の@StringResを持たせて、UI側でviewModelの@StringResをIntで取得し、文字列に変換。

具体的に

例えば、strings.xmlに以下のようなexample_messageがあるとする。

<resource>
    <string name="example_success">成功しました!!!</string>
    <string name="example_error">エラーが発生しました</string>
</resources>

UIからはこんな感じで呼び出せます。

@Composable
fun ExampleScreen(){
    Text(text = stringResource(R.string.example_success))
}

→画面には「成功しました!!!」が表示されます

ViewModelから呼び出そうとすると。。。

//簡単な状態管理をデータクラスで作成
data class ExampleUiState(
    val message: String = ""
    val error: String = ""
)

class ExampleViewModel : ViewModel() {
    private val _ui = MutableStateFlow(ExampleUiState())
    val ui: StateFlow<ExampleUiState> = _ui.asStateFlow()

    fun updateMessage() {
        if (_ui.value.error.isNotBlank())
            _ui.update { it.copy(message = stringResource(R.string.example_error)) }
        else
            _ui.update { it.copy(message = stringResource(R.string.example_success)) }
    }
}

エラーになります。
Composableの中に書けと🤨

viewModel側で条件分岐させて、この場合はこのメッセージみたいなことをやっていると、
UI側でも条件分岐させて、この場合はこのstringResね、みたいになるので、
めんどくさいわっ!!!!ってなります。
なので、viewModel側で出すメッセージを決めておいて、UI側はそれを受けて表示するだけ
としたいのです。

そんなわけで・・・

上で書いたようにviewModelはstring.xmlの文字列を読むことはできないのですが、
以下のように指定すれば、IDを読むことができるのです。(謎です)

data class ExampleUiState(
+    @StringRes val message: Int, //アノテーションつけてStringResだと宣言し、Int型にする
    val error: String = ""
)

class ExampleViewModel : ViewModel() {
    private val _ui = MutableStateFlow(ExampleUiState())
    val ui: StateFlow<ExampleUiState> = _ui.asStateFlow()

    fun updateMessage() {
        if (_ui.value.error.isNotBlank())
+            _ui.update { it.copy(message = R.string.example_error) }   //IDを取得し更新
        else
+            _ui.update { it.copy(message = R.string.example_success) } //IDを取得し更新
    }
}

UI側には

@Composable
fun ExampleScreen(){
    val uiState :ExampleUiState by viewModel.ui.collectAsState()
    Text(text = stringResource(uiState.message))   //ExampleUiStateのメッセージ(中身はStringResのID)を指定
}

実は・・・

自分はやったことがないのですが、ViewModelから直接strings.xmlの文字列を
読み込むことはできるようです。
ApplicationContextを使う方法です。
難しそうだったので、もっとスキルアップしたら使ってみたいと思います。
おとなしく、@StringResを使っておくのが簡単で( ・∀・)イイ!!

Discussion