Привязка представления Kotlin Android: findViewById против Butterknife против расширения Kotlin для Android

Я пытаюсь найти лучший способ привязки 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

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

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

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

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

Хорошо ли работают расширения Kotlin для Android с шаблоном RecyclerView + ViewHolder?

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

Пример: для 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" ... />
</>

person triad    schedule 29.09.2017    source источник


Ответы (6)


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. Метод довольно маленький, поэтому он не сильно увеличивает размер APK.
  2. Заменяет каждый вызов синтетического свойства вызовом функции.

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

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

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

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

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

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

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

Я надеюсь, что это помогает.

person chandil03    schedule 29.09.2017

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

введите описание изображения здесь

Мой совет:

  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.

person Rvb84    schedule 29.09.2019

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

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

Это обсуждалось здесь.

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

Все расширения Kotlin Android Extensions делают для вас findViewById. См. здесь.

Хорошо ли работают расширения Kotlin для Android с шаблоном RecyclerView + ViewHolder?

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


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

person zsmb13    schedule 29.09.2017
comment
Как насчет библиотеки привязки данных? - person Mr. Nacho; 16.08.2018

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

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"
}

Затем фрагмент уничтожается, потому что вы его заменяете, но затем вы возвращаетесь, и он снова регенерирует 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"
}
person crgarridos    schedule 29.09.2017

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

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

Просмотр привязки

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

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


Отличия от findViewById

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

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

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


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

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

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

Использование

Чтобы воспользоваться преимуществами привязки 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() }
person user158    schedule 12.09.2019
comment
Я получаю неразрешенную ссылку на попытку объявить привязку: ResultProfileBinding. Я добавил строки для включения привязки просмотра в файле Gradle моего модуля, и это находится в Android Studio 4. Есть идеи? - person Oscar; 17.06.2020
comment
@Oscar вы переименовали фрагмент? в таком случае убедитесь, что имя привязки правильное. - person user158; 17.06.2020
comment
Спасибо. Проблема заключалась в имени созданного класса. Я делал одно из руководств Google, которое начинается с основного действия, поэтому, конечно, имя должно было быть ActivityMainBinding. - person Oscar; 17.06.2020

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

потому что это явно больше, чем kotlin-extension

p.s findviewbyid - очень неудобство и шаблонный код

person 최봉재    schedule 13.02.2019