MVVM分为Model View ViewModel三层,网上介绍mvvm好处的有很多,这里不再叙述,直接开始正题
开发工具本人使用Android Studio3.6.3 ,开发语言使用Kotlin 进行开发
第一步创建如下项目结构
1.pngview = view层
viewmodel = viewmodel层
modelimpl = model层
第二步,在app下build.gradle文件里的android{}节点下添加以下代码,开启DataBinding
dataBinding{
enabled = true
}
第三步:编写登录布局以及Activity文件
activity_main
<?xml version="1.0" encoding="utf-8"?>
<!--总布局必须为小写layout -->
<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">
<!-- 此用处会在加载列表中详细说明-->
<data>
</data>
<!-- 这里放的是登录的布局-->
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 代码很简单,两个输入框一个按钮-->
<EditText
android:layout_margin="20dp"
android:id="@+id/etName"
android:hint="请输入账号"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:layout_margin="20dp"
android:id="@+id/etPass"
android:hint="请输入密码"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btLogin"
android:layout_margin="20dp"
android:text="登录"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
MainActivity 此处代码为 kotlin
class MainActivity : AppCompatActivity() {
//lateinit 关键词可以不初始化变量 类型是布局名字+Binding
lateinit var binding : ActivityMainBinding
val context = this
lateinit var viewmodel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initView()
onClick()
}
fun initView(){
/**
* 不太理解DataBinding的可以看 Android jetpack官方的文档
* 有了binding对象可以 通过binding.控件ID 来操作 不用在 实例化组件了
* 前提是必须执行第二步
*/
//R.layout.activity_main为 登录布局
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)//给binding赋值
viewmodel = MainViewModel(context,binding)//实例化viewmodel层,并给上下文 binding传过去
}
fun onClick(){
//设置点击方法,这里调用viewmodel层方法,viewmodel代码在后边会补全
//前面说过,可以通过 binding.控件id 来操作控件 这里给登录按钮设置点击事件
binding.btLogin.setOnClickListener {
viewmodel.login()//点击则调用viewmodel层login方法
}
}
第四步骤,编写登录viewmodel层
首先编写自定义接口,我这里新建一个包 mycallback
interface mALLCallBack {
/**
* 定义三个接口
* 开始调用 显示加载框loading
* 调用错误 提示错误信息
* 调用完成 隐藏加载框loading
*/
fun onStart()
fun onError(msg: String)
fun onComplete()
}
在编写viewmodel
class MainViewModel {
var context: Context
var binding: ActivityMainBinding
var modelImpl : MainModel
//构造方法
constructor(context:Context,binding: ActivityMainBinding){
this.context = context
this.binding = binding
modelImpl = MainModel()//这里给model加个 impl区分一下,可读性高一些
}
fun login(){
//由于view层 传了binding对象,所以我们直接用binding对象获取账号密码
val name = binding.etName.text.toString();
val pass = binding.etPass.text.toString();
if(name.equals("") || pass.equals("")){
Toast.makeText(context,"账号密码不能为空",Toast.LENGTH_LONG).show()
return
}
//调用model层login方法,请求网络数据,传参账号密码,并且实现回调接口
modelImpl.login(name, pass, object : mALLCallBack{
override fun onStart() {
//这里是开始请求接口 可以用于弹出loading
}
override fun onError(msg: String) {
//请求错误,显示错误信息
Toast.makeText(context,msg,Toast.LENGTH_LONG).show()
}
override fun onComplete() {
//请求完成,隐藏loading 并跳转到主页
context.startActivity(Intent(context, ListActivity().javaClass))
}
})
}
}
第五步骤,编写登录viewmodel层
由于这里要写接口调用,我这里先用死数据代替
class MainModel {
fun login(name: String, pass: String, call: mALLCallBack){
call.onStart()//请求接口
if(name.equals("admin") && pass.equals("admin")){//如果账号密码都是 admin则登录成功
call.onComplete() //表示调用完成 也就是登录成功
}else {
call.onError("账号密码错误")
}
}
}
网友评论