android - MVVM 注入 Activity 字符串资源阅读器
问题描述
因此,每个人都知道将 Context 引用(不是 Application)传递给ViewModel
是一件坏事。就我而言,有些项目需要使用 android 字符串资源表示按字母顺序排序(因此,要阅读它,我需要一个 Activity 上下文)。
推荐的方法是什么?将项目列表从 Activity 传递ViewModel
给 Activity 以读取这些字符串并返回到ViewModel
看起来确实有点不这样MVVM-ish
,并且注入ViewModel
字符串资源读取器会泄漏 Context..
对此有什么想法吗?
解决方案
一种选择是从AndroidViewModel扩展,它具有对应用程序上下文的引用。然后,您可以使用它Context
来加载字符串资源并将它们传送回您的Activity
.
public class MyViewModel extends AndroidViewModel {
private final LiveData<String> stringResource = new MutableLiveData<>();
public MyViewModel(Application context) {
super(context);
statusLabel.setValue(context.getString(R.string.labelString));
}
public LiveData<String> getStringResource() {
return stringResource;
}
}
但是,正如Jose Alcerreca在这篇 Android Developers Medium Post 中指出的那样,这不是推荐的做法,因为例如,如果Locale
更改和 Activity 被重建,ViewModel 将不会对此配置更改做出反应并继续交付过时的字符串(来自上一个Locale
)。
因此,建议的方法是仅从 ViewModel 返回资源 id 并获取 Activity 上的字符串。
public class MyViewModel extends ViewModel {
public final LiveData<Integer> stringResource = new MutableLiveData<>();
public MyViewModel(Application context) {
super(context);
stringResource.setValue(R.string.labelString);
}
public LiveData<Integer> getStringResource() {
return stringResource;
}
}
更新
由于您必须从您的字符串资源中获取字符串资源,Activity
但在您的中应用排序逻辑ViewModel
,我认为您不能避免将List<String>
返回传递给您ViewModel
进行排序:
public class MyViewModel extends ViewModel {
public final MutableLiveData<Integer> stringArrayId = new MutableLiveData<>();
public MyViewModel(Application context) {
super(context);
stringArrayId.setValue(R.array.string_array_id);
}
public LiveData<Integer> getStringArrayId() {
return stringArrayId;
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
viewModel.getStringArrayId().observe(this, strArrayId -> {
String[] resolvedStrings = getResources().getStringArray(strArrayId);
List<String> sortedStrings = viewModel.sortList(Arrays.asList(resolvedStrings));
updateUi(sortedStrings);
});
}
}
如果您认为这还不够 MVVM'ish,也许您可以继续解决List<String>
您的问题,ViewModel
并在排序列表中有一个额外LiveData
的内容,每次LiveData
保存原始字符串列表更改时都会更新。
public class MyViewModel extends ViewModel {
public final MutableLiveData<Integer> stringArrayId = new MutableLiveData<>();
public final MutableLiveData<List<String>> stringsList = new MutableLiveData<>();
public final LiveData<List<String>> sortedStringList;
public MyViewModel(Application context) {
super(context);
stringArrayId.setValue(R.array.string_array_id);
sortedStringList = Transformations.map(stringsList, l -> {
Collections.sort(l);
return l;
});
}
public LiveData<Integer> getStringArrayId() {
return stringArrayId;
}
public LiveData<List<String>> sortedStringList() {
return sortedStringList;
}
public void setStringsList(List<String> resolvedStrings) {
stringsList.setValue(resolvedStrings);
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
viewModel.getStringArrayId().observe(this, strArrayId -> {
String[] resolvedStrings = getResources().getStringArray(strArrayId);
viewModel.setStringsList(Arrays.asList(resolvedStrings));
});
viewModel.sortedStringList().observe(this, sortedStrings -> updateUi(sortedStrings));
}
}
对我来说感觉设计过度了,你仍然需要将它发送List<String>
回你的ViewModel
. 但是,如果排序顺序取决于可以在运行时更改的过滤器,那么采用这种方式可能会有所帮助。然后,您可以添加一个MediatorLiveData
以在过滤器更改或字符串列表更改时做出反应,然后您的视图只需将这些更改通知给ViewModel
并且将观察排序列表。
推荐阅读
- spring - 如果 bean 类没有任何 getter/setter 方法,则使用和注入 Spring bean?
- android - 为 arm64 Android 手机构建应用程序包时在 ApplicationInfo.nativeLibraryDir 中找不到本机库
- php - 更改 PHP 编译
- laravel - Laravel 5.8 - 一个路由两个不同的控制器动作
- javascript - material-ui useScrollTrigger 与子目标 ref 的用途是什么?
- python - 如何确保在删除某些元素时检查列表中的每个元素
- typescript - 使用 querySelectorAll 时,Typescript 对象可能为 'null'.ts(2531)
- javascript - 寻找一个简单的文本模板引擎来生成查询
- python - 从循环内部获取迭代器对象
- c#-4.0 - 处理多个子目录和每个目录下的所有文件