认识Jetpack(上):

目录

Jetpack的基本知识:

1.Android开发架构的发展历程:

1.1:MVC:

1.2:MVP:

1.3:MVVM:

2.什么是Jetpack:

3.如何构建Jetpack项目:

Lifecycle:

1.从广告引导页的需求说起:

2.基本使用:

写法1:

写法2:Lifecycle

LifecycleOwner:

补充:

ViewModel:

1.屏幕旋转保存数据:

写法1:重写onSavelnstanceState方法

写法2:ViewModel

注意:

2.向ViewModel传递参数:

3.使用ViewModel实现数据共享:

LiveData:

1.基本使用:

缺陷:

2.map和switchmap:

2.1:map方法:

2.2:switchMap方法:

3.补充:

ViewBinding:

1.从findViewById说起:

2.基本使用:

3.总结:

DataBinding:

1.基本使用:

1.1:基础布局绑定表达式:

写法1:

写法2:DataBinding

1.2:利用DataBinding绑定点击事件:

写法1:

写法2:DataBinding

1.3:标签布局使用DataBinding:

2.ViewBinding和DataBinding的区别:


Jetpack的基本知识:

1.Android开发架构的发展历程:

互联网技术日新月异,越来越多优秀的开发工程师开始追寻更高效率的开发模式,因此,不断涌现出新的软件开发模式,其中MVC、MVP以及MVVM这三种模式一直是软件行业争论的焦点。下面就分别来看一下这三种开发模式在Android应用开发中是如何应用的吧。

1.1:MVC:

MVC的全称是Model-View-Controller即模型-视图一控制器,Model负责数据的管理,View负责UI的显示,Controller负责逻辑控制。在Android中充当视图层角色的是各种xml文件,充当逻辑控制层角色的是Activity或者Fragment,充当模型层的是网络请求等部分

由于XML的能力较弱,在实际项目中数据设置一般都是在Activity或Fragment中完成的,因此导致Activity既充当了Controller层又充当了View层,且Controller层需要调用Model层获取数据,从而导致绝大多数的任务都是在Controller中完完成的,这也就使得Controller层不易维护,因为Model层与View层耦合性较高,容易牵一发而动全身。 

1.2:MVP:

MVP的全称是Model-View-Presenter, Model负责数据的管理,View负责UI的显示,Presenter负责逻辑控制,但是与MVC不同的是,MVP改变了通信方向,View层和Model层不再直接通信,而是通过Presenter层作为“中间人”。

View层产生事件,通知Presenter层,Presenter层则通知Model层更新数据,Model层更新数据后,返回并通知Presenter层,Presenter层再通知View层更新界面。MVP相比于MVC的好处显而易见,即将View层与Model层解耦,使得每一层的职责更清晰、明确。但MVP作为“中间人”,需要借助接口回调的方式转发消息,从而导致接口类文件增多,且实现类无法避免许多无用的空实现。

1.3:MVVM:

其实MVP已经算是一种很好的开发模式了,MVVM模式则相当于MVP的一种改进版本,MVVM的全称是Model-View-ViewModel,要注意的是,这里的ViewModel并不能直接与Jetpack中的ViewModel组件划等号。ViewModel中有一个Binder,在不同系统的MVVM开发模式中对Binder有不同的实现,比如前端开发中的Vue.js或iOS开发中的RAC,而在Android开发中充当Binder角色的则是Jetpack组件中的DataBinding,Binder的作用就是替代MVP中Presenter层的“中间人”角色。此模式会将View和ViewModel层完全解耦,从而使得职责划分更清晰。

MVVM开发模式是当前Google最推荐的开发模式,为了便于使用MVVM开发模式,Google还打造了一套工具集——Jetpack。 

2.什么是Jetpack:

按照Google官方的介绍,Jetpack是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可以在各种Android版本和设备中一致运行的代码,这样开发者就可以集中精力编写重要的代码了。早在2017年的时候,Google就推出了一系列架构组件,称为ArchitectureComponents,并于2018年在Google I/O大会上提出Jetpack,且将Architecture Components纳入其中,时至今日,越来越多的组件,如room,paging3等等也被纳入其中。Jetpack主要分为基础,架构组件,行为,页面这四个模块

3.如何构建Jetpack项目:

