ViewBinding、DataBinding、ViewModel上手

ViewBinding、DataBinding、ViewModel上手

未经授权,不得转载
open api:wanandroid

1. 开启 ViewBinding、DataBinding特性

// app#build.gradle#android
    buildFeatures {
        dataBinding true
        viewBinding true
    }

2. 添加依赖

// app#build.gradle#dependencies
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:latest'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:latest'

3. ViewBinding、DataBindg 上手

  • fragment_simple.xml,快捷键 alt+enter 可以快速转换为data binding layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <import type="lga.app.wahome.api.model.User" />

        <variable
            name="user"
            type="User" />

    </data>

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

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="simple ViewBinding + DataBinding" />

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="@{user.name}"
            tools:text="android" />

    </LinearLayout>
</layout>
  • SimpleFragment.kt
/**
 * simple ViewBinding + DataBinding
 */
class SimpleFragment : Fragment() {

    companion object {
        const val TAG = "fragment_home"

        @JvmStatic
        fun newInstance() = SimpleFragment()
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // ViewBinding
        val binding = FragmentSimpleBinding.inflate(inflater, container, false)

        binding.user = User("Kotlin")
        binding.lifecycleOwner = this

        return binding.root
    }
}

4. 进阶

  • 定义数据源:IArticleListDataSource.kt
interface IArticleListDataSource {
    suspend fun reqHomeArticleList(page: Int, count: Int): ApiResponse<ArticleList<Article>>
}
data class ApiResponse<T>(
    val `data`: T?,
    val errorCode: Int,
    val errorMsg: String
) {
        fun isSuccess() = errorCode == 0
}

实现数据源,从网络获取数据:ArticleListRemoteDataSource.kt

package lga.app.wahome.home.vm

import lga.app.wahome.api.RetrofitCreator
import lga.app.wahome.api.WanApiService

class ArticleListRemoteDataSource(apiCreator: RetrofitCreator) : IArticleListDataSource {

    private var service: WanApiService = apiCreator.wanApiService

    override suspend fun reqHomeArticleList(page: Int, count: Int) =
        service.reqHomeArticleList(page)
}
package lga.app.wahome.api

import lga.app.wahome.api.model.Article
import lga.app.wahome.api.model.ArticleList
import retrofit2.http.*

interface WanApiService {

    companion object {
        const val BASE_URL = "https://www.wanandroid.com/"
    }

    /**
     * 首页文章列表
     * https://www.wanandroid.com/article/list/0/json
     */
    @GET("article/list/{page}/json")
    suspend fun reqHomeArticleList(@Path("page") page: Int): ApiResponse<ArticleList<Article>>
}
  • 数据源封装到数据仓库:ArticleListRepository.kt
package lga.app.wahome.home.vm

class ArticleListRepository(private val dataSource: IArticleListDataSource) {

    suspend fun reqHomeArticleList(page: Int = 0, count: Int = 15) =
        dataSource.reqHomeArticleList(page, count)
}
  • ViewModel 实现:ArticleListViewModel.kt
package lga.app.wahome.home.vm

import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import lga.app.wahome.api.model.Article
import lga.app.wahome.home.ui.HomeFragment

class ArticleListViewModel(private val repo: ArticleListRepository) : ViewModel() {

    private val _articles = MutableLiveData<List<Article>>()
    val articles: LiveData<List<Article>> = _articles

    fun requestData(page: Int = 0, count: Int = 15) {
        viewModelScope.launch {
            val response = repo.reqHomeArticleList(page)
            if (response.isSuccess()) {
                val dataPack = response.data
                if (dataPack != null) {
                    val data = dataPack.datas
                    if (data != null) {
                        Log.d(HomeFragment.TAG, "${data.size}")

                        data.let { _articles.value = it }
                    } else {
                        Log.d(HomeFragment.TAG, "no data")
                    }
                } else {
                    Log.d(HomeFragment.TAG, "no data")
                }
            } else {
                Log.d(HomeFragment.TAG, "error")
            }
        }
    }
}
  • 自定义 Factory,方便传参:ArticleListVMFactory.kt
package lga.app.wahome.home.vm

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

class ArticleListVMFactory(private val repo: ArticleListRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T =
        ArticleListViewModel(repo) as T
}
  • ArticleListInjection.kt
package lga.app.wahome.home.vm

import lga.app.wahome.api.RetrofitCreator

object ArticleListInjection {

    private val dataSource = ArticleListRemoteDataSource(RetrofitCreator)
    private val repo = ArticleListRepository(dataSource)
    private val vmFactory = ArticleListVMFactory(repo)

    fun provideRepo(): IArticleListDataSource = dataSource

    fun provideVMFactory() = vmFactory
}

写到这里,ViewModel 骨架完成,下面写 UI

  • fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="ViewBinding + coroutine + ViewModel" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        tools:text="0" />

</LinearLayout>
  • HomeFragment.kt
package lga.app.wahome.home.ui

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import kotlinx.coroutines.*
import lga.app.wahome.databinding.FragmentHomeBinding
import lga.app.wahome.home.vm.ArticleListInjection
import lga.app.wahome.home.vm.ArticleListViewModel

/**
 * ViewBinding + coroutine + ViewModel
 */
