【问题标题】:Kotlin Android View Binding: findViewById vs Butterknife vs Kotlin Android ExtensionKotlin Android 视图绑定:findViewById vs Butterknife vs Kotlin Android Extension
【发布时间】:2018-03-10 22:58:20
【问题描述】:

我正在尝试找出在 Kotlin 中进行 Android 视图绑定的最佳方法。似乎有几个选项:

findViewById

val button: Button by lazy { findViewById<Button>(R.id.button) }

黄油刀

https://github.com/JakeWharton/butterknife

@BindView(R.id.button) lateinit var button: Button

Kotlin Android 扩展

https://kotlinlang.org/docs/tutorials/android-plugin.html

import kotlinx.android.synthetic.main.activity_main.*

我对 java 领域的 findViewById 和 Butterknife 非常熟悉,但是 Kotlin 中每种视图绑定方法的优缺点是什么?

Kotlin Android Extensions 是否与 RecyclerView + ViewHolder 模式配合得很好?

Kotlin Android Extensions 如何通过include 处理嵌套视图的视图绑定?

例如:对于使用 activity_main.xml 的 Activity,如何访问 View custom1

activity_main.xml

<...>
    <include layout="@layout/custom" android:id="@+id/custom" />
</>

custom.xml

<...>
    <View android:id="@+id/custom1" ... />
    <View android:id="@+id/custom2" ... />
</>

