嗨,我有一项 Activity LoginActivity.kt
和LoginViewModel
。我正在调用login
中的登录API方法LoginViewModel
。一旦成功,我想开始家庭 Activity 。在 MVVM 方法中正确的做法是什么?
LoginViewModel.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
}
}
登录 Activity .kt
class LoginActivity : BaseActivity(), LoginNavigator {
@Inject
lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
performDependencyInjection()
super.onCreate(savedInstanceState)
val activityLoginBinding: ActivityLoginBinding = DataBindingUtil.setContentView<ActivityLoginBinding>(this, R.layout.activity_login)
activityLoginBinding.loginViewModel = loginViewModel
loginViewModel.mNavigator = this
}
最佳答案
假设一个简单的场景,使用您的登录想法,用户登录失败,应用程序需要制作一个简单的Toast或SnackBar来显示相关信息,例如“您的用户名和密码不正确”。您需要的代码是
Toast(必需上下文
)
Toast.makeText(context, text, duration).show();
信息栏(必需的 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();
});
}
您将以相反的方式找到 Activity 的引用,这会使代码更加复杂并且难以维护,因为每次您需要获取 Activity 或上下文的引用来执行与 Activity 相关的操作时 View 模型中的 View 或 Activity 而不是 Activity
来自google sample ,您可以看到输入完成后调用 doSearch()
函数。获取搜索结果后,绑定(bind)会将最新结果返回给该观察者,现在是更新适配器中结果的 Activity 工作。
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的回答,你会看到他的论点
WeakReference to a NavigationController which holds the Context of the Activity. This is a common used pattern for handling context-bound stuff inside a ViewModel.
I highly decline this for several reasons. First: that usually means that you have to keep a reference to your NavigationController which fixes the context leak, but doesnt solve the architecture at all.
The best way (in my oppinion) is using LiveData which is lifecycle aware and can do all the wanted stuff.
如果您在 View 模型中实现 ui 操作,您可能会考虑另一个问题,如果您在 View 或上下文中收到 NullPointerException 或对其进行一些增强,您会首先找到哪个类? View 模型还是 Activity ?由于第一个操作是保存 UI 操作,第二个操作是保存数据绑定(bind)。在故障排除中这两种情况都可能发生。
关于android - 如何在 mvvm 模式中登录成功时从 viewmodel 启动 Activity ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47160241/