Jetpack所有的库都是发布在AndroidX下面的,所以我们只需要新建支持AndroidX的项目就可以在项目中引用任意的Jetpack组件了。那什么是AndroidX呢?相信每个Android开发人员都使用过support-v4和appcompat-v7支持库,这两种支持库是Android早期为了解决新版API的向后兼容问题而发布的,但是Google随后意识到这种包含v4、v7版本号的命名方式已经不合时宜,因此推出了AndroidX,将所有API的包名都统一为androidx.*的方式,AndroidX不仅提供与支持库同等的功能,而且还提供了新的库,28compileSdkVersion的编译版本不能低于API28。
gradle.properties中的android.useAndroidX属性必须存在且值为true,这Android插件才会使用对应的AndroidX库,而非支持库。如果未指定,那该标志默认为false。
新建项目成功后,就可以在项目中使用Jetpack的组件库了。.0.0是支持库的最后一个版本。Google将不再发布android.support库版本,因此对于开发者来说,使用AndroidX替代支持库是或早或晚的事情,接下来我们一起来看如何新建支持AndroidX的项目。从Android Studio 3.4版本开始,新建的项目已经默认勾选使用AndroidX了,但是可以勾选使用Android支持库,不过,这会影响使用最新的服务和Jetpack库,所以这里不用勾选Use legacy android.support libraries选项。

如果想要更改项目中的配置,有两点需要注意的,避免影响使用AndroidX:

  1. compileSdkVersion的编译版本不能低于API28。
  2. gradle.properties中的android.useAndroidX属性必须存在且值为true,这Android插件才会使用对应的AndroidX库,而非支持库。如果未指定,那该标志默认为false。

新建项目成功后,就可以在项目中使用Jetpack的组件库了。

Lifecycle:

感知activity的生命周期并不复杂,在一个activity中感知它的生命周期非常简单,而如果要在一个非activity的类中去感知activity的生命周期,我们除了可以在activity中嵌入一个fragment进行感知,或者通过手写监听器的方式进行感知,还可以使用Lifecycle组件,它可以让任何一个类都可以轻松感知到activity的生命周期,且不需要在activity编写大量的逻辑处理。当然Lifecycle组件也可以感知fragment组件。

1.从广告引导页的需求说起:

在实际App项目开发中,广告引导页是一个很常见的需求,具体描述如下:

  • 用户打开App显示5秒钟的广告,广告结束后进入App主页面。
  • 广告结束前,用户可以点击跳过广告。
  • 页面销毁时,计时器销毁。

2.基本使用:

写法1:

class MainActivity : AppCompatActivity(){

    lateinit var button: Button
    lateinit var textView: TextView
    private var advertisementManager:AdvertisementManager?=null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button=findViewById(R.id.button)
        textView=findViewById(R.id.textView)
        advertisementManager= AdvertisementManager()
        advertisementManager?.advertisementManagerListener=object:AdvertisementManagerListener{
            override fun timing(second:Int){
                textView.text="广告剩余$second 秒"
            }
           override fun enterMainActivity(){
               MainActivity2.actionStart(this@MainActivity)
               finish()
            }
        }
        //点击跳过广告
        button.setOnClickListener{
            MainActivity2.actionStart(this@MainActivity)
            finish()
        }
        //开始广告
        advertisementManager?.start()
    }

    override fun onDestroy() {
        super.onDestroy()
        advertisementManager?.onCancel()
    }
}
interface AdvertisementManagerListener {
    //计时
    fun timing(second:Int)
    //技术结束,进入主页面
    fun enterMainActivity()
}
class AdvertisementManager {
    var TAG=" AdvertisementManager "
    var advertisementManagerListener:AdvertisementManagerListener?=null

    private var countDownTimer:CountDownTimer?=object:CountDownTimer(5000,1000){
        override fun onTick(millisUntilFinished: Long) {
            Log.d(TAG, "广告剩余:${(millisUntilFinished/1000).toInt()}")
            advertisementManagerListener?.timing((millisUntilFinished/1000).toInt())
        }

        override fun onFinish() {
            Log.d(TAG, "广告结束")
            advertisementManagerListener?.enterMainActivity()
        }
    }

    fun start(){
        Log.d(TAG,"开始计时")
        countDownTimer?.start()
    }

    fun onCancel(){
        Log.d(TAG, "停止计时")
        countDownTimer?.cancel()
        countDownTimer=null
    }
}

