🎃
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