Kotlin | Вопросы собесов
2.57K subscribers
28 photos
960 links
Download Telegram
🤔 Почему в приложении много манифестов?

При использовании библиотек и модулей, каждая библиотека может содержать собственный AndroidManifest.xml. При сборке все манифесты объединяются в итоговый файл с учетом правил мёрджа.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
🤔 Как работает jetpackCompose?

Jetpack Compose – это декларативный UI-фреймворк от Google для создания интерфейсов в Android. Вместо традиционных XML + View в Compose используется функции-компоненты, которые описывают UI.

🟠Главные принципы работы Jetpack Compose
Декларативный подход – UI создаётся через функции, без XML.
еактивность – UI автоматически обновляется, если данные изменились.
Компонентный подход – UI состоит из маленьких, переиспользуемых функций.
Composable-функции (@Composable) – основной строительный блок Compose.

🟠Как создаётся UI в Compose?
Вместо Activity и Fragment с findViewById() используется @Composable-функции.
@Composable
fun Greeting(name: String) {
Text(text = "Привет, $name!")
}

@Composable
fun MyScreen() {
Column {
Greeting("Андрей")
Greeting("Мария")
}
}

@Preview
@Composable
fun PreviewMyScreen() {
MyScreen()
}


🟠Реактивность: автоматическое обновление UI
Jetpack Compose использует реактивный подход, где UI обновляется при изменении состояния.
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) } // Реактивное состояние

Column {
Text("Счётчик: $count")
Button(onClick = { count++ }) {
Text("Увеличить")
}
}
}


🟠Основные UI-компоненты в Compose
Текст
Text(text = "Привет, мир!", fontSize = 20.sp)


Кнопка
Button(onClick = { /* действие */ }) {
Text("Нажми меня")
}


Ввод текста (TextField)
var text by remember { mutableStateOf("") }

TextField(value = text, onValueChange = { text = it })


Размещение элементов (вертикально/горизонтально)
Column {
Text("Первый элемент")
Text("Второй элемент")
}

Row {
Text("Слева")
Text("Справа")
}


🟠Взаимодействие с ViewModel
Compose работает с MVVM через ViewModel и LiveData / StateFlow.
class MyViewModel : ViewModel() {
private val _count = MutableStateFlow(0)
val count: StateFlow<Int> = _count

fun increment() {
_count.value++
}
}

@Composable
fun CounterScreen(viewModel: MyViewModel = viewModel()) {
val count by viewModel.count.collectAsState()

Column {
Text("Счётчик: $count")
Button(onClick = { viewModel.increment() }) {
Text("Добавить")
}
}
}


🟠Навигация в Jetpack Compose
Вместо Fragment используется NavController.
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("details") { DetailsScreen() }
}
}


Теперь можно перейти на другой экран так
navController.navigate("details")


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊4👍3
🤔 Какие есть реализации List?

- ArrayList — массивная, быстрая при доступе по индексу.
- LinkedList — список на узлах, эффективен при частых вставках/удалениях.
- Vector — устаревший, синхронизированный.
- CopyOnWriteArrayList — потокобезопасный.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍2
🤔 Расскажи про коллекцию Map

Map – это коллекция пар ключ-значение. Каждый ключ уникален, а значения могут повторяться. В Kotlin Map не наследуется от Collection, но является частью стандартных коллекций.

🟠Виды `Map` в Kotlin
В Kotlin есть два основных типа Map:
Map (неизменяемая) – нельзя добавлять/удалять элементы после создания.
MutableMap (изменяемая) – можно добавлять, удалять и изменять элементы.

Создаётся с помощью mapOf()
val users = mapOf(1 to "Alice", 2 to "Bob", 3 to "Charlie")
println(users[1]) // Выведет: Alice


Создаётся с помощью mutableMapOf()
val users = mutableMapOf(1 to "Alice", 2 to "Bob")
users[3] = "Charlie" // Добавляем элемент
users[1] = "Alex" // Изменяем значение по ключу
users.remove(2) // Удаляем элемент
println(users) // {1=Alex, 3=Charlie}


🟠Основные операции с `Map`
map[key] – получить значение (или null, если ключа нет).
map.getValue(key) – получить значение (или исключение, если ключа нет).
map.getOrDefault(key, defaultValue) – вернуть значение или defaultValue, если ключа нет.
map.getOrElse(key) { default } – если ключа нет, выполнить лямбда-выражение.
val users = mapOf(1 to "Alice", 2 to "Bob")
println(users[1]) // Alice
println(users.getOrDefault(3, "Unknown")) // Unknown


