Kotlin Android View Binding: findViewById vs Butterknife vs Kotlin Android Extension

Я пытаюсь найти лучший способ сделать Android View Binding в Kotlin. Кажется, есть несколько вариантов:

findViewById

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

нож для масла

https://github.com/JakeWharton/butterknife

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

Расширения Android Kotlin

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

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

Я хорошо знаком с findViewById и Butterknife в java land, но каковы плюсы и минусы каждого подхода к привязке к каждому виду в Kotlin?

Поддерживает ли Kotlin Android Extensions хорошо с шаблоном RecyclerView + ViewHolder?

Также как Kotlin Android Extensions обрабатывает привязку вида для вложенных представлений через include?

ex: для Activity с использованием activity_main.xml, как бы получить доступ к 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" ... />
</>

Ответ 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. Просто используйте идентификатор импортированных представлений.

Взгляните на следующие официальные документы:

Kotlin Android Extensions является плагином для компилятора Kotlin, и он выполняет две функции:

  • Добавляет скрытую функцию кеширования и поле внутри каждой деятельности Kotlin. Метод довольно мал, поэтому он не увеличивает размер APK.
  • Заменяет каждый синтетический вызов свойства вызовом функции.

    Как это работает, при вызове синтетического свойства, когда получатель является классом активности Kotlin Activity/Fragment, который находится в источниках модуля, вызывается функция кэширования. Например, учитывая

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

скрытая функция кэширования создается внутри MyActivity, поэтому мы можем использовать механизм кэширования.

Однако в следующем случае:

fun Activity.b() { 
    this.textView.setText("")
}

Мы не знали бы, будет ли эта функция вызываться только из Деяний из наших источников или в простых Java-действиях. Таким образом, мы не используем кеширование там, даже если экземпляр MyActivity из предыдущего примера является получателем.

Ссылка на выше страница документации

Надеюсь, это поможет.

Ответ 2

Я не могу отметить этот вопрос как дубликат, так как вы задаете несколько вопросов, на которые были ответили/обсуждались по разным вопросам.

Каковы плюсы и минусы каждого подхода к привязке вида в Котлин?

Об этом говорилось здесь.

Как Kotlin Android Extensions обрабатывает привязку вида для вложенных представлений через include? ex: для Activity с использованием activity_main.xml, как можно получить доступ к custom1?

Все расширения Kotlin для Android - это вызов findViewById для вас. См. здесь.

Поддерживает ли Kotlin Android Extensions хорошо с шаблоном RecyclerView + ViewHolder?

Да, да. Тем не менее, вы должны использовать сохранение Представлений, которые вы получаете от него, в свойствах, поскольку для них нет кеша, например, в "Действиях или фрагментах". См. здесь.


Если у вас остались нерешенные вопросы, не стесняйтесь просить разъяснений.

Ответ 3

Позаботьтесь об использовании

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

Я уже сталкиваюсь с проблемой, когда представление уничтожается, и по мере того как экземпляр вашего фрагмента выживает (я думаю, что в случае acitivities он не применяется), они содержат ленивое свойство, ссылающееся на старое представление.

Пример:

У вас есть статическое значение в макете, скажем 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"
}

Затем фрагмент будет уничтожен, потому что вы его замените, но затем вернитесь назад и снова восстановите callin 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"
}

Ответ 4

Теперь существует четвертая опция, которая называется Просмотр привязки, доступная в Android Studio 3.6 Carnary 11

Цитата из документов.

View Binding

Просмотр привязки - это функция, которая позволяет легче писать код который взаимодействует с мнениями. Как только привязка вида включена в модуле, он генерирует класс привязки для каждого файла макета XML, присутствующего в этом модуль. Экземпляр связующего класса содержит прямые ссылки на все представления, имеющие идентификатор в соответствующем макете.

В большинстве случаев привязка вида заменяет findViewById.


Differences from findViewById

Привязка к просмотру имеет важные преимущества по сравнению с использованием findViewById:

  • Нулевая безопасность: Поскольку привязка представления создает прямые ссылки на представления, нет риска исключения нулевого указателя из-за недопустимого просмотр ID. Кроме того, когда представление присутствует только в некоторых конфигурации макета, поле, содержащее его ссылку в класс привязки помечен @Nullable.

  • Безопасность типов: Поля в каждом классе привязки имеют типы, соответствующие представлениям, на которые они ссылаются в файле XML. Это значит, что нет риска исключения из класса.


Отличия от библиотеки привязки данных

Просмотр привязки и библиотека привязки данных создают привязку классы, которые вы можете использовать для прямой ссылки на представления. Тем не менее, есть заметные различия:

  • Библиотека привязки данных обрабатывает только макеты привязки данных, созданные с использованием тега <layout>.
  • Привязка вида не поддерживает переменные макета или выражения макета, поэтому ее нельзя использовать для привязки макетов к данным в XML.

Usage

Чтобы воспользоваться привязкой View в модуле вашего проекта, добавьте следующая строка в его файл 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() }

Ответ 5

если вы используете библиотеку datainding. Вы должны привязать представление данных.

потому что это больше, чем kotlin-extensions

ps findviewbyid очень неудобно и шаблонный код

Ответ 6

Есть много способов получить доступ к представлениям в Android. Краткий обзор:

enter image description here

Мой совет:

  1. findViewById: старая школа. Избегайте.
  2. ButterKnife: старая школа, но меньше шаблонов и некоторые дополнительные функции. Все еще не хватает безопасности времени компиляции. Избегайте, если возможно.
  3. Kotlin Synthetic: действительно элегантная кэшированная версия findViewbyId. Лучшая производительность и намного меньший шаблон, но все еще не безопасность времени компиляции. Лучший вариант, если безопасность времени компиляции не требуется.
  4. ViewBinding: где-то между опцией 1-3 и привязкой данных. Но не хватает мощные опции DataBinding (например, двухстороннее связывание данных и использование переменных внутри файлов XML).
  5. Привязка данных: самый мощный вариант. Очень хорошо интегрируется с LiveData и ViewModels (JetPack) для создания реактивного интерфейса (похожего на RxJava). Может замедлить время сборки (использует процессор аннотаций точно так же, как ButterKnife) в больших проектах/пользовательских интерфейсах. Мои сильные личные предпочтения.

Смотрите также: https://www.youtube.com/watch?v=Qxj2eBmXLHg

Забавно отметить, что Джейк Уортон (первоначальный автор ButterKnife) теперь присоединился к Google и работает над ViewBinding.