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]()
Мой совет:
- findViewById: старая школа. Избегайте.
- ButterKnife: старая школа, но меньше шаблонов и некоторые дополнительные функции. Все еще не хватает безопасности времени компиляции. Избегайте, если возможно.
- Kotlin Synthetic: действительно элегантная кэшированная версия findViewbyId. Лучшая производительность и намного меньший шаблон, но все еще не безопасность времени компиляции. Лучший вариант, если безопасность времени компиляции не требуется.
- ViewBinding: где-то между опцией 1-3 и привязкой данных. Но не хватает
мощные опции DataBinding (например, двухстороннее связывание данных и использование переменных внутри файлов XML).
- Привязка данных: самый мощный вариант. Очень хорошо интегрируется с LiveData и ViewModels (JetPack) для создания реактивного интерфейса (похожего на RxJava). Может замедлить время сборки (использует процессор аннотаций точно так же, как ButterKnife) в больших проектах/пользовательских интерфейсах. Мои сильные личные предпочтения.
Смотрите также: https://www.youtube.com/watch?v=Qxj2eBmXLHg
Забавно отметить, что Джейк Уортон (первоначальный автор ButterKnife) теперь присоединился к Google и работает над ViewBinding.