写法2:Lifecycle

加上以下代码:

lifecycle.addObserver(advertisementManager!!)
 @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun start(){
        Log.d(TAG,"开始计时")
        countDownTimer?.start()
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onCancel(){
        Log.d(TAG, "停止计时")
        countDownTimer?.cancel()
        countDownTimer=null

    }

LifecycleOwner:

LifecycleOwner它可以把activity的生命周期通知给实现Lifecycle的类。首先调用LifecycleOwner的getLifecycle方法,得到一个Lifecycle对象,然后调用它的addObserver方法来观察LifecycleOwner的生命周期,再把实现Lifecycle的类传进去。那怎么才可以获取一个LifecycleOwner实例呢?

  1. 只要当前Activity或者fragment继承于androidX.core.app.ComponentActivity,androidx.fragment.app.FragmentActivity,androidx.appcompat.app.AppCompatActivity,androidx.fragment.appFragment。都是可以自己使用Lifecycle的,也就是直接使用​ lifecycle.addObserver()  ​,因为这些类实现了LifecycleOwner接口,也就是它们本身就是一个LifecycleOwner的实例。
  2. 如果当前的activity是没有继承实现LifecycleOwner接口的android.app.Activity,那就可以自定义一个LifecycleOwner。只需要在当前的Activity页面实现LifecycleOwner接口,并且重写getLifecycle方法即可。

补充:

  1. 如果想要让实现Lifecycle的类主动获取当前的生命周期状态,可以把在类中的构造方法中加入一个Lifecycle类型的参数,然后再调用它的currentState方法即可获取当前的生命周期状态。
  2. getLifecycle方法实际上返回的是一个LifecycleRegistry对象,该对象是Lifecycle的唯一实现类,Lifecycle抽象类中定义了添加观察者,移除观察者和获取当前生命周期状态的方法。

ViewModel:

ViewModel可以分担Activity的一部分工作,以生命周期的方式存储和管理界面相关的数据,且可以在发生屏幕旋转等等配置更改之后让数据继续保存,只有当Activity退出的时候才会跟着Activity一起销毁,因此,将与界面相关的变量存放在ViewModel中,即使旋转屏幕,界面上显示的数据也不会丢失。

当然,屏幕旋转保存数据还有以下方式:

  1. 屏幕旋转时,生命周期的变化取决于configChanges属性,这里未配置configChanges的属性,所以屏幕由竖屏切换为横屏时,会重新执行每个生命周期的方法。当然也可以通过修改configChanges属性使得APP旋转的时候不被销毁。
  2. 通过重写onSavelnstanceState方法在Activity被销毁的时候将当前计时点存储起来,重新开始计时的时候从上次计时的节点开始计时。

1.屏幕旋转保存数据:

写法1:重写onSavelnstanceState方法

​
   savedInstanceState?.let { 

            //获取key对应的值
            millis=it.getLong(KEY_MILLIS)
        }

    override fun onSaveInstanceState(outState:Bundle){
        //重写onSaveInstanceState方法并且把要保存的数据存储
        super.onSaveInstanceState(outState)
        outState.putLong(KEY_MILLIS,millis)

    }
    companion object{
        const val KEY_MILLIS="keyMillis"
    }

ViewModel虽然可以防止屏幕旋转引起的数据丢失,但ViewModel并不能代替onSaveInstanceState方法,主要原因有如下两点:

  1. onSaveInstanceState方法可以存储少量的序列化数据,ViewModel可以存储任意数据,只是使用时的限制不同。
  2. onSaveInstanceState可以达到数据持久化的目的,但是ViewModel不可以,使用场景不同。

为什么说ViewModel不能达到数据持久化的目的呢?因为当Activity被真正销毁的时候,ViewModel会将资源进行回收。

写法2:ViewModel

 //获取ViewModel的实例
lateinit var advertisingViewModel: AdvertisingViewModel

advertisingViewModel= ViewModelProvider(this).get(AdvertisingViewModel::class.java)
//ViewModel类
class AdvertisingViewModel: ViewModel() {
    var vMillis:Long=5000
}

若开发者想要在ViewModel类中使用资源文件,则要使用到Context上下文了。但是一定不能够把Activity的上下文传递给ViewModel,否则会存在内存泄漏的风险。处理方法是:把父类ViewModel修改为AndroidViewModel即可。

class AdvertisingRCViewModel(application: Application):AndroidViewModel(application) {
    var vMillis:Long=5000
}

注意:

不能直接的去创建ViewModel的实例,而是要通过ViewModelProvider来获取ViewModel的实例,之所以要这么写是因为ViewModel有自己独立的生命周期,并且其的生命周期还要长于Activity,所以直接在onCreate方法中创建时,在每次onCreate方法执行的时候,ViewModel都会创建一个新的实例,导致数据无法保存。语法规则为:

ViewModelProvider(Activity或者Fragment实例).get(ViewModel::class.java)

而get方法的参数不为null的情况下,会创建ViewModel,创建好之后将其缓存在ViewModelStore中,如果当前需要创建的ViewModel对象已经存在的话,则直接从ViewModelStore中取出来。所以在屏幕旋转前后使用的是同一个对象也就是说在创建ViewModel的时候,只要传入的class对象是一样的,那么获取的ViewModel就是同一个对象,基于这一点可以在同一个宿主Activity的不同Fragment之间进行数据的共享。

2.向ViewModel传递参数:

向ViewModel的构造函数中传递参数,需要借助ViewModelProvider.Factory接口即可。

  1. ViewModelProviders.Factory 接口是负责实例化 ViewModels 对象的启动装置.
  2. ViewModelProvider.Factory 是一个包含 create 方法的接口。这个 create 方法负责创建你的 VeiwModel实例.
  3. 这是由于你在实例化 ViewModel 对象时,不能直接在activity或者fragment中调用 ViewModel 的构造方法,而且你又想去设置 ViewModel 构造方法的参数,因此你需要将参数传入 ViewModelProvider.Factory 它将会帮你创建你的 ViewModel。
​//ViewModel类
class AdvertisingViewModel(parameter:Int): ViewModel() {
    //
}

 在这里给ViewModel添加了一个参数,然后新建一个实现了ViewModelProvider.Factory接口的类:

class MyViewModelProviderFactory(private val parameter:Int):ViewModelProvider.Factory{
    
    override fun<T:ViewModel>create(modelClass:Claa<T>):T{
        return AdvertisingViewModel(parameter) as T
    }
}

在MyViewModelProviderFactory类中的构造函数中添加了一个参数,利用这个参数就可以在create方法中创建ViewModel的实例了,并且传入需要的参数。最后在Activity中获取ViewModel实例的时候也要改变一下:

​ //获取ViewModel的实例
advertisingViewModel= ViewModelProvider(this,MyViewModelProviderFactory(parameter))
                          .get(AdvertisingViewModel::class.java)

3.使用ViewModel实现数据共享:

在实际开发中,经常会遇到两个Fragment之间有通信的需求,假设现在有AFragment和BFragment,这两个Fragment中都有滑动的标签,我们想要让两个Fragment的标签选项实现同步滑动,比如:在AFragment中选中了“新闻”标签,切换到BFragment时也会自动切换到“新闻”标签。一般情况下实现这个需求的方式有两种:

  1. 在某个Fragment中选中数据时将选择的标签位置记录下来,当切换Fragment时,取出当前记录的位置进行切换。
  2. 通过为宿主Activity增加实现接口的方式进行通信。

上面是开发者经常使用的两种方式,现在使用ViewModel的特性便可以很简单地解决这个问题。

注意:在Fragment中通过ViewModelProvider获取ViewModel对象时,如果参数是this,则获取的是Fragment自对应的ViewModel对象,此种方式不能用来实现数据共享功能。而如果参数是requireActivity(),则获取的是宿主Activity对应的ViewModel,这种获取方式可以用来实现数据共享。

LiveData:

LiveData是Jetpack提供的一种响应式编程组件,它可以包含任何类型的数据,并且在数据发生变化的时候通知给观察者。LiveData特别适合与ViewModel结合在一起使用,虽然它也可以使用在其他的地方,但是在绝大多数的情况下,都是使用在ViewModel中的。

1.基本使用:

在ViewModel实现类中声明一个MutableLiveData<>类型的变量,其中MutableLiveData是LiveData的实现类。

var timingResult=MutableLiveData<Long>()

 然后提供MutableLiveData的读写方法,MutableLiveData有3种读写方法,分别是getValue方法,setValue方法和postValue方法,其中postValue方法是用于在非主线程中给LiveData设置数据,而前面两者是提供给主线程中调用的。

fun setTimingResult(vMillis:Long){
        timingResult.value=vMillis
    }

然后在变量发生变化时需要将变量通过setTimingResult方法赋值给timingResult变量。

 advertisingViewModel.setTimingResult(millisUntilFinished/1000)

最后在需要观察的地方通过observer方法来订阅LiveData对象,这样当LiveData的值改变时,就可以收到更新的通知了。

  advertisingViewModel.timingResult.observe(this, Observer {
            textView.text="广告剩余$it 秒"
            if (it==0L){
                Log.d(TAG,"广告结束")
                MainActivity2.actionStart(this@MainActivity)
                finish()
            }
        })

 任何的LivaData对象都可以调用它的observer方法来观察数据的变化,该方法接收两个参数:第一个是LifecycleOwner对象,第二个是一个observer接口,当LiveData对象包含的数据发生变化时,就会回调到这里。

缺陷:

这里的MutableLiveData<>类型的变量并没有设置为private,所以在ViewModel的外部也是可以对变量进行赋值的,但是如果声明为private之后,也没有办法在Activity中观察数据的变化了,对于这种情况,一般会单独声明非私有类型的LiveData类型的变量

 private var timingResult=MutableLiveData<Long>()
    val _timingResult:LiveData<Long>
    get() = timingResult

 那么在Activity中调用时就可以:

 advertisingViewModel._timingResult.observe(this, Observer {
            textView.text="广告剩余$it 秒"
            if (it==0L){
                Log.d(TAG,"广告结束")
                MainActivity2.actionStart(this@MainActivity)
                finish()
            }
        })

这样,当外部在调用_timingResult变量时实际上调用的是timingResult 变量的值,由于_timingResult是val的,所以无法给_timingResult重新赋值,只能是timingResult。但是timingResult的值却可以更改,保证数据的完整性。

2.map和switchmap:

LiveData的基本用法已经可以满足大部分的需求了,而有些特殊的需求,LivaData提供了两种转换方法:map方法和switchmap方法。

2.1:map方法:

map方法的作用是将实际包含数据的LiveData和仅仅用于观察数据的LiveData进行转换。

​data class Student(var name:String,var score:Int)

 private var timingResult=MutableLiveData<Student>()
    val _timingResult:LiveData<Int>
    get() = Transformations.map(timingResult){it.score}

这么写的意义是:如果外部只想要Student类的score变量,我们还把整个Student通过timingResult传递给_timingResult让外部调用,导致整个Student类暴露给外部,倒是有点不合适了。所以map方法就是来解决这种问题的,它可以将Student类型的LiveData自由的转换成为任意其他类型的LiveData,以此来传递外部想要的类型。

2.2:switchMap方法:

使用switchMap方法的前提是ViewModel中的某一个LiveData对象是调用另外的方法获取的,那么就可以借助switchMap方法,将这个LiveData对象转换为另外一个可以观察的LiveData对象。switchMap()方法同样接收两个参数;第一个参数传入我们新增的LiveData,switchMap()方法会对它进行观察;第二个参数是一个转换函数。注意,我们必须在这个转换函数中返回一个 LiveData对象,因为 switchMap()方法的工作原理就是要将转换函数中返回的lLiveData对象转换成另一个可观察的LiveData对象。

3.补充:

  • LiveData本质上是观察者模式。
  • observer方法首先会使用assertMainThread方法检查程序是否运行在主线程中,所以使用observer方法时要确保它在主线程中执行。
  • 如果想要让数据的监测变化不受活动状态的影响的话,可以使用observerForever方法,这样即使activity不处于活动状态,也可以接收到改变的数据,但是当activity销毁时,一定要调用removeObserver方法,否则LiveData会一直存在,导致内存泄漏。

ViewBinding:

1.从findViewById说起:

通过findViewById方法可以获取视图中的控件,进而编写与视图交互的代码。在activity中绑定布局中的控件一般有三种实现的方式,分别是:

  • 使用findViewById方法进行绑定。
  • 使用ButterKnife开源框架实现,其中ButterKnife是一个只专注于Android系统的View注入框架。
  • 使用kotlin的扩展插件来获取视图的控件。

2.基本使用:

ViewBinding提供了视图绑定功能,为开发者提供了更加简便的方式编写与视图交互的代码。首先在模块的build.gradle配置之后,系统会为该模块中的每个XML布局文件生成一个绑定类,这个绑定类的命名就是XML文件的名称转换为驼峰式,并且在末尾加上Binding,绑定类可以直接引用布局内所有具有id的视图。以activity_main.xml为例子,系统自动生成的绑定类的名称为ActivityMainBinding。


class MainActivity : AppCompatActivity() {
    
    //声明一个ActivityMainBinding类型的变量
    lateinit var activityMainBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //生成实例
        activityMainBinding= ActivityMainBinding.inflate(layoutInflater)
        
       // setContentView(R.layout.activity_main)
        //设置根视图
        setContentView(activityMainBinding.root)
        //这样就可以通过绑定类实例来操作包含id的控件
        activityMainBinding.button.setOnClickListener {  }
    }
}

