您好我有一项活动LoginActivity.kt
和LoginViewModel
。我在login
的{{1}}方法中调用了登录API。一旦成功,我想开始家庭活动。在MVVM方法中这样做的正确方法是什么?
LoginViewModel.kt
LoginViewModel
LoginActivity.kt
class LoginViewModel : BaseViewModel<LoginNavigator>(), AnkoLogger {
val emailField = ObservableField<String>()
private val email: String
get() = emailField.get()
val passwordField = ObservableField<String>()
private val password: String
get() = passwordField.get()
val progressVisibility: ObservableInt = ObservableInt(View.GONE)
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
fun login(view: View) {
// here I am calling API and on success
}
/**
* Validate email and password. It checks email and password is empty or not
* and validate email address is correct or not
* @param email email address for login
* @param password password for login
* @return true if email and password pass all conditions else false
*/
private fun isEmailAndPasswordValid(email: String, password: String): Boolean {
if (email.isEmpty()) return false
if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) return false
if (password.isEmpty()) return false
return true
}
}
答案 0 :(得分:2)
假设一个简单的场景使用您的登录想法,用户登录失败,应用程序需要制作一个简单的Toast或SnackBar来显示相关信息,例如&#34;您的用户名和密码不正确&#34;。您需要的代码是
Toast(必需Context
)
Toast.makeText(context, text, duration).show();
Snackbar(必需View
)
Snackbar.make(findViewById(R.id.myCoordinatorLayout),
R.string.email_archived, Snackbar.LENGTH_SHORT);
如果你想在你的ViewModel中使用它(我不熟悉Kotlin)
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
void function login(final View view) {
// here I am calling API and on success
repo.login(result -> {
if(result.statusCode == 401)
Toast.makeText(view.getContext(), "Login failed...", duration).show();
});
}
您将以相反的方式找到活动的引用,这会产生更复杂的代码并且难以维护,因为每次您需要获取活动或上下文的引用以执行与视图模型中的视图或活动,而不是活动
从google sample开始,您可以看到输入完成后调用doSearch()
函数。在获取搜索结果后,绑定会将最新结果返回给此观察者,现在是更新适配器中结果的活动作业。
private void initSearchInputListener() {
binding.get().input.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
doSearch(v);
return true;
}
return false;
});
binding.get().input.setOnKeyListener((v, keyCode, event) -> {
if ((event.getAction() == KeyEvent.ACTION_DOWN)
&& (keyCode == KeyEvent.KEYCODE_ENTER)) {
doSearch(v);
return true;
}
return false;
});
}
private void doSearch(View v) {
String query = binding.get().input.getText().toString();
// Dismiss keyboard
dismissKeyboard(v.getWindowToken());
binding.get().setQuery(query);
searchViewModel.setQuery(query);
}
private void initRecyclerView() {
binding.get().repoList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
LinearLayoutManager layoutManager = (LinearLayoutManager)
recyclerView.getLayoutManager();
int lastPosition = layoutManager
.findLastVisibleItemPosition();
if (lastPosition == adapter.get().getItemCount() - 1) {
searchViewModel.loadNextPage();
}
}
});
searchViewModel.getResults().observe(this, result -> {
binding.get().setSearchResource(result);
binding.get().setResultCount((result == null || result.data == null)
? 0 : result.data.size());
adapter.get().replace(result == null ? null : result.data);
binding.get().executePendingBindings();
});
searchViewModel.getLoadMoreStatus().observe(this, loadingMore -> {
if (loadingMore == null) {
binding.get().setLoadingMore(false);
} else {
binding.get().setLoadingMore(loadingMore.isRunning());
String error = loadingMore.getErrorMessageIfNotHandled();
if (error != null) {
Snackbar.make(binding.get().loadMoreBar, error, Snackbar.LENGTH_LONG).show();
}
}
binding.get().executePendingBindings();
});
}
另外,根据@Emanuel S的回答,你会看到他的论点
对包含上下文的NavigationController的WeakReference 活动。这是处理上下文绑定的常用模式 ViewModel中的东西。
我高度拒绝这一点有几个原因。第一:通常意味着 你必须保持对NavigationController的引用 修复了上下文泄漏,但根本没有解决这个问题。
最好的方式(在我看来)是使用LiveData这是生命周期 意识到并可以做所有想要的东西。
如果你在viewmodel中实现ui动作,你可以考虑另一个问题,如果你在视图或上下文中得到NullPointerException或者对它进行一些增强,你会先找到哪个类? ViewModel还是Activity?从第一个拿着UI动作,第二个拿着数据绑定。在故障排除中两者都有可能。