ViewModel类应包含Android元素吗?

时间:2018-06-20 10:29:38

标签: android mvvm

从MVP迁移到MVVM,并尝试从网络教程中学习。

一些教程指出ViewModel类不应对Activity或View(android.view.View)类有任何引用。

但是在一些教程中,我已经看到Views用于ViewModel类和Activity中,以使用ViewModel启动其他Activity。 For example:

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");
        }
    }
}

Another oneView.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));
    }
}

Another one和活动参考

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());
    }
}
  1. ViewModel类包含Android和View类是否可以, 这样对单元测试不好吗?

  2. 自定义视图模型类应扩展哪个类? ViewModel或     BaseObservable/Observable

  3. 是否有任何教程链接显示MVVM的简单用法以及 只专注于没有任何Dagger2,LiveData或RxJava的体系结构 扩展名?我现在只在寻找MVVM教程。

1 个答案:

答案 0 :(得分:2)

来自documentation

  

警告:ViewModel绝不能引用视图,生命周期或任何可能包含对活动上下文的引用的类。

这是因为ViewModel保留了配置更改。假设您有一项活动,并且旋转设备。该活动被杀死,并创建了一个新实例。如果将视图放在视图模型中,那么该活动将不会被垃圾回收,因为这些视图持有对先前活动的引用。同样,视图本身将被重新创建,但是您将旧视图保留在viewmodel中。基本上,不要在视图模型中放置任何视图,上下文,活动。

如果您想使用展示带有存储库模式的MVVM的简单示例,则可以在gitlab上查看我的示例存储库:https://gitlab.com/rafid059/music-api-test

Google也提供了一个很好的示例:https://github.com/googlesamples/android-sunflower/