如果在Fragment中使用,流程与activity中使用是类似的,声明一个FragmentBinding实例,通过inflate方法生成实例,设置根视图,但是因为Fragment的存在时间比其的视图时间长,所以需要在onDestroyView方法中清除对绑定类实例的引用,避免造成内存泄漏的风险。

class Fragment:Fragment() {
    
    lateinit var fragmentMainBinding:FragmentMainBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
    
        fragmentMainBinding=FragmentMainBinding.inflate(inflater,container,false)
        return fragmentMainBinding.root
    }
    
    override fun onDestroyView(){
        super.onDestroyView()
        //清除
        fragmentMainBinding=null
    }
}

3.总结:

  • 启用ViewBinding功能的配置是对整个模块而言的,即会为整个模块的所有布局文件生成对应的绑定类。如果某一个布局文件不需要的话,可以通过设置tool:viewBindingIgnore="true"属性,这样的话,系统就不会为该XML文件自动生成绑定类了。
  • 相比于findViewById方法,ViewBinding具有以下优点:具有Null安全(视图绑定会对视图直接引用),具有类型安全(每个绑定类中的字段都具有与它们在XML文件中引用的视图相匹配的类型)。
  • 原理是在模块的build.gradle开启之后,若有项目要进行编译,那么会扫描layout下的所有布局文件,并且生成对应的绑定类,最终还是调用findViewById方法来实现的。
  • ViewBinding是通过编译时扫描layout文件生成的ViewBinding类的,而ButterKnife是通过ART运行时注解生成的ViewBinding类实现的。

