MVVM模式的概念
MVVM的意思就是model,view,viewmodel,跟MVC和MVP模式的区别就是数据业务处理逻辑全部放在viewmodel中,然后视图跟实体绑定,一方数据改变的话不用再去手动更新数据了,会自动更新的。
不管是MVVM还是MVC或者MVP,它们终究只是一种架构模式,我们了解了大概的样式后 ,就能去实现了,而不是一定要使用特定的技术,第三方库,不要把自己局限住了。
就像MVVM模式主要的概念是什么?第一个就是视图跟实体的绑定,可以省去我们很多findviewbyid这种代码,谷歌官方就提供了DataBinding这种工具供我们去实现视图跟实体的绑定。第二个就是数据业务处理逻辑,如果都放在activity中,activity就显得很臃肿,所以谷歌官方提供了ViewModel这个类让我们在里面处理数据。
谷歌官方提供了4大组件让我们去实现MVVM模式,分别是Databinding,ViewModel,Livedata,Lifecycle,但这个例子中我们只用Databinding,ViewModel,Livedata这3个,这3个组件单拎一个出来都能写一篇文章了,不了解这3个组件的可以去看看官网的文档或者找其它博客看一下,我们直接进入实战。
第一步
请在应用模块的 build.gradle
文件中启用 dataBinding
构建选项,如以下示例所示:
android {
...
buildFeatures {
dataBinding true
}
}
然后还得导入ViewModel,Livedata的依赖
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.3.1")
第二步
创建你所需的实体类,这是我之前做的demo,有用到了Room,大家可以选择加或不加。
@Entity
data class SearchHistory (
@PrimaryKey(autoGenerate = true)
var mId:Int,
var mSearchText:String
){
constructor():this(0,"")
}
第三步
把你的布局文件传成databinding格式,因为我们要用到recycleview,所以主布局和item布局都要转换,来到我们的xml布局下,对着xml的第一行,按下Alt + Enter,选择 “Convert to data binding layout”,就可以生成DataBinding的布局规则
转换完就是这种格式
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</RelativeLayout>
</layout>
第四步
来到activity里绑定布局文件,框架自动会生成DataBinding类,类名是:xml名称+Binding,databinding调用视图组件啥的,直接对象名.id名就可以了。id名也会变成驼峰形式。顺便把我们等等要用到的viewmodel和适配器都初始化了
class SearchActivity : AppCompatActivity() {
private lateinit var mBinding: ActivitySearchBinding
private lateinit var mHistoryViewModel: SearchHistoryViewModel
private lateinit var mSearchHistoryAdapter: SearchHistoryAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_search)
mHistoryViewModel = ViewModelProvider(this).get(SearchHistoryViewModel::class.java)
initRecyclerView()
//这里通过LivaData动态更新数据
mHistoryViewModel.getSearchHistoryList().observe(this, Observer {
mSearchHistoryAdapter.setData(it)//这里的it指的是getSearchHistoryList获得的list集合
})
mHistoryViewModel.loadSearchHistorys()
}
fun initRecyclerView() {
val searchHistoryListView = mBinding.rvSearchHistory
searchHistoryListView.layoutManager = GridLayoutManager(this, 4)
mSearchHistoryAdapter = SearchHistoryAdapter()
searchHistoryListView.adapter = mSearchHistoryAdapter
}
}
第五步
准备好ViewModel,我这里是用到了Room数据库存取数据,大家可以根据自己的需求去改。
class SearchHistoryViewModel: ViewModel() {
private val searchHistoryList: MutableLiveData<List<SearchHistory>> = MutableLiveData<List<SearchHistory>>()
fun getSearchHistoryList(): MutableLiveData<List<SearchHistory>> {
return searchHistoryList
}
fun addSearchHistory(searchText: String) {
var searchHistory = SearchHistory()
searchHistory.mSearchText = searchText
Thread{
MyApplication.getInstance().getDatabase().searchHistoryDao().insert(searchHistory)
}.start()
loadSearchHistorys()
}
fun deleteAllSearchHistorys() {
Thread{
MyApplication.getInstance().getDatabase().searchHistoryDao().deleteAll()
}.start()
loadSearchHistorys()
}
fun loadSearchHistorys() {
Thread{
val searchHistorys = MyApplication.getInstance().getDatabase().searchHistoryDao().getAll()
searchHistoryList.postValue(searchHistorys)
}.start()
}
}
第六步
准备好Adapter,可以看到之前我们在viewHolder类里面的时候要绑定一堆数据,不停地去findviewbyid,用了databinding后只需绑定一个对象就够了
class SearchHistoryAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var mSearchHistoryList: List<SearchHistory> = emptyList()
fun setData(searchHistoryList: List<SearchHistory>){
mSearchHistoryList = searchHistoryList
notifyDataSetChanged()
}
class viewHolder(private val binding: ItemSearchHistoryBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(searchHistory: SearchHistory){
binding.searchHistory = searchHistory
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val binding = DataBindingUtil.inflate<ItemSearchHistoryBinding>(
LayoutInflater.from(parent.context)
, R.layout.item_search_history, parent, false)
return viewHolder(binding)
}
override fun getItemCount(): Int {
return mSearchHistoryList?.size?: 0
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val viewholder = holder as viewHolder
val searchHistory = mSearchHistoryList[position]
viewholder.bind(searchHistory)
}
}
第七步
在item布局绑定数据,通过@{}去绑定,如果想双向绑定就加个=号,@={}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="searchHistory"
type="com.shun.entity.SearchHistory" />
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/padding_super_tiny">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:textColor="@color/common_text_gray"
android:layout_gravity="center"
android:text="@{searchHistory.MSearchText}"
android:textSize="@dimen/text_size_normal"
android:background="@drawable/selector_history"
android:id="@+id/tv_search_history"/>
</LinearLayout>
</layout>
想添加事件处理的话,可以去看我之前写的文章
我这个demo删了很多东西,所以是运行不了的,主要给大家看个思路。