前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MVVM项目实战之路-搭建一个登录界面

MVVM项目实战之路-搭建一个登录界面

作者头像
coderZhen
发布2018-06-28 16:56:16
2.2K0
发布2018-06-28 16:56:16
举报
文章被收录于专栏:Android开发经验

最近有一个新任务,给公司内部开发一个App,需求初始很简单,刚好之前一直在看MVVM这种开发模式,所以决定用Data Binding Library以及ViewModel等实现这种模式,因为代码和之前写法确实有很大不同,所以决定将这个app实现过程中这种新的模式的一些具体业务场景的代码写法用博客记录下来,希望对其他人有用。

本文基于Android Studio3.0

一.MVVM

首先,当然要先简单了解下什么是MVVM开发模式,来看一张图:

mvvm.png

代码语言:javascript
复制
可以看出,在MVVM中,我们的代码结构分为三层:
  • View 这里主要进行视图控件的一些初始设置,不应该有任何的数据逻辑操作
  • Model 在这层中,会定义我们的实体类,以及所有的业务逻辑操作,比如通过数据库或者网络来操作数据等都应该在这里进行
  • ViewModel 就像上图一样,ViewModel是连接View与Model的中间桥梁,ViewModel与Model直接交互,处理完业务逻辑后,通过DataBinding将数据变化反应到用户界面上。

MVVM的具体意义以及与MVC,MVP等的对比这里不再赘述。

二.实现一个登录界面

这里,我们先用这种模式实现一个登录界面,界面简单如下:

2.png

需求很简单:用户输入用户名,密码,点击登录按钮后调用接口进行检查,成功则跳转到下一个界面,失败则提示错误信息。