DataBinding:

通过视图绑定组件VicwBinding可以编写更简洁的与视图交互的代码。而DataBinding组件与ViewBinding组件一样,也可以引用视图id,但DataBinding有着更加丰富的功能。

1.基本使用:

DataBinding组件通过使用声明式格式将数据源绑定到布局中。DataBinding在Google推荐的MVVM架构中发挥着重要的作用,MVVM架构的本质是数据驱动页面,而目前Android系统提供给开发者的实现这一功能的最佳组件只有DataBinding和Jetpack Compose。 在配置完成后进入使用。

dataBinding{
        enabled=true
    }

1.1:基础布局绑定表达式:

写法1:

需求把编辑框的name和id显示在文本框内。常用写法:

//新建数据类user,包含name和id
data class User(var name:String?,var id:String?)

然后完成布局,两个编辑框,两个文本框和一个按钮。

在点击按钮时,获取编辑框的内容生成一个user对象,并且给文本框进行赋值。

class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding:ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //绑定
        activityMainBinding= ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)
        //按钮
        activityMainBinding.button.setOnClickListener {
            //获取user对象
            val user=getUser()
            activityMainBinding.textView.text=user.name
            activityMainBinding.textView2.text=user.id

        }
    }
    
    private fun getUser():User{
       return User(getName(),getId())
    }
    
    private fun getName():String?{
      return  activityMainBinding.name.text?.toString()
    }
    
    private fun getId():String?{
        return activityMainBinding.id.text?.toString()
    }
}
写法2:DataBinding

