Как развивалась работа с кнопкой в Android?
На днях вышел Jetpack Compose, поэтому решил ностальгически пройтись по тем способам работы с View-слоем, какие были популярны у Android-разработчиков ранее, потому что работа с XML на View-слое уходит в прошлое.
0. Способы "из коробки"
0.1 findViewById
findViewById - нативный вариант работы с кнопкой. Итак, у нас есть в XML какая-то кнопка. Чтобы работать с ней из Java или Kotlin кода - нужно в XML задать ей ID:
... <Button android:id="@+id/button" android:text="Button" ... /> ...
При компиляции приложения aapt создаст класс R, который содержит ресурс IDs для всех ресурсов в каталоге res.
Как правило, с кнопкой нужно работать не в одном каком-то методе жизненного цикла, но и обращаться к ней из класса бизнес-логики, то есть Presenter'а или ViewModel'и и нужно, чтобы кнопка была объявлена полем, заодно повесим слушать кликов что примерно будет выглядеть так:
public class MainActivity extends AppCompatActivity { Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.nd_activity_main); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onClickButtons(v) } }); } void onClickButtons(View view) { Toast.makeText( this, "Здесь обработка клика", Toast.LENGTH_SHORT).show(); } }
0.2 onClick в XML
Этот добавился несколько позже и позволяет задать вызов метода из XML-разметки напрямую. И данный способ не работает во фрагментах, только в Activity. Выглядит это так:
... <Button android:id="@+id/button" android:text="Button" android:onClick="onClickButtons" ... /> ...
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } void onClickButtons(View view) { Toast.makeText( this, "Здесь обработка клика", Toast.LENGTH_SHORT).show(); } }
0.3 OnClickListener
Здесь нужно было просто реализовать интерфейс в Activity или Fragment'е, что на в итоге приводит к такому:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.nd_activity_main); button = (Button) findViewById(R.id.button); button.setOnClickListener(this); } @Override public void onClick(View v) { onClickButtons() } void onClickButtons(View view) { Toast.makeText( this, "Здесь обработка клика", Toast.LENGTH_SHORT).show(); } }
2. ButterKnife
Разработчики - люди ленивые. И им изначально казалось, что совершается слишком много телодвижений для начала работы со виджетом. И большую популярность обрела библиотека ButterKnife, которую написал Jake Wharton. Первая версия библиотеки вышла 5 марта 2013 года. Как выглядел тот же самый, но чуть расширенный код относительно того, что выше (пример позволяет обрабатывать клик двух кнопок):
public class MainActivity extends AppCompatActivity { @BindView(R.id.button) Button button; @BindView(R.id.button2) Button button2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick({R.id.button, R.id.button2}) void onClickButtons(View view) { switch (view.getId()) { case R.id.button: Toast.makeText(this, "Здесь обработка клика button", Toast.LENGTH_SHORT).show(); break; case R.id.button2: Toast.makeText( this, "Здесь обработка клика button2", Toast.LENGTH_SHORT).show(); break; } } }
3. DataBinding
В 2015 году уже были release-candidate версии этого решения от Google. Точную дату выхода релизной версии не помню, но по-моему активно заговорили о таком решении после Google IO 2017. Нельзя сказать, что оно было очень популярным, так как суть работы происходит через кодогенерацию, что увеличивает время сборки проектов. Это плохо тем, что в сегодняшние дни очень крупные компании стараются модуляризировать свою архитектуру, чтобы при каких-либо изменениях пересобиралась как можно меньшая часть проекта и соответственно уменьшалось время сборки, хотя они даже не используют DataBinding. Сама суть DataBindinga в связывании напрямую xml-кода и ViewModel-класса бизнес-логики, что позволяло даже писать Java-код в XML, то есть у нас напрямую вызывалась функция класса ViewModel'и через лямбда-выражение:
... <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="viewModel" type="com.example.project.MainViewModel" /> ... <Button android:id="@+id/button" android:text="Button" android:onClick="@{() -> viewModel.onClickButtons()}" ... /> ...
4. Kotlin Synthetyc
Если верить тому, как мельком я посмотрел доступные GitHub-репозитории, то разработка Kotlin Synthetyc началась примерно примерно в середине 2016 года. Но данный способ работы со View хоть и был немногословным, но быстро стал Deprecated в версии Kotlin 1.4.20, что датирована 5 мая 2021 года. Принцип работы основан на генерации кода плагином, который автоматически подтягивал нужный import.
import kotlinx.android.synthetic.main.activity_main.* class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.nd_activity_main) button.setOnClickListener { onClickButtons() } } fun onClickButtons() { Toast.makeText( this, "Здесь обработка клика", Toast.LENGTH_SHORT).show(); } }
5. ViewBinding
В феврале 2020 года с выходом Android Gradle plugin 3.6.0 - разработчики смогли подключать ViewBinding для работы со View в своих проектах. Данный метод тоже основан на генерации binding-классов, но ликвидирован недостаток DataBindinga в виде очень долгого времени сборки, ну и соответственно нет возможности писать код в XML.
class MainActivity: AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.button.setOnClickListener { onClickButtons() } } fun onClickButtons() { Toast.makeText( this, "Здесь обработка клика", Toast.LENGTH_SHORT).show(); } }
6. Jetpack compose
28 июля 2021 года - по-настоящему революционная дата для Android-разработчиков. В этот день сменился полностью подход работы со View-слоем приложения. Если во всех вышеназванных способах суть заключалась в различных попытках связать XML-код и Java/Kotlin код, то вышедший в релизной версии Jetpack Compose предлагает декларативную верстку UI прямо в Kotlin-классах. И этот тот подход, какой определенно вытеснит в будущем XML.
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Example("Android") } } } @Composable fun Example(name: String) { val ctx = LocalContext.current Button(onClick = { onClickButtons(ctx,"Здесь обработка клика") }, colors = ButtonDefaults.textButtonColors( backgroundColor = Color.Red )) { Text(text = name) } } fun onClickButtons(ctx: Context, msg: String) { Toast.makeText(ctx, msg, Toast.LENGTH_SHORT).show() }