UI Components
ViewStub
Используется для отложенного включения и инициализации другого представления (или макета) в UI. Предназначен для оптимизации производительности при запуске приложения, когда нужно отложить загрузку или отображение некоторых частей интерфейса до тех пор, пока они действительно не понадобятся. Представляет собой невидимый элемент в макете, который содержит ссылку на другой макет, который нужно загрузить.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ViewStub
android:id="@+id/view_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/your_layout_to_inflate" />
</LinearLayout>
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Получаем ViewStub
val viewStub: ViewStub = findViewById(R.id.view_stub)
// Загружаем макет
viewStub.inflate()
}
}
inflate
Когда вызывается метод inflate()
, ViewStub заменяется указанным макетом, и этот макет становится частью интерфейса пользователя.
FrameLayout
Легкий достаточно одного measure-прохода чтобы понять как отображать своих детей внутри себя.
LinearLayout
Легкий если не использовать веса, достаточного одного прохода для отображения детей. Если использовать веса нужно 2 прохода: узнать веса и расставить относительно друг друга.
ConstraintLayout
Умеет делать что угодно, один из тяжелых, нужно 2 прохода, внутри себя решает систему линейных уравнений чтобы размещать детей.
MotionLayout
Предоставляет возможность создания сложных анимаций и переходов между различными состояниями пользовательского интерфейса. Является частью ConstraintLayout. Позволяет синхронизировать анимации нескольких элементов.
<androidx.constraintlayout.motion.widget.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Элементы интерфейса -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/myView"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@android:color/holo_blue_light"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- MotionScene для определения анимаций -->
<androidx.constraintlayout.motion.widget.MotionScene
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end"
app:duration="1000">
<OnSwipe
app:dragDirection="dragUp"
app:touchAnchorId="@id/myView"
app:touchAnchorSide="top"
app:touchRegionId="@id/myView"/>
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/myView"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/myView"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="parent"/>
</ConstraintSet>
</androidx.constraintlayout.motion.widget.MotionScene>
</androidx.constraintlayout.motion.widget.MotionLayout>
MotionScene
Определяет анимации и переходы, включая состояния и переходы между ними.
Transition
Описывает, как и когда происходит переход между двумя состояниями.
OnSwipe
Обработчик для начала перехода на основе жеста.
RelativeLayout
Нужно 2 прохода чтобы отобразить детей. Динозавр.
RemoteViews
View которая может отображаться в другом процессе. Поддерживает ограниченное количество виджетов. Используется для виджетов или уведомлений. Клики обрабатываются через PendingIntent
.
TextView
PrecomputedText
Возможность выполнить все измерения текста в любом потоке. Добавлен в Android 9.
fun TextView.setTextAsync(longString: String) {
val params = this.textMetricsParams
val ref = WeakReference(this)
CoroutineScope.launch(Dispatchers.Default) {
val precomputedText = PrecomputedText.create(longString, params)
withContext(Dispatchers.Main) {
ref.get()?.let { textView ->
textView.text = precomputedText
}
}
}
}
Spannable
Spannable – это интерфейс, характеризующий текст, который имеет стилистическую разметку. Например с помощью Spannable можно создать текст, часть которого окрашена в иной цвет. Метод Spannable.setSpan() принимает произвольный объект типа Object, который используется для разметки. Этот метод не бросает исключений. Классы, которые реализуют интерфейс Spannable, должны игнорировать неподдерживаемые объекты разметки. Пример: ForegroundColorSpan для изменения цвета текста.
Compound Drawable
Это drawable который можно добавить в TextView. Эффективнее чем использовать LinearLayout.
RecyclerView
Улучшенная замена ListView. Bмеет более гибкий API, хороший дизайн, соответствует принципу единственной ответственности. Переиспользует viewholders при скролле. Разделяет хранение и отображение данных. Можно изменить UI в runtime с помощью LayoutManager. Логика анимаций вынесена в ItemAnimator.
Несколько recyclerview внутри scrollview отображают количество элементов максимум с высоту девайса
Заменить ScrollView на NestedScrollView.
DiffUtil
Для оптимизации: метод areContentTheSame проверяет только те поля объекта, которые видны на экране.
RecycledViewPool
Позволяет обмениваться представлениями между несколькими RecyclerView.
AdapterDataObserver
В методе onItemRangeInserted
отслеживать загрузку списка.
setHasFixedSize
Если true - никакой контент или дочерние view внутри RecyclerView не влияют на его высоту или ширину. Владея этой информацией, RecyclerView не выполнит лишние измерения.
setHasStableIds
Если true - сообщает RecyclerView, что нужно ориентироваться, помимо позиций, ещё и на stableId элементов. Рекомендуется использовать stableId для каждого ViewHolder — это помогает RecyclerView переиспользовать view, если меняется список.
setNestedScrollingEnabled
Если вам не нужно, чтобы этот контейнер прокручивался внутри другого скролла, тогда передавайте false. Отключение позволяет не выполнять дополнительные вычисления для жестов.
Лагает прокрутка. Что делать?
- не выполнять тяжелые операции в onBindViewHolder
.
- использовать DiffUtil
вместо notifyDataSetChanged
обновлять одну строку вместо отрисовки всего списка.
- использовать RecycledViewPool
для вложенных списков.
- использовать ViewStub
для ленивого создания view в случае если нужно отобразить ошибку вместо хранения в памяти целого view.
- в XML использовать merge чтобы не добавлять лищние view в иерархию.
- использовать VectorDrawable
вместо тяжелых PNG и JPG, использовать nine-patch для background.
- явно указывать размер view если он фиксирован чтобы не тратить ресурсы на вычисление.
- использовать setHasFixedSize
setHasStableIds
setItemViewCacheSize
.
- обновлять индикаторы через findViewHolderForAdapterPosition
а не через DiffUtil
.
AdapterDelegates
Favor composition over inheritance for RecyclerView Adapters.
FastAdapter
The bullet proof fast and easy to use adapter library which minimizes developing time to a fraction.
ScrollView
fillViewport
Определяет должно ли ScrollView растягивать свое содержимое для заполнения области просмотра.
Constraintlayout
ViewGroup которая позволяет гибко размещать и изменять размеры виджетов.
Guideline
Вспомогательный класс работающий внутри Constraintlayout. Виджеты могут использовать Guideline для позиционирования.
Flow
Позволяет размещать виджеты на которые есть ссылки, горизонтально или вертикально подобно цепочке.
chain
Группа виджетов связанных друг с другом двунаправленными ограничениями.
barrier
Ограничить view набором других view.
baseline
Выровнять view по базовой линии текста.
guideline
Добавить горизонтальную или вертикальную направляющую по которой можно выровнять другие view.
CoordinatorLayout
Сверхмощный FrameLayout. Предназначен для взаимодействия дочерних виджетов между собой например: DrawerLayout
FloatingActionButton
.
ViewPager2
Передать currentItem как аргумент фрагменту.
class PagerViewModel @Inject constructor(
savedStateHandle: SavedStateHandle
): ViewModel() {
val tabLiveData: LiveData<Int> = savedStateHandle.getLiveData<Int>("tab")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.tabLiveData.observe(viewLifecycleOwner) { tab ->
view.post { binding.viewPager.setCurrentItem(tab, false) }
}
}
companion object {
fun create(tab: Int) = PagerFragment().apply {
arguments = bundleOf("tab" to tab)
}
}
WebView
Позволяет отображать веб-страницы внутри вашего приложения.
• Использование WebView может представлять потенциальные угрозы безопасности, так как оно позволяет выполнять JavaScript и обрабатывать веб-контент.
• Важно правильно управлять жизненным циклом WebView, например, освобождать ресурсы и сохранять состояние, если это необходимо
<WebView
android:id="@+id/my_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val webView: WebView = findViewById(R.id.my_webview)
webView.settings.javaScriptEnabled = true // Включение JavaScript
webView.webViewClient = WebViewClient() // Обработка ссылок внутри WebView
webView.loadUrl("https://www.example.com") // Загрузка веб-страницы
}
override fun onPause() {
super.onPause()
webView.onPause()
}
override fun onResume() {
super.onResume()
webView.onResume()
}
override fun onDestroy() {
super.onDestroy()
webView.destroy()
}
}
javaScriptEnabled
Включает или отключает поддержку JavaScript.
setDomStorageEnabled
Включает или отключает поддержку DOM-хранилища.
setSupportZoom
Включает или отключает поддержку масштабирования.
goBack
Управлять возвратом на предыдущие страницы в WebView.
if (webView.canGoBack()) {
webView.goBack()
} else {
super.onBackPressed()
}
WebViewClient
По умолчанию, ссылки, которые пользователь нажимает в WebView, открываются в браузере. Чтобы ссылки открывались внутри WebView, нужно установить WebViewClient.
webView.webViewClient = object: WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
view.loadUrl(url)
return true
}
}
Android UI Components. Вопросы на собесе
- Для чего нужен CoordinatorLayout?
Предназначен для управления поведением дочерних View, таких как
FloatingActionButton
иAppBarLayout
. Он позволяет координировать взаимодействие между этими View, например, автоматическое скрытие или изменение их размера при прокрутке списка.
- Ранжируй Layouts по производительности?
FrameLayout
→LinearLayout
→ConstraintLayout
→RelativeLayout
- С помощью какого компонента RecyclerView можно добавить разделители?
ItemDecoration
- Лагает RecyclerView в чем может быть проблема?
• Тяжелые операции в методе
onBindViewHolder
• Неэффективная работа с изображениями
• Частый вызов
notifyDataSetChanged
• Неправильная настройка
DiffUtil
- Как работает DiffUtil в RecyclerView?
DiffUtil
оптимизирует обновление списка, сравнивая старый и новый наборы данных. Вычисляет разницу между ними и сообщаетRecyclerView
, какие элементы нужно обновить, вставить или удалить. Вызывается методcalculateDiff
, который анализирует изменения. Это повышает производительность и предотвращает мерцание при обновлениях. Под капотом следующие алгоритмы: алгоритм поиска наименьших различий и метод наибольшей общей подпоследовательности.
- Какие методы есть у DiffUtil?
•
areItemsTheSame
- проверяет, идентичны ли два элемента (по позиции) в старом и новом списках.•
areContentsTheSame
- проверяет, равны ли содержимое двух элементов (по позиции) в старом и новом списках.
- С помощью какого компонента лучше выполнять отложенный (по запросу) inflate дочерней View?
• LazyView
• AsnycLayoutinflater
• ViewStub
- Сколько дочерних элементов может содержать ScrollView?
• 2
• 1
• 3
• Сколько угодно
- Какой метод используется для обновления содержимого AdapterView?
•
notifyDataSetChanged
• updateAdapter
• refreshAdapter
• invalidateAdapter
- Какой метод используется для настройки слушателя нажатий в View?
• setOnTouchListener
•
setOnClickListener
• setClickListener
• setOnClick
- Какой из следующих атрибутов XML используется для установки стиля текста в TextView?
• textStyle
•
textAppearance
• textFont
• textSize
- Что отрисуется на экране при inflate этого XML?
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="horizontal"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout>
• left-top
• right-top
• left-bottom
• right-bottom