class HomeFragment : Fragment(), CoroutineScope by MainScope() {

    companion object {
        const val TAG = "fragment_home"

        @JvmStatic
        fun newInstance() = HomeFragment()
    }

    private lateinit var binding: FragmentHomeBinding
    private lateinit var tv: TextView

    private val viewModel: ArticleListViewModel by viewModels { ArticleListInjection.provideVMFactory() }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // ViewBinding
        binding = FragmentHomeBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        atUISetup()
        atVMSetup()

        doRequestData()
    }

    private fun atUISetup() {
        tv = binding.tv
    }

    private fun atVMSetup() {
        viewModel.articles.observe(viewLifecycleOwner) {
            tv.text = it[0].title
        }
    }

    private fun doRequestData() {
        viewModel.requestData(0)
    }
}

5. 总结
上文给出了 simple ViewBinding + DataBinding,ViewBinding + coroutine + ViewModel
的示例,而 DataBinding + coroutine + ViewModel,ViewBinding + DataBinding + coroutine + ViewModel
的示例大同小异。此外,也不喜欢把业务逻辑与 xml 布局耦合到一块,如需了解全部示例,请点击传送门

未经授权,不得转载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
### 回答1: ViewBindingDataBinding都是Android开发中的数据绑定框架,它们可以将布局文件中的视图与代码中的变量绑定起来,从而方便地操作视图和数据。 ViewBindingAndroid官方在Android Studio 3.6中推出的一个新特性,它通过生成一个与布局文件对应的绑定类来实现视图绑定,可以避免findViewById的繁琐操作,提高代码的可读性和可维护性。 DataBinding是Google在Android Studio 1.3中推出的一个数据绑定框架,它可以将数据与视图绑定起来,实现双向绑定,从而简化了代码的编写和维护。使用DataBinding可以将视图的属性和数据模型的属性绑定起来,当数据模型的属性发生变化时,视图会自动更新,反之亦然。 总的来说,ViewBinding适用于简单的视图绑定场景,而DataBinding则适用于更复杂的数据绑定场景。 ### 回答2: ViewBindingDataBinding都是Android中的数据绑定库。它们旨在使应用程序中的 UI 和逻辑分离,同时还可以减少模板化代码。虽然它们都执行类似的任务,但它们之间存在一些区别。 首先,ViewBinding是针对单个视图文件生成绑定类。这样,在绑定代码中,您只能访问单个视图。因此,ViewBinding的代码量要比DataBinding少。 相比而言,DataBinding要复杂一些。 DataBinding不仅可用于绑定单个视图,还可用于绑定整个布局层次结构。它需要实现一整套工具来使数据绑定的代码与布局文件相对应。 其次,ViewBinding是使用annotation处理器生成绑定类的方式实现数据绑定,这意味着它可以相对快速地编译布局文件。另一方面,DataBinding需要生成额外的 Java 代码和一些 XML 文件来实现数据绑定,这使编译时间更长。 但是,DataBinding可以使用双向绑定技术,这意味着数据的变化可以直接反映到视图中。这使得在处理表单输入等特定场景时使用DataBinding会更容易一些。 总体而言,ViewBinding是更简单的选择,适合较小的项目,而DataBinding则更为复杂,适合需要大规模数据绑定的项目。它们都是可用的解决方案,您可以根据需求进行选择。 ### 回答3: ViewBindingDataBindingAndroid中的两种视图绑定库。两者的共同点是都可以通过自动生成的绑定类来实现视图的绑定,从而避免了findViewById的繁琐操作;不同点在于ViewBinding的目的是简化视图绑定,而DataBinding则可以实现更复杂的数据绑定和数据处理。 ViewBinding的主要作用是消除繁琐的findViewById操作,并且能够更好地保证类型安全。使用ViewBinding,我们只需要在需要进行视图绑定的Activity或Fragment中引入绑定类,然后通过绑定类实例来访问布局文件中所包含的所有控件。ViewBinding使用起来非常简单,只需要在build.gradle中添加如下配置即可: android { ... viewBinding { enabled = true } } 使用ViewBinding的代码示例: private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.textView.text = "Hello ViewBinding" } 对于DataBinding而言,它能够更好地实现数据绑定和数据处理,可以使用表达式将数据绑定到视图上,进行数据转换和格式化,从而实现更灵活的UI界面。使用DataBinding需要在build.gradle中添加如下配置: android { ... dataBinding { enabled = true } } 使用DataBinding的代码示例: <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.example.User" /> </data> <LinearLayout ... > <TextView ... android:text="@{user.name}" /> <TextView ... android:text="@{user.age,StringFormat}" /> <Button ... android:onClick="@{() -> user.onButtonClick()}" android:text="@{user.buttonText}" /> </LinearLayout> </layout> DataBinding可以更好地实现MVC中的数据与视图分离,也可以通过ViewModel和LiveData等技术实现MVVM模式。在使用DataBinding开发过程中,需要注意避免过于复杂的绑定表达式和数据转换等操作,以及注意内存泄漏的问题。 总的来说,ViewBindingDataBinding都是非常有用的Android开发库,可以根据具体的需求来选择使用哪种库,在提高开发效率的同时也能提升应用程序的性能。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-Jay-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值