Как стать автором
Обновить

Комментарии 16

Спасибо, интересная реализация.
Думаю, было бы неплохо такую фишку стандартизировать, чтобы пользователям не нужно было запоминать свайпы для каждого приложения. Желательно в виде библиотеки, где разработчикам всего то и нужно, что наследовать свои Activity от вашей реализации.
Ну и еще я думаю, что свайп слева как в iOS был бы лучше, чем сверху, так как достать туда проще. Да и кнопка и стрелка «назад» показывают именно влево.
А, ну еще так как большинство приложений являются по сути вертикальными списками с различным контентом, то свайп сверху действительно неуместен в большинстве случаев.
Конкретно в этой реализации необязательно тянуть палец до самого заголовка. Свайп можно начать с любого места.
Атрибут android:windowIsTranslucent приведет к тому, что перекрываемая activity не перейдет в stopped состояние.

Добрый день! Именно для этого мы его и написали — чтобы задняя activity была видна в любой момент времени.

По этому наверно правильнее делать на фрагментах, так как они в любом случае остаются видимыми.

так как они в любом случае остаются видимыми

Это зависит от того, как мы совершаем транзакцию: add или replace. Replace нам удалит предыдущий фрагмент с экрана, так что он тоже не будет видимым и работать это не будет. Add добавит фрагмент поверх предыдущего, однако предыдущий фрагмент не получит onPause()/onResume() колбэков, в отличие от примера с activity. Если использовать подход с фрагментами, то лучше сразу использовать BottomSheetFragmentDialog из дизайнерской либы.

Я бы сказал, что в общем случае лучше руководствоваться здравым смыслом. Если контент фрагментов слишком разный (стили, цвета, набор иконок в тулбаре, наличие и отсутствие бокового меню, интеграция с context), то объединение их в одну активити ради scroll-to-dismiss принесет больше проблем, чем профита.

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


Я понимаю, что сама задача требует активного предыдущего экрана под текущим, но в хорошей реализации должно быть учтено, что оставаться должен только ОДИН предыдущий экран.


Есть компромиссное решение — делать скриншот экрана, но тут проблем еще больше, ибо довольно часто при переходе назад экран будет уже не соответствовать скриншоту

все активити остаются висеть в памяти в активном состоянии.

Почему все? Только активная и предыдущая
Плюс при повороте будет создаваться весь стек активити

Как и активных фрагментов
и это заметно

Если это заметно, скорее всего активити/фрагмент делают больше, чем им положено
отсутствие вызова онСтоп не позволяет впрямую обработать переход на новый экран (остановить какой-нибудь поллинг)

Для этого есть onPause
Есть компромиссное решение — делать скриншот экрана

Был и такой вариант :) Под кодовым названием План Y :)
Почему все? Только активная и предыдущая

если открыть третье активити с возможностью закрытия свайпом, то и первое и второе и третье будут активными. и так далее.


Активити создаются заметно дольше фрагментов. Никаких тяжелых операций в них запускать для этого не надо, достаточно взять не самый топовый девайс и можно определять на глаз, где фрагменты, а где активити.


А вообще хорошая статья, кому-то точно поможет разобраться)

Очевидно, что другого способа добиться требуемого эффекта с activity не существует, а программно этим управлять нельзя. В общем это я к тому, что c activity scroll-to-dismiss оптимально реализовать не получится.
Спасибо за статью, очень познавательно.
Но не получается добавить затемнение.
Может можно увидеть живой проект на гите?

Вот кусок кода. У меня слайд вправо, так что код немного изменен.
abstract class SlidingActivity : AppCompatActivity() {

   var windowScrim: ColorDrawable = ColorDrawable(Color.argb(0xE0, 0, 0, 0))
   <...>
   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
         window.statusBarColor = Color.TRANSPARENT
      }

      windowScrim.alpha = 0
      window.setBackgroundDrawable(windowScrim)
   }

   private fun updateScrim() {
        Log.v(TAG, "updateScrim")
        val progress = root.x / screenSize.x
        val alpha = (progress * 255f).toInt()
        windowScrim.alpha = 255 - alpha
        this.window.setBackgroundDrawable(windowScrim)
    }

   override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
      <...>
      when (ev.action) {
         MotionEvent.ACTION_MOVE -> {
            if ((isSlidingRight(startX, startY, ev) && canSlideRight()) || isSliding) {
                  updateScrim()
                  <...>
            }
         }
      }
   }
}


Все работает отлично, при сдвиге updateScrim() в логе отрабатывается тоже, но затемнения не происходит.
Может быть, я что-то упускаю?

Добрый день! С виду все правильно. Нужно убедиться, что updateScrim() вызывается на каждое событие ACTION_MOVE и при завершающей анимации. В самой функции updateScrim() стоит проверить, что alpha действительно считается правильно и меняется от 0 до 255.
На гитхабе, к сожалению, проекта нет, зато можем показать гист :) https://gist.github.com/forceLain/49e02e8d772e99707ebda9137b3b6ccb

Спасибо!
Но даже полностью неизмененный код затемнение не делает.
Видимо что-то глубже.
Буду искать.
Разобралась. Моя ошибка была в том, что не тот view отправляла в обработку SlidingActivity:)

рады, что все получилось)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий