Расскажи про самые интересные задачи, которые решал

Ответ

Разрабатывал кастомный View для анимированного графика с жестами масштабирования/прокрутки. Использовал Canvas, ValueAnimator и обработку MotionEvent для плавного UX.

Оптимизировал работу с RecyclerView в ленте новостей:

  • Дифференциальные вычисления с DiffUtil
  • Прелоадинг изображений через Glide с приоритезацией видимых элементов
  • Кастомный LayoutManager для сложных grid-раскладок

Пример:

val diffCallback = object : DiffUtil.ItemCallback<NewsItem>() {
    override fun areItemsTheSame(old: NewsItem, new: NewsItem) = old.id == new.id
    override fun areContentsTheSame(old: NewsItem, new: NewsItem) = old == new
}
listAdapter.submitList(newData) { /* UI update callback */ }

Ответ 18+ 🔞

Ну вот, сидишь такой, думаешь: «Сделаю-ка я кастомную вьюху для графика, с анимациями, с жестами — будет красиво, пользователи ахнут». А потом начинается ёперный театр. Берёшь Canvas, начинаешь рисовать, потом подключаешь ValueAnimator, чтобы всё плавненько двигалось. А потом доходишь до обработки MotionEvent для масштабирования и прокрутки — и тут волнение ебать! Чувствуешь себя богом интерфейсов, пока не наткнёшься на баг, когда два пальца работают как хуй с винтом. Но в итоге, если не облажаться, UX получается — просто овердохуища плавный.

А потом вспоминаешь про ленту новостей на RecyclerView, которая тормозит как мартышлюшка в зоопарке. Тут уже терпения ноль ебать, надо оптимизировать. Первым делом — дифы. DiffUtil — вещь, конечно, хитрая жопа, но когда он сам вычисляет, что поменялось, а не перерисовывает всё подряд, это просто песня. Экономит кучу ресурсов.

Потом картинки. Glide — наш всему голова. Но если ему не сказать, он будет прелоадить всё подряд, а тебе надо только то, что пользователь видит или вот-вот увидит. Ставишь приоритет на видимые элементы — и вуаля, скролл летает.

Ну и верх пирога — кастомный LayoutManager для этих ваших сложных grid-раскладок. Тут, конечно, можно с ума сойти. Пишешь его, отлаживаешь, а он элементы расставляет как попало. Сам от себя охуеваешь, пока не найдёшь ту самую строчку с ошибкой в расчёте размеров. Но зато когда заработает — красота.

Вот, смотри, кусочек кода для дифов, чтобы не быть голословным:

val diffCallback = object : DiffUtil.ItemCallback<NewsItem>() {
    override fun areItemsTheSame(old: NewsItem, new: NewsItem) = old.id == new.id
    override fun areContentsTheSame(old: NewsItem, new: NewsItem) = old == new
}
listAdapter.submitList(newData) { /* UI update callback */ }

Выглядит просто, да? Но эта простота — она обманчивая. Главное — правильно equals() и hashCode() переопределить в своей модели NewsItem, а то areContentsTheSame будет врать, и тогда тебе будет не до смеха. Проверено, как говорится, на своей шкуре. В общем, делай всё с умом, и будет тебе счастье, а не пиздопроебибна с лагами.