【问题讨论】:

    标签: android kotlin findviewbyid butterknife kotlin-android-extensions


    【解决方案1】:

    kotlin-android-extensions 更适合Kotlin。 ButterKnife 也不错,但kotlin-android-extensions 在这里是一个更好、更明智的选择。

    原因Kotlin 使用 synthetic 属性,这些属性使用 caching function 按需调用(因此活动/片段加载速度稍快),而 ButterKnife 一次绑定所有视图ButterKnife.bind()(这会花费更多时间)。使用Kotlin,您甚至不需要使用注解来绑定视图。

    是的,它与 RecyclerView + ViewHolder 模式也很好,你只需要导入kotlinx.android.synthetic.main.layout_main.view.*(如果layout_main.xml 是 Activity/Fragment 布局文件名)。

    您不需要为使用include 导入的布局做任何额外的工作。只需使用导入视图的 id。

    查看以下官方文档说明:

    Kotlin Android Extensions 是 Kotlin 编译器的插件,它做了两件事:

    1. 在每个 Kotlin Activity 中添加一个隐藏的缓存函数和一个字段。该方法非常小,因此不会增加太多 APK 的大小。
    2. 用函数调用替换每个合成属性调用。

      其工作原理是,当调用合成属性时,接收器是模块源中的 Kotlin Activity/Fragment 类,缓存函数被调用。例如,给定

    class MyActivity : Activity()
    fun MyActivity.a() { 
        this.textView.setText(“”)
    }
    

    MyActivity内部生成了一个隐藏的缓存函数,所以我们可以使用缓存机制。

    但是在以下情况下:

    fun Activity.b() { 
        this.textView.setText(“”)
    }
    

    我们不知道这个函数是仅在来自我们源的活动上调用还是在普通的 Java 活动上调用。因此,我们不在那里使用缓存,即使前面示例中的 MyActivity 实例是接收者。

    以上链接documentation page

    希望对你有帮助。

    【讨论】:

      【解决方案2】:

      在 Android 中有很多方法可以访问视图。快速概览:

      我的建议是:

      1. findViewById:老派。避免。
      2. ButterKnife:老派,但样板较少,并增加了一些功能。仍然缺乏编译时安全性。尽可能避免。
      3. Kotlin Synthetic:真正优雅的 findViewbyId 缓存版本。更好的性能和更少的样板,但仍然没有编译时安全性。不需要编译时安全的最佳选择。
      4. ViewBinding:介于选项 1-3 和数据绑定之间。但缺乏 强大的 DataBinding 选项(如 2 路数据绑定和在 XML 文件中使用变量)。
      5. 数据绑定:最强大的选项。与 LiveData 和 ViewModels (JetPack) 很好地集成以创建反应式 UI(类似于 RxJava)。可以在大型项目/UI 上减慢构建时间(使用注释处理器,就像 ButterKnife)。我的个人喜好。

      另见:https://www.youtube.com/watch?v=Qxj2eBmXLHg

      有趣的是,Jake Wharton(ButterKnife 的原作者)现已加入 Google 并致力于 ViewBinding。

      【讨论】:

        【解决方案3】:

        我不能将此问题标记为重复问题,因为您要问的多个问题已在不同问题下得到回答/讨论。

        Kotlin 中每种视图绑定方法的优缺点是什么?

        已经讨论过here

        Kotlin Android Extensions 如何通过包含处理嵌套视图的视图绑定?例如:对于使用 activity_main.xml 的 Activity,如何访问 View custom1?

        Kotlin Android Extensions 所做的一切就是为您调用 findViewById。见here

        Kotlin Android Extensions 是否与 RecyclerView + ViewHolder 模式配合得很好?

        是的,确实如此。但是,您必须使用将您从中获得的视图保存到属性中,因为它们没有像活动或片段中那样的缓存。见here


        如果您仍有未解决的问题,请随时要求澄清。

        【讨论】:

        • Data Binding 库怎么样?
        【解决方案4】:

        注意使用

        val button: Button by lazy { findViewById<Button>(R.id.button) }
        

        当视图被销毁时,我已经遇到了这个问题,并且当你的片段实例存活时(我认为在活动的情况下它不适用),它们持有 lazy 属性引用到旧景。

        例子:

        布局中有一个静态值,比如说android:text="foo"

        //calling first time
        override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
            button.setText("bar")
            // button is called for the first time, 
            // then button is the view created recently and shows "bar"
        }
        

        然后片段被破坏,因为你替换它,但你回来了,它再次重新生成调用 onCreateView。

        //calling second after destroyed
        override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
            button.setText(Date().time.toString())
            //button is already set, then you are setting the value the to old view reference
            // and in your new button the value won't be assigned
            // The text showed in the button will be "foo"
        }
        

        【讨论】:

          【解决方案5】:

          现在有一个第四个选项,称为View Binding,可通过Android Studio 3.6 Carnary 11

          引用docs.

          视图绑定

          视图绑定是一项功能,可以让您更轻松地编写代码 与视图交互的。在模块中启用视图绑定后, 它为其中存在的每个 XML 布局文件生成一个绑定类 模块。绑定类的实例包含对 在相应布局中具有 ID 的所有视图。

          在大多数情况下,视图绑定会替换 findViewById


          findViewById的区别

          视图绑定比使用 findViewById 有重要的优势:

          • Null 安全性: 由于视图绑定会创建对视图的直接引用,因此不会因无效而导致空指针异常的风险 查看标识。此外,当视图仅存在于某些 布局的配置,包含其在 绑定类标有@Nullable

          • 类型安全:每个绑定类中的字段具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着 没有类转换异常的风险。


          与数据绑定库的区别

          视图绑定和数据绑定库都生成绑定 可以用来直接引用视图的类。然而,有 有显着差异:

          • 数据绑定库仅处理使用&lt;layout&gt; 标签创建的数据绑定布局。
          • 视图绑定不支持布局变量或布局表达式,因此不能用于将布局与 XML 中的数据绑定。

          用法

          要利用项目模块中的视图绑定,请添加 以下行到其build.gradle 文件:

          android {
              viewBinding.enabled = true
          }
          

          例如,给定一个名为 result_profile.xml 的布局文件:

          <LinearLayout ... >
              <TextView android:id="@+id/name" />
              <ImageView android:cropToPadding="true" />
              <Button android:id="@+id/button"
                  android:background="@drawable/rounded_button" />
          </LinearLayout>
          

          在本例中,您可以调用ResultProfileBinding.inflate() activity:

          private lateinit var binding: ResultProfileBinding
          
          @Override
          fun onCreate(savedInstanceState: Bundle) {
              super.onCreate(savedInstanceState)
              binding = ResultProfileBinding.inflate(layoutInflater)
              setContentView(binding.root)
          }
          

          绑定类的实例现在可以用来引用任何 意见:

          binding.name.text = viewModel.name
          binding.button.setOnClickListener { viewModel.userClicked() }
          

          【讨论】:

          • 我在尝试声明绑定时得到“未解析的引用”:ResultProfileBinding。我在模块的 Gradle 文件中添加了启用视图绑定的行,这是在 Android Studio 4 中。有什么想法吗?
          • @Oscar 你重命名了片段吗?如果是,请确保绑定名称正确。
          • 谢谢。问题是生成的类的名称。我正在做一个从主要活动开始的谷歌教程,所以名字当然应该是 ActivityMainBinding。
          【解决方案6】:

          如果您使用数据索引库。你应该数据绑定视图绑定。

          因为它比 kotlin 扩展更显式

          p.s findviewbyid 很不方便和样板代码

          【讨论】:

            猜你喜欢
            • 2018-09-25
            • 2018-10-17
            • 1970-01-01
            • 2020-02-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多