image Одним из краеугольных камней в Material design являются осмысленные движения между экранами. Lollipop предоставляет поддержку этих анимаций в форме фреймворка переходов между Activity и Fragment. Поскольку статей по данной теме не так много, я решил написать свою собственную!

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

Если вы желаете увидеть, что получилось — готовое приложение находится на GitHub.

Поддержка предыдущих версий Android?


У меня есть для вас две новости: хорошая и плохая. Плохая новость заключается в том, что до Lollipop данный фреймворк не работает. Не смотря на это, проблема решается методами библиотеки поддержки с помощью которой вы можете реализовать анимированые переходы доступные в API 21+.

В статье будут использоваться функции из библиотеки поддержки для обеспечения перемещения контента.

Имена переходов


Для ассоциации View на первом экране и его двойника на втором нужна связь. Lollipop предлагает использовать свойство “transition name” для связи View между собой.

Существует два способа добавления имени перехода (transition name) для ваших View:

  • В коде можно использовать ViewCompat.setTransitionName(). Конечно, вы так же можете просто вызвать setTransitionName(), если поддержка начинается с Lollipop.
  • Для добавления в XML, используйте атрибут android:transitionName.

Важно отметить, что внутри одного макета (layout), имена переходов должны быть уникальны. Держите это в уме при организации переходов. Указывая transition name для ListView или RecyclerView задаст это же имя и для всех остальных элементов.

Настройка FragmentTransaction


Настройка FragmentTransactions должна быть вам очень знакомой:

getSupportFragmentManager()
        .beginTransaction()
        .addSharedElement(sharedElement, transitionName)
        .replace(R.id.container, newFragment)
        .addToBackStack(null)
        .commit();

Чтобы указать какую View будем передавать между фрагментами — используем метод addSharedElement().

Переданная View в addSharedElement() это View из первого фрагмента, которую вы хотите разделить (share) со вторым фрагментом. Имя перехода тут является именем перехода в разделенной (shared) View во втором фрагменте.

Настройка анимации перехода


Наконец-то пришел момент, когда мы зададим анимацию перехода между фрагментами.

Для shared элементов:

  • Для перехода с первого фрагмента во второй используем метод setSharedElementEnterTransition().
  • Для возврата назад используем метод setSharedElementReturnTransition(). Анимация произойдет при нажатии кнопки назад.

Заметьте, что вам необходимо вызвать эти методы во втором фрагменте, поскольку, если вы сделаете это в первом — ничего не произойдет.

Вы так же можете анимировать переходы для всех non-shared View. Для этих View, используйте setEnterTransition(), setExitTransition(), setReturnTransition(), и setReenterTransition() в соответствующих фрагментах.

Каждый из этих методов принимает один параметр Transition предназначенный для выполнения анимации.

Создавать анимацию мы будем очень просто. Мы используем наш кастомный transition для передвижения изображения (об этом чуть позже), и исчезание (Fade) при выходе.

Классы анимации перехода


Android предоставляет некоторые готовые анимации переходов, что подходят для большинства случаев. Fade выполняет анимацию исчезновения. Slide анимирует переход появления/исчезновения скольжением из угла экрана. Explode анимация подобная взрыву, изображение движется от краев экрана. И наконец, AutoTransition заставит изображение исчезать, двигаться и изменять размер. Это лишь некоторые примеры из пакета перемещений, их на самом деле намного больше!

Я упоминал, что нам понадобится кастомный переход для нашего изображения. Вот он:

public class DetailsTransition extends TransitionSet {
    public DetailsTransition() {
        setOrdering(ORDERING_TOGETHER);
        addTransition(new ChangeBounds()).
                addTransition(new ChangeTransform()).
                addTransition(new ChangeImageTransform()));
    }
}

Наш кастомный переход есть ни что иное как набор из трех готовых переходов собранных вместе:

  • ChangeBounds анимирует границы (положение и размер) нашей view.
  • ChangeTransform анимирует масштаб view, включая родителя.
  • ChangeImageTransform позволяет нам изменять размер (и/или тип масштаба) нашего изображения

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

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

Все вместе


Код который в итоге у нас получился оказался достаточно простым:

DetailsFragment details = DetailsFragment.newInstance();

// Note that we need the API version check here because the actual transition classes (e.g. Fade)
// are not in the support library and are only available in API 21+. The methods we are calling on the Fragment
// ARE available in the support library (though they don't do anything on API < 21)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    details.setSharedElementEnterTransition(new DetailsTransition());
    details.setEnterTransition(new Fade());
    setExitTransition(new Fade());
    details.setSharedElementReturnTransition(new DetailsTransition());
}

getActivity().getSupportFragmentManager()
        .beginTransaction()
        .addSharedElement(holder.image, "sharedImage")
        .replace(R.id.container, details)
        .addToBackStack(null)
        .commit();

Вот и все! Простой способ реализации анимации переходов между двумя фрагментами готов!

Комментарии (10)


  1. withoutuniverse
    08.11.2015 20:29
    -1

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


    1. AStefanovskiy
      08.11.2015 22:24

      Подредактировал.


  1. Mishok2000
    08.11.2015 22:49

    Честно говоря всегда бесила данная анимация в новом Андроиде. Она не имеет никакого реального смысла, так как мы понимаем, что в реальном мире та или иная карточка, или объект не может плавно появиться, или же испариться.
    Тогда какую анимацию надо вставить? Я лично не знаю. Был бы очень рад услышать ответ на мой вопрос от профессиональных дизайнеров, да и вообще от кого угодно :)


    1. PapaBubaDiop
      08.11.2015 23:54
      -2

      В Библии изложен сценарий анимации.
      1. Сначала было слово. (Label)

      Плюс 10 поправок от Диснея.


    1. Lure_of_Chaos
      09.11.2015 01:57

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

      А по существу — я бы рекомендовал использовать улучшенный эффект — не смену, а превращение картинки. и то, в более похожих картинках. было бы ближе к средневековому волшебству :)


    1. gwer
      09.11.2015 10:17

      Такая анимация крайне сомнительна для Material design. Все, что может происходить, описано в гайде от гугла. Куда логичнее будет рассматривать фото как карточку, которая сначала масштабируется вместе с фото, а затем растягивается отдельно (только фон), и уже на пустом ее месте отображается текст.


      1. Mishok2000
        09.11.2015 10:45

        Да, хорошая идея. В воображении смотрится отлично. Спасибо


        1. gwer
          09.11.2015 10:57

          Гуглу спасибо. Все придумано до нас.


  1. petrovichtim
    09.11.2015 13:03

    Спасибо за статью. Сделал как написано, картинка перелетает, но вот обратно фрагмент растворяется.В чем может быть проблема?


    1. petrovichtim
      10.11.2015 17:43

      Из-за Loader не работала вот рассказали