Kotlin | Вопросы собесов
2.57K subscribers
28 photos
960 links
Download Telegram
🤔 Какие потоки есть в 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
🤔 Как в Kotlin можно сделать несколько конструкторов в одном классе?

Через:
- primary constructor + init блок,
- secondary constructor с ключевым словом constructor,
- значения по умолчанию в параметрах.


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

В Android можно создавать интерфейс без XML с помощью Jetpack Compose или программного кода (View в Kotlin/Java).

🚩Jetpack Compose — современный декларативный подход

Jetpack Compose — это новый способ создания UI в Android без XML, основанный на Kotlin.
@Composable
fun GreetingScreen() {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Привет, мир!", fontSize = 24.sp)
Button(onClick = { println("Кнопка нажата") }) {
Text("Нажми меня")
}
}
}

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
GreetingScreen()
}
}
}


🚩Создание UI через View в Kotlin (старый способ)

Если Jetpack Compose не подходит, можно создать интерфейс программно, без XML, используя стандартные View.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Создаём контейнер (LinearLayout)
val layout = LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}

// Создаём текст
val textView = TextView(this).apply {
text = "Привет, мир!"
textSize = 24f
setTextColor(Color.BLACK)
}

// Создаём кнопку
val button = Button(this).apply {
text = "Нажми меня"
setOnClickListener {
textView.text = "Кнопка нажата!"
}
}

// Добавляем элементы в контейнер
layout.addView(textView)
layout.addView(button)

// Устанавливаем UI
setContentView(layout)
}
}


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

Handler используется вместе с Looper, чтобы поставить задачу в очередь исполнения в определённом потоке. Если ты создаёшь Handler, привязанный к конкретному Looper, все отправленные сообщения и Runnable выполняются по очереди, синхронно внутри этого потока, но асинхронно по отношению к другим потокам. То есть Handler помогает упорядочить выполнение задач внутри одного потока.

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

В Kotlin есть множество удобных функций для получения одного элемента или списка элементов из коллекций (List, Set, Map). Какую функцию использовать — зависит от задачи.

🚩Получение одного элемента

🟠`get(index)` и `getOrNull(index)`
получение по индексу
Если индекс может выйти за границы массива, лучше использовать getOrNull().
val list = listOf("A", "B", "C")

println(list[1]) // "B"
println(list.getOrNull(5)) // null


🟠`first()` и `last()`
первый и последний элементы
println(list.first())  // "A"
println(list.last()) // "C"

Используйте firstOrNull() или lastOrNull(), если список может быть пустым
println(emptyList<String>().firstOrNull())  // null


🟠`find {}` и `findLast {}`
поиск по условию Возвращает первый или последний элемент, удовлетворяющий условию.
val numbers = listOf(10, 20, 30, 40)

println(numbers.find { it > 15 }) // 20
println(numbers.findLast { it > 15 }) // 40
println(numbers.find { it > 50 }) // null


🟠`single()` и `singleOrNull()`
если ожидается только один элемент
val oneElementList = listOf(42)
println(oneElementList.single()) // 42
println(oneElementList.singleOrNull()) // 42

val emptyList = emptyList<Int>()
println(emptyList.singleOrNull()) // null

single() бросает исключение, если элементов нет или их больше одного.

🚩Получение нескольких элементов (списка)

filter {} – фильтрация элементов по условию
val numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers) // [2, 4]


take(n) и takeLast(n) – первые/последние n элементов
val items = listOf("a", "b", "c", "d", "e")

println(items.take(3)) // [a, b, c]
println(items.takeLast(2)) // [d, e]


slice(indices) – получение элементов по индексам
val sliced = items.slice(listOf(0, 2, 4))
println(sliced) // [a, c, e]


map {} – преобразование списка
val squared = numbers.map { it * it }
println(squared) // [1, 4, 9, 16, 25]


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

GC отслеживает достижимость объектов из корневых точек (stack, static, thread references). Если объект:
- Не имеет ни одной активной ссылки
- Не достижим по цепочке ссылок
…то он считается неиспользуемым и может быть удалён.
Алгоритмы: mark-and-sweep, tracing, generational collection.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 В чем различие Default диспатчер и IO диспатчер?

В Kotlin Coroutines есть несколько диспетчеров (Dispatchers), но Default и IO используются чаще всего.

🚩Главное различие:

Dispatchers.Default — для тяжёлых вычислений (CPU-операции).
Dispatchers.IO — для операций ввода-вывода (сеть, файлы, БД).

🟠`Dispatchers.Default` – для сложных вычислений (CPU-bound)
Этот диспетчер используется, если код загружает процессор (например, сложные вычисления).
import kotlinx.coroutines.*

fun main() = runBlocking {
launch(Dispatchers.Default) {
val result = heavyComputation()
println("Результат: $result")
}
}

suspend fun heavyComputation(): Int {
delay(1000)
return (1..1_000_000).sum()
}