🟠Проверка наличия ключей и значений
map.containsKey(key) – есть ли ключ?
map.containsValue(value) – есть ли значение?
println(users.containsKey(2))  // true
println(users.containsValue("Charlie")) // false


Перебор элементов
for ((key, value) in users) {
println("ID: $key, Name: $value")
}


Фильтрация Map
val filtered = users.filter { (key, value) -> key % 2 == 0 }
println(filtered) // {2=Bob}


🚩Особенности `Map` в Kotlin

🟠Порядок элементов
mapOf() – не гарантирует порядок. linkedMapOf() – сохраняет порядок добавления.
🟠Производительность
Обычный HashMap (mutableMapOf()) – O(1) для поиска по ключу. TreeMapO(log n), но поддерживает сортировку по ключам.

val sortedUsers = sortedMapOf(3 to "Charlie", 1 to "Alice", 2 to "Bob")
println(sortedUsers) // {1=Alice, 2=Bob, 3=Charlie}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Что известно про генерации Young, Old в GC?

- Young Generation:
- Содержит новые объекты.
- Быстрое создание и удаление.
- Делится на Eden и два Survivor-пула.
- GC здесь называется Minor GC и работает быстро.
- Old Generation (Tenured):
- Хранит объекты, пережившие несколько циклов GC.
- Удаление объектов тут требует более тяжёлой и полной сборки — Major GC.
Такой подход (генерационная модель) помогает оптимизировать производительность, так как большинство объектов "умирает молодыми".


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
🤔 От какого объекта наследуются все классы в Java?

В Java все классы неявно наследуются от класса Object, если явно не указано другое наследование.
class MyClass {
// Неявно наследуется от Object
}

class MyClass2 extends Object {
// То же самое, просто указано явно
}


🚩Методы, унаследованные от `Object`

Класс Object содержит основные методы, доступные во всех классах:
class Person {
String name;

Person(String name) {
this.name = name;
}

@Override
public String toString() {
return "Person{name='" + name + "'}";
}
}

