③ 实现登录
下面回到MainActivity中,增加代码如下图所示:
乍一看好像没啥不同的,无非就是给mainViewModel中的两个变量赋了值。不过这里有一个数据持久化的内容在里面,怎么证明呢?看一下下面这个GIF图
这个图可能有一些黑屏的地方,因为我在给自己的手机做横竖屏切换的时候,手机录屏好像有一点问题,不过没事。因为这个结果是对的,那就是数据持久化,因为我们知道手机在切换屏幕的时候Activity是会重新创建的,因此如果我们的数据是放在Activity中,那么切换屏幕之后就会重置,输入框也不会有值,但是通过ViewModel去保存输入框的值就不同了,虽然你的Activity在切换屏幕的时候销毁并且重新创建了,但是我的MainModel依然稳定,所以我才能在横屏的时候也登陆,这样不会造成数据丢失。
LiveData是用来做什么的?数据变化感知,也就是说如果我一个页面中对一个TextView进行多次赋值的话,可以通过LiveData来操作,只需要在值改变的时候进行设置就好了,可以简化页面上的代码。下面举一个实际的例子来说明。依然是之前那个登录页面,不过需要修改一下MainViewModel中的变量,如下:
① 可修改数据
public MutableLiveData account = new MutableLiveData<>();
public MutableLiveData pwd = new MutableLiveData<>();
请注意这里使用的是MutableLiveData,表示值的内容开变动,而LiveData是不可变的。<>中的是泛型,你可以直接将一个对象放进去,当对象的内容有改动时,通知改变就可以了,现在这么写是为了方便理解。下面进入MainActivity中,首先我们改变一下布局activity_main.xml在按钮的下面再加如下代码
<TextView
android:id=“@+id/tv_account”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”/>
<TextView
android:id=“@+id/tv_pwd”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”/>
② 数据观察
然后回到MainActivity中,修改代码如下图所示:
上面的图中从上往下有四处标注,我们从下面的两处标注来看,首先在给MainViewModel中的account赋值时,采用了MutableLiveData的setValue()的方式,还有一种方式是postValue(),这里要注意一点setValue()只能在主线程中调用,postValue()可以在任何线程中调用。pwd也是一样的,然后在最后一处标注的地方,对MainViewModel中的account和pwd进行数据观察,当这两个值有改变时通知页面最新的值,这里用了lambda表达式进行了一次简化,实际的代码是这样的。
下面我们运行一下:
Android的DataBinding在已经内置了,因此只需要在app模块的build.gradle中开启就可以使用了。DataBinding,顾名思义就是数据绑定,可以看到现在的三个组件都与数据有关系,ViewModel数据持有,LiveData数据观察、DataBinding数据绑定。
① 单向绑定
而DataBinding的绑定有两种方式:单向数据绑定和双向数据绑定。举个例子:比如我手机上收到一个通知,我需要显示通知的文字内容在页面上,这就是单向绑定,而我页面上的文字内容改变也重新发一个通知出去,这就是双向绑定。可以理解为A和B进行交互。A发消息,B要做出反应。B发消息,A也要相应改变。最常用的就是当我Model中的数据改变时,改变页面上的值。这个是单向绑定。下面我们可以新建一个用户User对象,代码如下:
public class User extends BaseObservable {
private String account;
private String pwd;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
notifyChange();//通知改变 所有参数改变
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public User(String account, String pwd) {
this.account = account;
this.pwd = pwd;
}
}
这里我继承了BaseObservable,注意它在androidx.databinding包下。然后我们的数据是需要显示在页面上的,而之前是通过Activity获取xml中的控件,然后显示数据在控件上,而现在有了DataBinding,可以直接和xml的中数据进行绑定,这看起来和JS比较像。下面我们对activity_main.xml进行改变。改变后代码如下:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”>
<variable
name=“user”
type=“com.llw.mvvm.User” />
<LinearLayout
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:gravity=“center”
android:orientation=“vertical”
android:padding=“32dp”
tools:context=“.MainActivity”>
<com.google.android.material.textfield.TextInputLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”>
<com.google.android.material.textfield.TextInputEditText
android:id=“@+id/et_account”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“@color/white”
android:hint=“账号” />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“12dp”>
<com.google.android.material.textfield.TextInputEditText
android:id=“@+id/et_pwd”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“@color/white”
android:hint=“密码”
android:inputType=“textPassword” />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id=“@+id/btn_login”
android:layout_width=“match_parent”
android:layout_height=“48dp”
android:layout_margin=“24dp”
android:insetTop=“0dp”
android:insetBottom=“0dp”
android:text=“登 录”
app:cornerRadius=“12dp” />
<TextView
android:id=“@+id/tv_account”
android:text=“@{user.account}”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”/>
<TextView
android:id=“@+id/tv_pwd”
android:text=“@{user.pwd}”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”/>
这里我在最外层加了一个layout标签,然后将原来的布局放在layout里面,再增加一个数据源,也就是user对象,然后再底部的两个tv_account和tv_pwd两个TextView中的text属性中绑定了user对象中的属性值。当然这样还没有完成,最后一步是在MainActivity中去进行绑定的。
进入MainActivity。在onCreate方法中,先将其他的代码注释掉。
然后修改待明日如下图所示
这里要注意一点,DataBindingUtil.setContentView返回的是一个ViewDataBinding对象,这个对象的生成取决于你的Activity,例如MainActivity,就会生成ActivityMainBinding。然后再通过生成的ActivityMainBinding去设置要显示在xml中控件的值。因此你会看到我完全没有去findViewById,然后控件再去设置这个setText。还有一点就是当你使用了DataBinding之后就不需要去手动findViewById了,通过编译时技术会生成驼峰命名的对象,如上图的btnLogin、etAccount、etPwd。上图的代码就是通过更改数据然后通知到xml做更改,初始化的修改时admin、123456。然后再通过输入框去修改。我将会输入study、666,然后点击登录按钮,也会将输入框的数据显示在TextView上,这样是否会省去很多不必要的繁琐工作呢?下面运行一下:
② 双向绑定
双向绑定是建立在单向绑定的基础上,实际的开发中用到双向绑定的地方并没有单向绑定多,双向绑定举一个例子,在输入框输入数据时候直接将数据源中的数据进行改变,这里会用到ViewModel和LiveData。下面进行双向绑定的使用,修改一下MainViewModel,代码如下:
public class MainViewModel extends ViewModel {
public MutableLiveData user;
public MutableLiveData getUser(){
if(user == null){
user = new MutableLiveData<>();
}
return user;
}
}
下面修改User类,这里面做了一些改变
public class User extends BaseObservable {
public String account;
public String pwd;
@Bindable
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
notifyPropertyChanged(BR.account);//只通知改变的参数
}
@Bindable
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
notifyPropertyChanged(BR.pwd);//只通知改变的参数
}
public User(String account, String pwd) {
this.account = account;
this.pwd = pwd;
}
}
不同于notifyChange()改变某一个参数,某一个对象都会通知,现在notifyPropertyChanged()就具有针对性,只通知对应属性改变。之前在activity_main.xml中的data标签中是使用的User,现在我们改成ViewModel,顺便把布局调整一下,代码如下:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”>
<variable
name=“viewModel”
type=“com.llw.mvvm.viewmodels.MainViewModel” />
<LinearLayout
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:gravity=“center”
android:orientation=“vertical”
android:padding=“32dp”
tools:context=“.MainActivity”>
<TextView
android:id=“@+id/tv_account”
android:text=“@{viewModel.user.account}”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”/>
<TextView
android:layout_marginBottom=“24dp”
android:id=“@+id/tv_pwd”
android:text=“@{viewModel.user.pwd}”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”/>
<com.google.android.material.textfield.TextInputLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”>
<com.google.android.material.textfield.TextInputEditText
android:id=“@+id/et_account”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“@color/white”
android:text=“@={viewModel.user.account}”
android:hint=“账号” />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“12dp”>
<com.google.android.material.textfield.TextInputEditText
android:id=“@+id/et_pwd”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“@color/white”
android:text=“@={viewModel.user.pwd}”
android:hint=“密码”
android:inputType=“textPassword” />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id=“@+id/btn_login”
android:layout_width=“match_parent”
android:layout_height=“48dp”
android:layout_margin=“24dp”
android:insetTop=“0dp”
android:insetBottom=“0dp”
android:text=“登 录”
app:cornerRadius=“12dp” />
这里要注意点的地方有几个,第一个是数据源,这里绑定的是ViewModel,那么相对应的ViewModel中的数据数据都可以拿到。
第二个就是响应的地方,通过这种方式去显示ViewModel中对象的变量数据在控件上。这里我把这两个TextView放到输入框的上方
第三个地方,也是双向绑定的意义,就是UI改变数据源。我们都知道当输入框输入时,text属性值会改变为输入的数据,而@={viewModel.user.account}就是将输入的数据直接赋值给数据源。这样在Activity中我们将不需要去进行输入框的处理,减少了耦合。
下面让我们回到MainActivity中。修改代码后如下:
private ActivityMainBinding dataBinding;
private MainViewModel mainViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//数据绑定视图
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mainViewModel = new MainViewModel();
//Model → View
User user = new User(“admin”, “123456”);
mainViewModel.getUser().setValue(user);
//获取观察对象
MutableLiveData user1 = mainViewModel.getUser();
user1.observe(this, user2 -> dataBinding.setViewModel(mainViewModel));
dataBinding.btnLogin.setOnClickListener(v -> {
if (mainViewModel.user.getValue().getAccount().isEmpty()) {
Toast.makeText(MainActivity.this, “请输入账号”, Toast.LENGTH_SHORT).show();
return;
}
if (mainViewModel.user.getValue().getPwd().isEmpty()) {
Toast.makeText(MainActivity.this, “请输入密码”, Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(MainActivity.this, “登录成功”, Toast.LENGTH_SHORT).show();
});
}
结尾
最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。
首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)
泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter
接下来是资料清单:(敲黑板!!!)
1.数据结构和算法
2.设计模式
3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记
4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)
不论遇到什么困难,都不应该成为我们放弃的理由!共勉~
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter
[外链图片转存中…(img-ful5u6cn-1714654424064)]
接下来是资料清单:(敲黑板!!!)
1.数据结构和算法
[外链图片转存中…(img-CbmJ6K8o-1714654424065)]
2.设计模式
[外链图片转存中…(img-lf6V08Ir-1714654424065)]
3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记
[外链图片转存中…(img-xgJjiGvL-1714654424065)]
4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)
[外链图片转存中…(img-f3AlggKv-1714654424066)]
不论遇到什么困难,都不应该成为我们放弃的理由!共勉~
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
[外链图片转存中…(img-AEf5519w-1714654424066)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!