Как развивалась работа с кнопкой в 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()
}