废话不多说,It`s time to show code!

  1. 启用DataBinding 首先,要保证你的Gradle插件版本要大于 1.5.0-alpha1及以上(现在基本都比这个版本高了吧),然后在app下的build.gradle文件添加以下代码:
代码语言:javascript
复制
dataBinding {
    enabled = true
}

2.添加ViewModel and LiveData

代码语言:javascript
复制
implementation "android.arch.lifecycle:extensions:1.0.0"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0"

3.登录页面activity_login.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="viewmodel"
            type="com.example.zq.mvvmdemo.user.LoginViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">


        <EditText
            android:layout_width="240dp"
            android:layout_height="40dp"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="100dp"
            android:hint="@string/login_name"
            android:text="@={viewmodel.loginName}" />

        <EditText
            android:layout_width="240dp"
            android:layout_height="40dp"
            android:layout_below="@+id/name"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="30dp"
            android:hint="@string/login_password"
            android:text="@={viewmodel.loginPass}" />

        <Button
            android:id="@+id/submit"
            android:layout_width="240dp"
            android:layout_height="40dp"
            android:layout_below="@+id/password"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="40dp"
            android:text="@string/login"
            android:textSize="16dp" />
    </LinearLayout>

</layout>

从这里开始,就会发现和我们之前的写法有很大的区别了,之前的xml文件根节点是LinearLayout或者RelativeLayout等布局,但是在使用DataBinding后,我们的xml文件可以概括成这样:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<layout >
    <data>
    .......
    </data>
    <LinearLayout>
        ........
        自己的布局
        ........
    </LinearLayout>
</layout>

最外层以layout标签包裹,里边用data标签表示我们要绑定的数据的名字以及类型,然后就是我们自己的布局。

之前我们已经知道,ViewModel是View与Model层交互的桥梁,所以具体用到的业务数据,比如这里用户名,密码等我这里全部放到了ViewModel中,然后将ViewModel与View进行绑定:

代码语言:javascript
复制
<data>
        <variable
            name="viewmodel"
            type="com.example.zq.mvvmdemo.user.LoginViewModel" />
 </data>

4.LoginViewModel文件

代码语言:javascript
复制
public class LoginViewModel extends AndroidViewModel {
    private static final String TAG = "LoginViewModel";

    private final SingleLiveEvent<String> mOpenUserList = new SingleLiveEvent<>();
    private final SnackbarMessage mSnackbarText = new SnackbarMessage();
    private final Context mContext; // To avoid leaks, this must be an Application Context.

    public final ObservableField<String> loginName = new ObservableField<>();
    public final ObservableField<String> loginPass = new ObservableField<>();

    public LoginViewModel(Application mContext) {
        super(mContext);
        this.mContext = mContext.getApplicationContext();
    }

    public SingleLiveEvent<String> getmOpenUserList() {
        return mOpenUserList;
    }

    SnackbarMessage getSnackbarMessage() {
        return mSnackbarText;
    }

    private void login(String loginName, String loginPass) {

        if (TextUtils.isEmpty(loginName)) {
            mSnackbarText.setValue(mContext.getString(R.string.login_name_not_input));
            return;
        }
        if (TextUtils.isEmpty(loginPass)) {
            mSnackbarText.setValue(mContext.getString(R.string.login_pass_not_input));
            return;
        }


        mOpenUserList.setValue("123456");

    }


    public void login() {
        login(loginName.get(), loginPass.get());
    }
}

首先我们来看loginName和loginPass这两个变量,ObservableField为DataBinding中提供的一个类,它使我们的对象变得可观测,即修改界面上的值,对应的loginName和loginPass的值就会改变,反之亦然。

再看mOpenUserList与mSnackbarText,它们是LiveData类型的,LiveData是一个数据持有类,并且在给定的生命周期中其变化是可观测的,这里用来处理ViewModels与 UI views (activities and fragments)的一些交互。

login()方法由点击登录按钮后触发,这里注意,因为loginName和loginPass已经与我们的视图文件绑定在一起了,所以就不用在调用的时候从EditText获取文本内容再传进来了。

getmOpenUserList()与getSnackbarMessage()将mOpenUserList与mSnackbarText公布给 UI views (activities and fragments),来处理一些交互,在这个例子里,主要是弹出提示以及跳转页面。

5.LoginActivity文件

代码语言:javascript
复制
public class LoginActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ViewModelFactory factory = ViewModelFactory.getInstance(getApplication());
        final LoginViewModel loginViewModel = ViewModelProviders.of(this, factory).get(LoginViewModel.class);

        final ActivityLoginBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
        binding.setViewmodel(loginViewModel);

        loginViewModel.getmOpenUserList().observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                Intent intent = new Intent(LoginActivity.this, UserListActivity.class);
                intent.putExtra("token", s);
                startActivity(intent);
                finish();
            }
        });

        loginViewModel.getSnackbarMessage().observe(this, new SnackbarMessage.SnackbarObserver() {
            @Override
            public void onNewMessage(String message) {
                SnackbarUtils.showSnackbar(binding.getRoot(), message);
            }
        });

        findViewById(R.id.submit).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginViewModel.login();
            }
        });
    }

}

这里就相当于View层,按照前面说的,这里应该只有一些界面的设置,不应该有任何的逻辑处理。ActivityLoginBinding是自动生成的,注意,在写完xml文件后要Build-Make Project一下,才会生成这个文件。

梳理一下逻辑:

点击登录按钮后,会调用LoginViewModel中的login()方法,进行参数的检查,如果参数不合法,为mSnackbarText设置对应的文案提示,因为我们在Activity已经监测了mSnackbarText的 变化,当它的值发生变化后,会通过回调通知回来,我们可以进行提示:

代码语言:javascript
复制
SnackbarUtils.showSnackbar(binding.getRoot(), message);

当参数全部合法后,改变mOpenUserList,同样会触发回调,进而跳转到下一个界面:

代码语言:javascript
复制
Intent intent = new Intent(LoginActivity.this, UserListActivity.class);
                intent.putExtra("token", s);
                startActivity(intent);
                finish();

以上就是用这种新的开发模式来完成这个登录需求的一个记录,在检查参数这里写的比较简单,其实应该用接口来检验,这又牵扯到一层封装,这里暂时先不写。可以看出,用这种模式写法与之前确实有很大不同,其中的一些问题可能描述的也不是很清楚,欢迎大佬给出建议和指正错误,会慢慢改正。

demo代码地址:https://github.com/SolveBugs/MVVMDemo

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017.12.28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档