- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我是 MVP 爱好者,但同时我思想开放,我正在努力提高我对 MVVM 和数据绑定(bind)的了解:
我在这里 fork 了https://github.com/jpgpuyo/MVPvsMVVM
原始存储库 https://github.com/florina-muntenescu/MVPvsMVVM来自@FMuntenescu
我创建了几个分支。在其中一个中,我想根据按钮上执行的点击次数显示具有不同样式的 2 个不同的警报对话框:
您可以在这里找到分支机构: https://github.com/jpgpuyo/MVPvsMVVM/tree/multiple_dialogs_databinding_different_style
我在 View 模型中创建了 2 个可观察字段,并添加了 1 个绑定(bind)适配器。
Activity :
private void setupViews() {
buttonGreeting = findViewById(R.id.buttonGreeting);
buttonGreeting.setOnClickListener(v -> mViewModel.onGreetingClicked());
}
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
app:greetingType="@{viewModel.greetingType}"
app:greetingMessage="@{viewModel.greetingMessage}">
View 模型:
public ObservableField<String> greetingMessage = new ObservableField<>();
public ObservableField<GreetingType> greetingType = new ObservableField<>();
public void onGreetingClicked() {
numberOfClicks++;
if (numberOfClicks % 2 == 0) {
mSubscription.add(mDataModel.getStandardGreeting()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(greeting -> {
greetingMessage.set(greeting);
greetingType.set(GreetingType.STANDARD);
}));
} else {
mSubscription.add(mDataModel.getDroidconGreeting()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(greeting -> {
greetingMessage.set(greeting);
greetingType.set(GreetingType.DROIDCON);
}));
}
}
MVVMBindingAdapter:
@BindingAdapter({"greetingType", "greetingMessage"})
public static void showAlertDialog(View view, GreetingType greetingType,
String greetingMessage) {
if (GreetingType.STANDARD.equals(greetingType)){
new DialogHelper().showStandardGreetingDialog(view.getContext(),
greetingMessage, greetingMessage);
} else if(GreetingType.DROIDCON.equals(greetingType)) {
new DialogHelper().showDroidconGreetingDialog(view.getContext(),
greetingMessage, greetingMessage);
}
}
使用 MVVM,不确定如何实现它以通过 java 单元测试进行完全可测试。我创建了一个绑定(bind)适配器,但是:
我需要在绑定(bind)适配器中使用 if/else 来显示一个或另一个对话框。
我不知道如何将对话框助手注入(inject)绑定(bind)适配器,因此无法使用单元测试进行验证,除非使用 powermock。
我为每个对话框添加了不同的样式,因为如果我不添加样式,我们可以认为对话框的标题和消息是从数据层检索的,但考虑到 android 样式来自数据会很奇怪层。
是否可以在 MVVM 中注入(inject)一个对话框助手来解决这个问题并使代码可测试?
哪一种是使用 MVVM 管理警报对话框的最佳方式?
最佳答案
我用于MVVM的解决方案是混合的,如下。
来自Medium帖子中提到的Jose Alcérreca的文章LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)在对 Show Dialog from ViewModel in Android MVVM Architecture 的 SO 答复中提到,我选择第四个选项“推荐:使用事件包装器”。原因是如果需要的话我可以查看消息。另外,我还从 this comment in Jose's Gist 添加了 observeEvent()
扩展方法。 .
我的最终代码是:
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
* See:
* https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
* https://gist.github.com/JoseAlcerreca/e0bba240d9b3cffa258777f12e5c0ae9
*/
open class LiveDataEvent<out T>(private val content: T) {
@Suppress("MemberVisibilityCanBePrivate")
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
inline fun <T> LiveData<LiveDataEvent<T>>.observeEvent(owner: LifecycleOwner, crossinline onEventUnhandledContent: (T) -> Unit) {
observe(owner, Observer { it?.getContentIfNotHandled()?.let(onEventUnhandledContent) })
}
用法如下(我的例子是数据同步完成时触发事件):
class ExampleViewModel() : ViewModel() {
private val _synchronizationResult = MutableLiveData<LiveDataEvent<SyncUseCase.Result>>()
val synchronizationResult: LiveData<LiveDataEvent<SyncUseCase.Result>> = _synchronizationResult
fun synchronize() {
// do stuff...
// ... when done we get "result"
_synchronizationResult.value = LiveDataEvent(result)
}
}
并通过使用 observeEvent()
来使用它,以获得漂亮、简洁的代码:
exampleViewModel.synchronizationResult.observeEvent(this) { result ->
// We will be delivered "result" only once per change
}
关于android - MVP 与 MVVM : how to manage alert dialogs in MVVM and improve testability,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56892366/
请注意,这并非特定于 Protractor。问题在于 Angular 2 的内置 Testability service Protractor 碰巧使用。 Protractor 调用 Testabil
我随机得到错误: Failed: Error while waiting for Protractor to sync with the page: "both angularJS testabili
我刚刚向新项目添加了一些单元测试。通常我使用 @testable import 来导入生产代码,所以我可以测试非公共(public)方法: @testable import My-Project im
我收到一条错误消息: "Error while waiting for Protractor to synce with the page: "Cannot read property '$$test
我有一个用户定义的变量“MODULE_NAME_WITH_SUFFIX”,它在每个模式中都不同。 现在我需要在我的测试中导入这个模块名称,但不知道如何导入。 在我进行简单导入之前: @testable
我现在已经在几个项目中遇到过这个问题,所以很想找到一个好的解决方案。 考虑以下场景: 我在我的应用程序目标中定义了一个名为 MyObject 的对象,它在 MyBusinessLogicObject
我有一个应用程序需要引用一组引用代码(一个 3 个字符的代码及其相关的简短描述)。数据不会改变——或者至少我过去从未知道它会改变——但我仍然无法将其硬编码到应用程序代码中。 我最初的想法是创建一个静态
“@testable import”似乎没有导入原始项目中的所有文件。外部框架的文件作为单独的文件添加到原始项目中,除非检查“目标成员资格”以进行单元测试,否则测试用例似乎无法访问它们。奇怪的是,单元
我尝试在 Linux Ubuntu 16.04 上向我的 Swift 项目添加单元测试。现在我有了目录结构: MyProject |-Sources | └MyProject | |-IPcalc.
我一直在开发 android 应用程序,但没有编写任何单元测试。最近我开始了解它并尝试使用 JUnit 来测试我的 android 应用程序。 我发现大多数时候我在 API 调用中遇到错误,但我仍然不
我有一个混合了 Obj-C 和 Swift 的项目,我在让我的单元测试正常工作时遇到了一些问题。 我正在使用 @testable import moduleName 指令导入我的文件,但它似乎没有导入
我知道 @testable import MyModule 提供从“测试”(使用“testTarget”构建)模块 MyModuleTests 探索 MyModule 的非公共(public)成员的能
我正在编写一个简单的 P2P 应用程序来测试在更大的项目中使用 UDP 打洞的可行性。 我昨天在家试用了我的测试应用程序,它们运行良好。 但是,我现在在工作,相同的代码不再能胜任这项工作。发件人正在发
我正在尝试使用 Swift 的 @testable 声明将我的类暴露给测试目标。但是我收到了这个编译器错误: Intervals 是包含我要公开的类的模块。我该如何摆脱这个错误? 最佳答案 在您的主要
我创建了一个没有单元测试的 Xcode 项目。当我尝试创建新的单元测试并尝试导入 @testable import 'ProjectName' 时,我添加了 cocoa pod ,它给出了错误无法加载
您使用什么样的实践来使您的代码对单元测试更加友好? 最佳答案 TDD——首先编写测试,强制你要考虑可测试性和帮助编写实际的代码需要的,而不是你认为可能的需要 接口(interface)重构——使得 m
当我更新此字段中的显示名称时 我所有的测试都失败了,因为 @testable import HomeApp 需要更新才能匹配。 我一直觉得 Display Name 只是出现在您的应用程序和其他一些地
正如我多次看到的那样,一个自执行的匿名函数用于包含整个库。如何测试这些库,例如QUnit 无法访问匿名函数包装器内的任何内容? 最佳答案 我同意你不想执行 Backdoor Manipulation通
直到现在,我习惯于在 ready() 函数中编写所有代码,例如: $(document).ready(function() { // all my code }); 现在我看到使用这种方法时,我的
据说 "static methods are death to testability" 。如果是这样,下面的可行替代模式是什么? class User { private $phone,
我是一名优秀的程序员,十分优秀!