如果使用DataBinding组件的话,就可以省去主动设置的过程。首先要修改布局文件,因为DataBinding的布局文件必须使用layout根标记,并且通过data标签设置对应的数据实体类。

<layout>

    <data>
        <variable
            name="user"
            type="com.example.myapplication.User" />
    </data>
......
</layout>

其中name属性声明了布局文件中可以使用的对象名称,type属性是对应的实体类。然后就可以使用对象的数据为文本组件进行赋值。


        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.id}" />

接着在activity中进行数据绑定,与ViewBinding一样,DataBinding也会为布局文件生成一个绑定类。例如:activity_main.xml,对应的生成类是ActivityMainBinding。绑定之后设置activityMainBinding.user属性之后,user类的数据就会自动的填充到XML布局中了。

activityMainBinding= ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)
        //按钮
        activityMainBinding.button.setOnClickListener {
            //获取user对象
            val user=getUser()
            //设置
            activityMainBinding.user=user
}

 视图中也可以引入表达式,例如:

    <data>
        <variable
            name="user"
            type="com.example.myapplication.User" />
        <import type="android.view.View"/>
    </data>


<TextView
            android:id="@+id/textView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.id}"
            //表达式
            android:visibility='@{user.id.equals("001")? View.GONE:View.VISIBLE}'/>