🟠`Dispatchers.IO` – для работы с файлами, сетью, БД (IO-bound)
Этот диспетчер оптимизирован для ввода-вывода (I/O): работа с файлами, сетью, БД.
import kotlinx.coroutines.*
import java.io.File

fun main() = runBlocking {
launch(Dispatchers.IO) {
val text = File("data.txt").readText()
println("Файл прочитан: $text")
}
}


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

`data class` в Kotlin автоматически генерирует полезные методы, такие как `equals()`, `hashCode()`, `toString()`, и `copy()`, что делает его идеальным для хранения данных. Обычные классы не предоставляют такую функциональность по умолчанию, и все эти методы должны быть реализованы вручную. Data-классы используются для создания объектов, которые содержат только данные, без сложной логики. Это сокращает количество шаблонного кода и улучшает читаемость.

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

В Android существует несколько способов запуска асинхронных операций. Наиболее распространённые из них включают использование AsyncTask (хотя он уже устарел), HandlerThread, AsyncTaskLoader, ExecutorService и современные подходы с использованием Kotlin Coroutines и библиотеки WorkManager.

🟠AsyncTask (устаревший)
AsyncTask был одним из первых инструментов для выполнения асинхронных задач, однако его использование сейчас не рекомендуется из-за проблем с управлением жизненным циклом и утечками памяти.
private class MyAsyncTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... voids) {
// Выполнение длительной операции
return "Result";
}

@Override
protected void onPostExecute(String result) {
// Обновление UI после завершения операции
}
}

// Запуск
new MyAsyncTask().execute();


🟠HandlerThread
HandlerThread — это удобный способ создания фонового потока с циклом обработки сообщений.
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());

handler.post(new Runnable() {
@Override
public void run() {
// Выполнение длительной операции
}
});


🟠ExecutorService
ExecutorService из стандартной библиотеки Java предоставляет более гибкий способ управления потоками.
ExecutorService executor = Executors.newSingleThreadExecutor();

executor.submit(new Runnable() {
@Override
public void run() {
// Выполнение длительной операции
}
});


🟠Kotlin Coroutines
Корутины в Kotlin — это современный и мощный инструмент для асинхронного программирования. Они упрощают работу с асинхронными задачами и обеспечивают безопасность работы с UI.
import kotlinx.coroutines.*

fun performAsyncTask() {
GlobalScope.launch(Dispatchers.IO) {
// Выполнение длительной операции
val result = longRunningTask()

withContext(Dispatchers.Main) {
// Обновление UI после завершения операции
}
}
}

suspend fun longRunningTask(): String {
delay(1000) // Симуляция длительной операции
return "Result"
}


🟠WorkManager
WorkManager — это библиотека для выполнения фоновых задач, которая обеспечивает выполнение задач даже после закрытия приложения или перезагрузки устройства.
public class MyWorker extends Worker {
public MyWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}

@NonNull
@Override
public Result doWork() {
// Выполнение длительной операции
return Result.success();
}
}

// Запуск Worker
WorkManager workManager = WorkManager.getInstance(context);
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class).build();
workManager.enqueue(request);


🟠RxJava
RxJava — это библиотека для реактивного программирования, которая также может быть использована для выполнения асинхронных задач.
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.schedulers.Schedulers;

Observable.fromCallable(() -> {
// Выполнение длительной операции
return "Result";
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
// Обновление UI после завершения операции
});


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

Это область памяти, управляемая сборщиком мусора.
GC следит за тем, какие объекты ещё используются, и очищает неиспользуемые (недостижимые).


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

При пересоздании Activity (например, при повороте экрана) состояние ScrollView или RecyclerView сбрасывается. Чтобы этого избежать, можно сохранить и восстановить позицию скролла.

🟠Использование `onSaveInstanceState()`
Android позволяет сохранять данные в Bundle перед уничтожением Activity, а затем восстанавливать их.
class MainActivity : AppCompatActivity() {
private lateinit var scrollView: ScrollView

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

scrollView = findViewById(R.id.scrollView)

// Восстанавливаем позицию скролла
savedInstanceState?.let {
val scrollY = it.getInt("scroll_position", 0)
scrollView.post { scrollView.scrollTo(0, scrollY) }
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("scroll_position", scrollView.scrollY)
}
}


🟠Для `RecyclerView`
У RecyclerView уже есть встроенный механизм сохранения состояния через LinearLayoutManager.
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var layoutManager: LinearLayoutManager

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

layoutManager = LinearLayoutManager(this)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = MyAdapter()

// Восстанавливаем позицию скролла
savedInstanceState?.let {
val scrollPosition = it.getInt("recycler_position", 0)
recyclerView.post { layoutManager.scrollToPosition(scrollPosition) }
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("recycler_position", layoutManager.findFirstVisibleItemPosition())
}
}


🟠Использование `android:freezesText` для `EditText`
Если EditText находится внутри ScrollView, можно включить android:freezesText="true", чтобы автоматически сохранять данные.
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:freezesText="true"/>


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