ViewBinding
ViewBinding 是一个小型的库,只是DataBinding的一个功能,也就是只有试图,并没有数据这一层
没有数据改变视图也跟着改变,其实就单单为了不在通过findViewByid去根据控件的id拿到布局的每一个个控件,而是会为了每个布局根据它布局的文件名生成一个类,而布局每一个控件id就是这个类的一个个字段,
所以可以获取一个类布局的实例对象,然后根据这个实例获取每一个控件字段,而不再是通过finViewByid去获取每一个控件了
如果项目只需要这个功能,而不需要数据与视图的双向绑定,就只需要用ViewBinding就可以
使用ViewBinding使用DataBinding 一样的,也是在build.gradle 下打开开关。
android{
viewBinding.enable = true
}
当打开开关后, 就会为所有的布局文件,生成对应的 布局类,利用gradle 技术 自动生成了 命名为 xxxBinding 的类
比如 你新建了一个 R.layout.result_profile 就会自动生成一个叫做 ResultProfileBinding 的类。
R.layout.result_profile
<LinearLayout ... >
<TextView android:id="@+id/name" />
<ImageView android:cropToPadding="true" />
<Button android:id="@+id/button"
android:background="@drawable/rounded_button" />
</LinearLayout>
并且这个每个控件的id就是这个ResultProfileBinding 的每一个属性
也就是 有两个属性,一个是名为 name 的 TextView,另一个是名为 button 的 Button。而没有写id的控件就不会生成属性,该布局中的 ImageView 没有 ID,因此绑定类中不存在对它的引用。
所以可以获取一个类布局的实例对象,然后根据这个实例获取每一个控件字段,而不再是通过finViewByid去获取每一个控件了
而想要获取布局类的一个实例对象,就是调用对应布局类的inflate(layoutInflater)
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding :ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//调用对应布局类的Inflate方法
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
activityMainBinding.name.setOnClickListener {
}
在上面依旧使用setContextView(R.layout.View)的写法,这里也可换一种写法
每一个布局类还有一个 getRoot() 方法,它获取的根view,也就是布局的顶层view 在这里 这个 ResultProfileBinding 类中的 getRoot() 方法会返回 LinearLayout 这个view
所以我们可以 setContextView 一个根view进去
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding :ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//先获取类布局实例对象
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
//再设置实例对象的根view进去
setContentView( activityMainBinding.root)
//使用类布局实例对象的每一个控件属性
activityMainBinding.name.setOnClickListener {
}
那对应fragment也是一样的
class Fragment : Fragment() {
lateinit var activityMainBinding : ActivityMainBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
val view = activityMainBinding.root
return view
}
}
这里因为DataBinding跟ViewBinding 在这里的功能是一致的,所以我们也一起把这一块的说下
上面写通过调用对应布局类的inflate方法获取类实例对象,然后再调用setContextView(布局类对象实例的根view)
在Databinding的话它依旧可以这么做,不过它还有其他一种写法, 可以直接操作
就是调用 DataBindingUtil.setContentView()。这个方法就是把上面两部操作一起做了,
即设置了setContentview,又获取到了一个类实例对象
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding :ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
activityMainBinding.name.setOnClickListener {
}
DataBindingUtil.inflate 也是可以 获取一个布局类实例
但是他跟上面的对应布局类.inflate 也就是 跟 ActivityMainBinding.inflate 不同
在上面的 ActivityMainBinding.inflate 中,因为已经确认了类似就是 ActivityMainBinding
所以在定义变量不需要申明类型,在 inflate后不需要申明类型,
但是你用 DataBindingUtil.inflate ,那就得二选一 申明一次该类的类型
在acitivity中:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//两种写法,第一种是在变量申明
val activityMainBinding :ActivityMainBinding=
DataBindingUtil.inflate(layoutInflater, R.layout.activity_main, null, false)
//第二种是在 inflate<>加
val activityMainBinding =
DataBindingUtil.inflate<ActivityMainBinding>(layoutInflater, R.layout.activity_main, null, false)
setContentView(activityMainBinding.root)
在fragment中
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//两种写法,第一种是在变量申明
val fragmentMainBinding: FragmentMainBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false)
//第二种是在 inflate<>加
val fragmentMainBinding = DataBindingUtil.inflate<FragmentMainBinding>(inflater,R.layout.fragment_main, container, false)
return fragmentMainBinding.root
}
}
如果说当前情况是已经能获取view实例了,比如说通过 LayoutInflater 获取view实例了,那么也可以通过
DataBindingUtil.bind 或者 MyLayoutBinding.bind()去获取这个view对应的布局类实例
val view = LayoutInflater.from(this).inflate(layoutId, parent, attachToParent)
val binding: ViewDataBinding? = DataBindingUtil.bind(view)
val binding: ViewDataBinding? = MyLayoutBinding.bind(view)
kotlin的绑定机制
kotlin的绑定机制 也是能够让我们不再需要findViewById去获取布局下的一个个控件实例。
想要使用 kotlin的绑定机制 需要配置如下
也是再module的build.gradle 写上
plugins {
id 'kotlin-android-extensions'//启用绑定机制
}
启用绑定机制后,可以直接获取项目中任意id的控件,控件名字就是id命名,注意是任意,下面的代码中引入虽然是
R.layout.activity_main2,但是你想要获取的控件能够不只是从这个R.layout.activity_main2 中的其中一个,而是项目下的任意id控件
,所以当你写的控件不是这里引入的 R.layout.activity_main2 其中一个,就会崩溃报错,所以这种绑定机制很容易造成人为写代码引起的错误
class MainActivity : AppCompatActivity() {
var textBinding : TextBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
//启用绑定机制后,可以直接获取项目中任意id的控件,控件名字就是id命名。
text_number.text
}
}
<?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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/text_number"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>