DataBinding不仅仅可以在Activity中使用,还可以在Fragment,RecycleView适配器中使用。 

 

1.2:利用DataBinding绑定点击事件:

在上面的例子里,按钮的点击事件是通过设置setOnClickListener方法来处理的。除此之外,还可以通过onClick属性使用DataBinding来绑定点击事件

写法1:
 <Button
            android:id="@+id/button"
            //增加属性
            android:onClick="confirm"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button" />

onClick属性的值是需要在activity中实现的方法,也是按钮点击事件之后要执行的内容。

    fun confirm(view: View){
        val user=getUser()
            activityMainBinding.user=user
    }

写法2:DataBinding

DataBinding的实现方式与上面有些类似,叫做方法引用。首先新建一个类,添加一个点击事件之后要触发的方法。

class ClickHandlers {
    var TAG="ClickHandlers"
    fun confirm(view: View){
        Log.d(TAG, "触发点击事件了")
    }
}

接着在XML中引入,并且设置onClick属性。

        <variable
            name="clickHandlers"
            type="com.example.myapplication.ClickHandlers" />

        android:onClick="@{clickHandlers::confirm}"

最后,在activity中绑定类。

 activityMainBinding.clickHandlers=ClickHandlers()

 方法引用的表达式是在编译时处理的,如果没有对应的方法,则报错。

1.3:标签布局使用DataBinding:

有时候为了优化布局会经常使用include,merge标签将部分的布局抽取出来,这种布局也叫做标签布局。DataBinding绑定标签布局的过程为:

<layout>

    <data>
        <variable
            name="user"
            type="com.example.myapplication.User" />
    <data>

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

         <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.id}" />
     </LinearLayout>
</layout>

这是一个标签文件user_data,它包括了两个文本框,因为在activity_main.xml中要使用它,所以在activity_main.xml文件中引入标签文件,然后设置user的值即可。

<include
    layout="@layout/user_data"
    bind:user="@{user}"/>

2.ViewBinding和DataBinding的区别:

View.Binding可以实现的功能DataBinding也可以实现,那么DataBinding与ViewBinding
有什么区别呢?DataBinding和ViewBinding都可生成可直接引用视图的绑定类,从这一点来说,DataBinding和 ViewBinding都可以代替findViewByld。但ViewBinding仅有引用视图的功能,因此和DataBinding相比,ViewBinding有以下优势:

  • 编译速度快:ViewBinding不需要处理DataBinding的注解,编译时间短,编译速度更快。
  • 使用简洁:ViewBinding对布局元素没有限制,不需要以layout开头,启动视图绑定后就可以在项目中使用。

DataBinding更像是ViewBinding的扩展版本,它提供了更多常用的功能,所以ViewBinding不具备布局表达式、双向数据绑定(可以实现编辑框的内容不通过按钮给到文本框)等功能。
在实际项目开发中,如果只是为了替代findViewByld的功能,使用ViewBinding完全足够。如果想使用数据绑定等一些更高级的操作,则需要使用DataBinding。不过,DataBinding虽然是MVVM模式的核心实现方式,但许多开发者却对其敬而远之,从前面的示例中也可以看出,设置数据的相关逻辑都写在了xml中,这会导致调试困难。不过,结合业务需求和团队开发技术,选择适合的方案才是最主要的。

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

mo@

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

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

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

打赏作者

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

抵扣说明:

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

余额充值