public class Main {
public static void main(String[] args) {
Person p = new Person("Alice");

System.out.println(p.toString()); // Person{name='Alice'}
System.out.println(p.hashCode()); // Хеш-код объекта
System.out.println(p.getClass()); // class Person
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Отличие IntentService от Service

- Service работает в главном потоке — требует явного создания потоков.
- IntentService запускается в фоновом потоке автоматически, обрабатывает каждый Intent по очереди и сам завершает себя после выполнения.
Однако IntentService считается устаревшим — теперь предпочтительнее JobIntentService или WorkManager.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3💊3
🤔 В андроиде существуют мэпы, в которые можно положить примитивные типы?

Да! В Android есть специальные Map-коллекции, которые позволяют хранить примитивные типы (int, long, boolean и т. д.) без автоупаковки (autoboxing).

🚩Почему это важно?

Обычные HashMap<Int, Int> в Kotlin используют автоупаковку (Integer вместо int), что:
Увеличивает потребление памяти (из-за объектов Integer, Long и т. д.).
Замедляет работу (из-за ненужного создания объектов).

Решение? Использовать специализированные мэпы из android.util!

🚩`SparseArray` (замена `HashMap<Int, Any?>`)

Хранит пары Int → Any, но без автоупаковки.
import android.util.SparseArray

val sparseArray = SparseArray<String>()
sparseArray.put(1, "Привет")
sparseArray.put(2, "Мир")

println(sparseArray[1]) // Привет
println(sparseArray[2]) // Мир


🚩`SparseIntArray` (замена `HashMap<Int, Int>`)

Хранит пары Int → Int без автоупаковки.
import android.util.SparseIntArray

val sparseIntArray = SparseIntArray()
sparseIntArray.put(1, 100)
sparseIntArray.put(2, 200)

println(sparseIntArray[1]) // 100
println(sparseIntArray[2]) // 200


🚩`SparseBooleanArray` (замена `HashMap<Int, Boolean>`)

Оптимизирован для Int → Boolean пар.
import android.util.SparseBooleanArray

val sparseBooleanArray = SparseBooleanArray()
sparseBooleanArray.put(1, true)
sparseBooleanArray.put(2, false)

println(sparseBooleanArray[1]) // true
println(sparseBooleanArray[2]) // false


🚩`LongSparseArray<T>` (замена `HashMap<Long, T>`)

Оптимизирован для Long → Any?, аналог SparseArray, но с Long ключами.
import android.util.LongSparseArray

val longSparseArray = LongSparseArray<String>()
longSparseArray.put(10000000000L, "Длинный ключ")

println(longSparseArray[10000000000L]) // Длинный ключ


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как можно понять, что класс является наследником?

Если он использует : и указывает имя другого класса или интерфейса. Также можно посмотреть в IDE или через is/instanceof в рантайме.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊7👍1🔥1
🤔 Можешь привести пример, когда android фреймворк использует паттерн Factory?

Да, Android Framework активно использует паттерн Factory в различных API. Один из самых известных примеров — LayoutInflater.

🚩Пример: `LayoutInflater` (Фабричный метод)

В Android для создания (инстанцирования) UI-компонентов из XML используется LayoutInflater, который реализует паттерн Factory Method. Вместо того чтобы вручную создавать объекты View, система предоставляет фабричный метод inflate(), который "производит" экземпляры View.

🚩Как работает?

В XML описан интерфейс.
LayoutInflater загружает XML и создает соответствующие объекты View.
Это абстрагирует создание UI-компонентов от разработчика.
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.custom_layout, parent, false)


🚩Другие примеры использования Factory в Android

🟠`MediaPlayer.create()`
Вместо MediaPlayer() напрямую используется MediaPlayer.create(context, R.raw.sound), который автоматически создаёт и настраивает объект.

🟠`PreferenceManager.getDefaultSharedPreferences()`
Позволяет получить SharedPreferences без необходимости вручную создавать экземпляр.

🟠`Fragment.instantiate()`
Фабричный метод для создания Fragment без явного вызова конструктора.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Какие потоки есть в RxJava?

1.
Schedulers.io(): для ввода-вывода, работы с сетью или файлами.
2. Schedulers.computation(): для интенсивных вычислений, таких как обработка данных или рендеринг.
3. Schedulers.newThread(): для запуска новых потоков.
4. Schedulers.single(): для выполнения задач в одном потоке.
5. AndroidSchedulers.mainThread(): для работы с главным (UI) потоком на Android.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
Forwarded from easyoffer
Новая фича на easyoffer Автоотлики

Вы автоматически откликаетесь на подходящие вам вакансии. Попробуйте её бесплатно и начните получать больше предложений о работе.

🚀 Запуск занимаем всего 3 минуты, а экономит очень много времени
🛡 Это безопасно: easyoffer официально одобрен HeadHunter и прошел его модерацию.
🥷🏻 Автоотклик незаметен для рекртера. Автоотклик ничем не отличается от обычного отклика, который вы делаете вручную

Рекрутеры давно используют автоматизацию для поиска кандидатов. Так почему вы должны откликаться вручную?

💡Совет – Добавьте шаблон сопроводительного письма, чтобы откликаться на большее количество вакансий (на некоторые вакансии нельзя откликнуться без сопроводительного)

Попробовать бесплатно → https://easyoffer.ru/autoapply
🤔 Как в Ретрофите в гет методе поставить атрибут в путь в контретом месте?

В Retrofit, чтобы передать значение в определенное место URL-адреса для GET-запроса, нужно использовать аннотацию @Path. Эта аннотация позволяет заменить параметр в строке пути на значение, переданное в метод.

🚩Как работает `@Path`?

1. Внутри аннотации @GET вы указываете строку пути, содержащую плейсхолдеры в фигурных скобках ({}).
2. Аннотация @Path связывает переменную метода с плейсхолдером в URL.
3. Когда метод вызывается, значение переменной подставляется вместо плейсхолдера в URL.

Допустим, у вас есть API с эндпоинтом
https://api.example.com/users/{id}/details


Настраиваем интерфейс Retrofit
interface ApiService {

@GET("users/{id}/details")
suspend fun getUserDetails(
@Path("id") userId: String
): Response<UserDetails>
}


Теперь, когда вы вызываете этот метод, вы можете передать значение для id, и Retrofit автоматически подставит его в URL
val apiService = retrofit.create(ApiService::class.java)

suspend fun fetchUserDetails() {
val userId = "123" // Пример значения
val response = apiService.getUserDetails(userId)
if (response.isSuccessful) {
println("User details: ${response.body()}")
} else {
println("Error: ${response.errorBody()?.string()}")
}
}


🚩Дополнительные аспекты использования `@Path`

🟠Как работают несколько параметров?
Вы можете использовать несколько плейсхолдеров в пути и связать их с несколькими параметрами с помощью @Path. Например:
API-эндпоинт
https://api.example.com/users/{userId}/posts/{postId}


Интерфейс Retrofit
interface ApiService {

@GET("users/{userId}/posts/{postId}")
suspend fun getPostDetails(
@Path("userId") userId: String,
@Path("postId") postId: String
): Response<PostDetails>
}


Вызов метода
val response = apiService.getPostDetails("123", "456")
// URL: https://api.example.com/users/123/posts/456


🟠Что если переменная null или пустая?
Если вы передаете null или пустую строку в @Path, это вызовет ошибку, так как Retrofit требует обязательного значения для всех параметров пути. Убедитесь, что передаваемое значение всегда валидно.

🟠Как избежать ошибок при форматировании?
Если значение пути может содержать символы, требующие экранирования (например, пробелы, специальные символы), Retrofit автоматически обработает это с помощью кодировки URL. Это делает использование @Path безопасным.
@GET("search/{query}")
suspend fun search(
@Path("query") searchQuery: String
): Response<SearchResults>


Если вы вызовете метод с поисковым запросом
apiService.search("hello world!")
// Retrofit закодирует URL: https://api.example.com/search/hello%20world%21


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Как называется метод, в котором будет описана работа сервиса?

В Android метод называется onStartCommand() — здесь происходит основная работа Service, если он не IntentService. В JobIntentService аналог — onHandleWork().


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
🤔 Как LeakCanary находит ссылку на уничтоженную Activity?

Использует комбинацию слабых ссылок (WeakReference), анализ кучи и цепочек удержания, чтобы найти ссылки на уничтоженные Activity или другие объекты, которые должны были быть освобождены сборщиком мусора. Вот более подробное объяснение того, как это работает:

🚩Шаги

1⃣Создание слабой ссылки (WeakReference)
Когда Activity (или другой компонент) уничтожается, LeakCanary создает слабую ссылку на этот объект.
val weakReference = WeakReference(activity)


2⃣Ожидание сборки мусора (Garbage Collection)
LeakCanary инициирует сборку мусора, чтобы попытаться освободить уничтоженный объект.
System.gc()
System.runFinalization()


3⃣Проверка слабой ссылки
LeakCanary проверяет, освобождена ли слабая ссылка. Если объект не был освобожден, это указывает на возможную утечку памяти.
if (weakReference.get() != null) {
// Объект все еще удерживается в памяти, вероятна утечка
}


4⃣Снимок кучи (Heap Dump)
Если слабая ссылка не была освобождена, LeakCanary создает снимок кучи (heap dump) для дальнейшего анализа.
val heapDumpFile = createHeapDump()


5⃣Анализ кучи с помощью Shark
LeakCanary использует библиотеку Shark для анализа снимка кучи. Shark проверяет все объекты в куче и их удерживающие ссылки.
val heapAnalyzer = HeapAnalyzer()
val analysis = heapAnalyzer.analyze(heapDumpFile)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
🤔 Зачем нужны методы equals и hashCode?

Методы `equals()` и `hashCode()` в Kotlin и Java используются для сравнения объектов на равенство и для определения хэш-кода объекта, соответственно. Корректная реализация обоих методов необходима, чтобы обеспечить правильное функционирование объекта в коллекциях, таких как HashSet и HashMap.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
🤔 Что такое Activity и для чего это используется?

Это компонент приложения, который предоставляет пользовательский интерфейс (UI), с которым пользователи могут взаимодействовать для выполнения различных действий, таких как набор номера телефона, просмотр фотографий, отправка электронной почты и т. д. Каждая активность представляет собой один экран с пользовательским интерфейсом. Если представить приложение как книгу, то активность будет одной страницей этой книги.

🚩Основное назначение

🟠Предоставление интерфейса пользователя
Основная задача — предоставить макет (layout), который будет содержать все элементы пользовательского интерфейса, такие как кнопки, текстовые поля, изображения и прочее, с которыми пользователь может взаимодействовать.

🟠Взаимодействие с пользователем
Служит в качестве "лица" приложения для взаимодействия с пользователем, обрабатывая пользовательские входные данные, такие как нажатие кнопок, ввод текста и т. д.

🟠Управление жизненным циклом
Android управляет активностями через заданный жизненный цикл, который определяет, как активность создается, запускается, останавливается и уничтожается. Разработчики могут переопределять методы жизненного цикла, чтобы добавить свою логику обработки для разных состояний активности.

🟠Переход между экранами
В приложении обычно есть несколько активностей, и он используется для перехода от одного экрана к другому. Для перехода между активностями используются интенты (Intents), которые не только помогают открыть новую активность, но и могут передавать данные между активностями.

🟠Взаимодействие с другими компонентами приложения
М
ожет взаимодействовать с другими компонентами приложения, такими как Services, BroadcastReceivers, и ContentProviders, используя интенты и другие механизмы Android для межкомпонентного взаимодействия.

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1💊1
🤔 Что известно про базовый класс для всех объектов в Kotlin?

Базовый класс — это Any. Он является корнем иерархии типов. Все классы неявно наследуются от него, если не указано другое.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1💊1
🤔 Почему могут быть проблемы со списками в data class?

В data class могут возникнуть проблемы со списками при использовании:
Методов copy() – список не копируется, а передаётся по ссылке.
Методов equals() и hashCode()List сравнивается по элементам, что может быть медленно.
Mutable списков (MutableList) – изменения внутри списка изменяют все копии объекта.

🚩Проблема `copy()` – список не клонируется

data class User(val name: String, val tags: List<String>)

fun main() {
val original = User("Alice", listOf("Admin", "Editor"))
val copy = original.copy() // Поверхностное копирование

println(original.tags === copy.tags) // true (один и тот же объект)
}


Используем toList(), чтобы создать новый неизменяемый список:
fun main() {
val original = User("Alice", listOf("Admin", "Editor"))
val copy = original.copy(tags = original.tags.toList())

println(original.tags === copy.tags) // false (разные объекты)
}


🚩`equals()` и `hashCode()` могут работать медленно

data class Message(val id: Int, val content: String, val tags: List<String>)

fun main() {
val message1 = Message(1, "Привет", listOf("important", "urgent"))
val message2 = Message(1, "Привет", listOf("important", "urgent"))

println(message1 == message2) // true (сравнивает элементы списка)
}


Если идентификатор (id) уникален, сравнивать только его
data class Message(val id: Int, val content: String, val tags: List<String>) {
override fun equals(other: Any?) = other is Message && this.id == other.id
override fun hashCode() = id.hashCode()
}


🚩`MutableList` в `data class` – неожиданные изменения

data class Task(val name: String, val subtasks: MutableList<String>)

fun main() {
val original = Task("Купить продукты", mutableListOf("Хлеб", "Молоко"))
val copy = original.copy() // Поверхностная копия

copy.subtasks.add("Яйца") // Меняет список в обоих объектах!

println(original.subtasks) // [Хлеб, Молоко, Яйца]
println(copy.subtasks) // [Хлеб, Молоко, Яйца]
}


Создаём новый MutableList внутри copy()
data class Task(val name: String, val subtasks: List<String>) {
fun deepCopy() = Task(name, subtasks.toMutableList())
}

fun main() {
val original = Task("Купить продукты", mutableListOf("Хлеб", "Молоко"))
val copy = original.deepCopy()

copy.subtasks.toMutableList().add("Яйца") // Теперь изменения не влияют на оригинал

println(original.subtasks) // [Хлеб, Молоко]
println(copy.subtasks) // [Хлеб, Молоко, Яйца]
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Почему могут быть проблемы со списками в data class?

Списки — это ссылочные типы, и если в data class вложен изменяемый список (MutableList), то:
- при копировании через copy() копируется ссылка, а не сам список;
- equals и hashCode могут работать некорректно, если список изменился после создания.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
🤔 Как называется лейаут в котором объекты могут наслаиваться друг на друга?

В Android для наложения (перекрытия) элементов друг на друга используется FrameLayout или Box (в Jetpack Compose).

🚩FrameLayout (в XML и View)

FrameLayout — это контейнер, в котором все вложенные элементы располагаются в левом верхнем углу, но при этом могут накладываться друг на друга.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/background" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Наложенный текст"
android:textSize="24sp"
android:textColor="#FFFFFF"
android:layout_gravity="center"/>
</FrameLayout>


🚩Box (в Jetpack Compose)

В Jetpack Compose аналогом FrameLayout является Box. Он также позволяет располагать элементы друг над другом.
Box(
modifier = Modifier.fillMaxSize()
) {
Image(
painter = painterResource(id = R.drawable.background),
contentDescription = "Фон",
modifier = Modifier.fillMaxSize()
)

Text(
text = "Наложенный текст",
fontSize = 24.sp,
color = Color.White,
modifier = Modifier.align(Alignment.Center)
)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2