android - ViewModel 类是否应该包含 Android 元素?
问题描述
从 MVP 迁移到 MVVM 并尝试从网络教程中学习。
一些教程指出 ViewModel 类不应该对 Activity 或 View(android.view.View) 类有任何引用。
但在一些教程中,我看到 ViewModel 类和活动中使用视图来使用 ViewModel 启动其他活动。 例如:
import android.arch.lifecycle.ViewModel;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import com.journaldev.androidmvvmbasics.interfaces.LoginResultCallback;
import com.journaldev.androidmvvmbasics.model.User;
public class LoginViewModel extends ViewModel {
private User user;
private LoginResultCallback mDataListener;
LoginViewModel(@NonNull final LoginResultCallback loginDataListener) {
mDataListener = loginDataListener;
user = new User("", "");
}
public TextWatcher getEmailTextWatcher() {
return new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
user.setEmail(editable.toString());
}
};
}
public TextWatcher getPasswordTextWatcher() {
return new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
user.setPassword(editable.toString());
}
};
}
public void onLoginClicked(@NonNull final View view) {
checkDataValidity();
}
private void checkDataValidity() {
if (user.isInputDataValid())
mDataListener.onSuccess("Login was successful");
else {
mDataListener.onError("Email or Password not valid");
}
}
}
另一个与View.OnClickListener
public class PostViewModel extends BaseObservable {
private Context context;
private Post post;
private Boolean isUserPosts;
public PostViewModel(Context context, Post post, boolean isUserPosts) {
this.context = context;
this.post = post;
this.isUserPosts = isUserPosts;
}
public String getPostScore() {
return String.valueOf(post.score) + context.getString(R.string.story_points);
}
public String getPostTitle() {
return post.title;
}
public Spannable getPostAuthor() {
String author = context.getString(R.string.text_post_author, post.by);
SpannableString content = new SpannableString(author);
int index = author.indexOf(post.by);
if (!isUserPosts) content.setSpan(new UnderlineSpan(), index, post.by.length() + index, 0);
return content;
}
public int getCommentsVisibility() {
return post.postType == Post.PostType.STORY && post.kids == null ? View.GONE : View.VISIBLE;
}
public View.OnClickListener onClickPost() {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
Post.PostType postType = post.postType;
if (postType == Post.PostType.JOB || postType == Post.PostType.STORY) {
launchStoryActivity();
} else if (postType == Post.PostType.ASK) {
launchCommentsActivity();
}
}
};
}
public View.OnClickListener onClickAuthor() {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
context.startActivity(UserActivity.getStartIntent(context, post.by));
}
};
}
public View.OnClickListener onClickComments() {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
launchCommentsActivity();
}
};
}
private void launchStoryActivity() {
context.startActivity(ViewStoryActivity.getStartIntent(context, post));
}
private void launchCommentsActivity() {
context.startActivity(CommentsActivity.getStartIntent(context, post));
}
}
另一个带有活动参考的
public class UserProfileViewModel {
/* ------------------------------ Constructor */
private Activity activity;
/* ------------------------------ Constructor */
UserProfileViewModel(@NonNull Activity activity) {
this.activity = activity;
}
/* ------------------------------ Main method */
/**
* On profile image clicked
*
* @param userName name of user
*/
public void onProfileImageClicked(@NonNull String userName) {
Bundle bundle = new Bundle();
bundle.putString("USERNAME", userName);
Intent intent = new Intent(activity, UserDetailActivity.class);
intent.putExtras(bundle);
activity.startActivity(intent);
}
/**
* @param editable editable
* @param userProfileModel the model of user profile
*/
public void userNameTextChange(@NonNull Editable editable,
@NonNull UserProfileModel userProfileModel) {
userProfileModel.setUserName(editable.toString());
Log.e("ViewModel", userProfileModel.getUserName());
}
}
ViewModel 类可以包含 Android 和 View 类,这对单元测试来说不是很糟糕吗?
自定义视图模型类应该扩展哪个类?
ViewModel
还是BaseObservable/Observable
?- 是否有任何教程链接显示 MVVM 的简单用法,并且只关注没有任何 Dagger2、LiveData 或 RxJava 扩展的架构?我现在只在寻找 MVVM 教程。
解决方案
从文档:
注意:ViewModel 绝不能引用视图、生命周期或任何可能持有对活动上下文的引用的类。
这是因为ViewModel
配置更改仍然存在。假设您有一个活动并且您旋转设备。该活动被杀死并创建一个新实例。如果您将视图放在视图模型中,那么该活动将不会被垃圾收集,因为视图持有对前一个活动的引用。此外,视图本身将被重新创建,但您将旧视图保留在视图模型中。基本上不要在视图模型中放置任何视图、上下文、活动。
这是来自谷歌的示例:https ://github.com/googlesamples/android-sunflower/
推荐阅读
- gitlab - 本地运行器文件存储在哪里?
- javascript - 您如何在带有反应的嵌入中创建用户列表?[不和谐.js]
- tensorflow - LSTM输入形状错误:输入0与layersequential_1不兼容
- asp.net - 从 postgresql 9.x 升级到 postgresql 13 后,Npgsql 无法与 PostgreSQL 对话
- oracle-apex - Oracle Apex 20 - 交互式报表在刷新时不显示任何行
- python - 如何创建字符串过滤脚本?
- c# - 如何使用 Windows 窗体应用程序在 C# 中的另一种方法中使用 Streamreader 数据
- web-scraping - 从动态 Highcharts 可视化中抓取数据
- node.js - req.user.displayname 未定义 Nodejs + passport = google oauth
- django - fake.date_between 总是在循环中返回相同的日期