Добавление анимации на основе физики в Android приложения. Создание анимаций переходов между Activity в Android

В этой статье рассмотрим как сделать анимацию элементов интерфейса в android. Под элементами интерфейса в данном случае имеются в виду все наследники класса View (полный список наследников можно посмотреть в документации класса View). Анимация это простой способ сделать приложение более живеньким:)

1. Начнем с создания тестового полигона. Сделаем простое приложение с кнопкой и картинкой посередине экрана. Код приводить не буду, он простой, если что, смотрите в исходниках (они в конце статьи).

2. В директории /res/anim создадим файл anim.xml и напишем туда
< set xmlns:android=" http : // schemas.android.com /apk/res/android " android:shareInterpolator=" false " > < alpha android:fromAlpha=" 0.0 " android:toAlpha=" 1.0 " android:duration=" 1000 " />
Это описание анимации, которое мы будем применять к нашей картинке. Подробнее что тут происходит рассмотрим ниже, а пока просто скопируем это в файл.

3. Чтобы загрузить анимацию из xml файла используется статический метод класса AnimationUtils
loadAnimation(Context context, int id) , где context - текущий контекст, а id - идентификатор ресурса с анимацией. Метод возвращает экземпляр класса Animation.
Animation - абстрактный класса для представления анимации в приложении.
Чтобы применить ее, полученный экземпляр класса Animation передается методу
startAnimation(Animation animation) класса View (и всех его наследников).

4. Напишем в файл AnimationTestActivity.java:
public class AnimationTestActivity extends Activity { ImageView image; Button button; Animation anim; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); image = (ImageView)findViewById(R.id.image); button = (Button )findViewById(R.id.button); anim = AnimationUtils.loadAnimation(this , R.anim.anim); // 1 button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { image.startAnimation(anim); //2 } }); } }
1) Читаем файл с идентификатором R.anim.anim (что соответствует файлу /res/anim/anim.xml) и получаем экземпляр класса Animation.
2) По нажатию на кнопку применяем анимацию для изображения.

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

6. Теперь рассмотрим подробно как же создается анимация в xml файле.
Существует 4 вида анимации:


  • alpha (прозрачность, видимость)

  • scale (масштабирование)

  • rotate (поворот)

  • translate (перемещение)

Для создания анимации мы должны описать начальное и конечное состояние объекта, а система сама решит как перейти из одного состояния в другое. В нашем примере
< alpha android:fromAlpha=" 0.0 " android:toAlpha=" 1.0 " android:duration=" 1000 " />
мы описываем анимацию alpha, то есть изменяем видимость объекта. Задаем начальное состояние fromAlpha="0.0" (полностью невидимое) и конечное toAlpha="1.0" (полностью видимое). Указываем продолжительность анимации duration="1000" (в миллисекундах). А все остальное, то есть как нужно изменять видимость объекта чтобы за секунду сделать его из невидимого в видимое, система делает сама. Рассчитывается это с помощью интерполяции - в вычислительной математике способ нахождения промежуточных значений величины по имеющемуся дискретному набору значений. Для каждой анимации можно задать интерполятор
-AccelerateDecelerateInterpolator (@android:anim/accelerate_decelerate_int erpolator) - скорость изменения в начале и конце низкая, а в середине ускоряется

-AccelerateInterpolator (@android:anim/accelerate_interpolator) - скорость изменения в начале низкая, а затем ускоряется

-AnticipateInterpolator (@android:anim/anticipate_interpolator) - изменения начинаются в обратную сторону, а затем резко двигаются вперед

-AnticipateOvershootInterpolator (@android:anim/anticipate_overshoot_inte rpolator) - изменения начинаются в обратную сторону, затем резко двигаются вперед и пролетают выше конечного значения, а затем возвращаются до конечного значения

-BounceInterpolator (@android:anim/bounce_interpolator) - скорость изменения увеличивается в конце

-CycleInterpolator (@android:anim/cycle_interpolator) - повторение анимации указанное число раз. Скорость изменения следует синусоиде

-DecelerateInterpolator (@android:anim/decelerate_interpolator) - скорость изменения уменьшается в конце

-LinearInterpolator (@android:anim/linear_interpolator) - скорость изменения постоянна

-OvershootInterpolator (@android:anim/overshoot_interpolator) - изменения резко двигаются вперед и пролетают выше конечного значения, а затем возвращаются до конечного значения

Задается интерполятор с помощью атрибута android:interpolator. Например
android:interpolator="@android:anim/cycl e_interpolator" . По умолчанию используется LinearInterpolator.

7. Описание начальных и конечных состояний
1) alpha (прозрачность, видимость)
- android:fromAlpha - начальное значение прозрачности. 0.0 - полностью прозрачное (невидимое), 1.0 - полностью непрозрачное (видимое)
- android:toAlpha - конечное значение прозрачности

2) scale (масштабирование)
- android:fromXScale - начальное значение масштаба по оси X (где текущий размер соответствует значению 1.0)
- android:toXScale - конечное значение масштаба по оси X
- android:fromYScale - начальное значение масштаба по оси Y (где текущий размер соответствует значению 1.0)
- android:toYScale - конечное значение масштаба по оси Y
- android:pivotX - х координата точки, которая останется неизменна после масштабирования
- android:pivotY - y координата точки, которая останется неизменна после масштабирования

Возможные значение pivotX и pivotY:
в пикселях относительно левого (или верхнего для координаты Y) края элемента (например «5»)
в процентах относительно левого (верхнего) края (например «5%»)
в процентах относительно левого (верхнего) края родительского элемента (например «5%p»)

Например, если pivotX=0, pivotY=0 (что соответствует верхнему левому углу элемента), то масштабирование будет изменять размер элемента вниз и вправо. Если pivotX=50%, pivotY=50%, то точка находится по центру элемента и размер изменяется во все сторону, при этом центр будет оставаться а в одной точке.

3) rotate (поворот)
- android:fromDegrees - Начальное значение угла поворота (в градусах, возможно отрицательное значение)
- android:toDegrees - конечное значение угла поворота
- android:pivotX - x координаты центра поворота.
- android:pivotY - y координата центра поворота.
Возможные значения pivotX и pivotY как у анимации scale

4) translate (перемещение)
- android:fromXDelta - x координата начальной точки перемещения. Возможные значения:
в пикселях относительно изначальной позиции (например «5»)
в процентах относительно ширины элемента (например «5%»)
в процентах относительно ширины родительского элемента (например «5%p»)
- android:toXDelta - x координата конечной точки перемещения
- android:fromYDelta - y координата начальной точки перемещения
- android:toYDelta - y координата конечной точки перемещения

8. Дополнительные параметры
Также есть атрибуты общие для всех четырех типов анимации, наиболее полезные из них:
- android:duration - длительность анимации (в миллисекундах)
- android:interpolator - определяет интерполятор для анимации
- android:repeatCount - кол-во дополнительных повторений анимации. Именно дополнительных, то есть один раз анимация выполнится по любому. Значением по умолчанию является «0» - это значит анимация выполнится только один раз. Значение «1» значит, что анимация выполнится два раза (один раз основной и один раз дополнительный). Значение «-1» или «infinite» - бесконечный повтор.
- android:repeatMode - определяет поведение анимации, когда она дошла до конца, а параметр repeatCount не равен 0. Есть два значения «restart» - анимация начинается заново и «reverse» - анимация пойдет в обратном порядке.
- android:startOffset - задержка перед началом анимации (в миллисекундах)

9. Объединение нескольких анимаций
К элементу можно применить одновременно несколько типов анимаций. Например если мы напишем:
< set xmlns:android=" http : // schemas.android.com /apk/res/android " > < alpha android:fromAlpha=" 0.0 " android:toAlpha=" 1.0 " android:duration=" 1000 " /> < rotate android:fromDegrees=" 0 " android:toDegrees=" 360 " android:pivotX=" 50% " android:pivotY=" 50% " android:duration=" 1000 " />
Картинка за 1 секунду изменит прозрачность (с полностью прозрачной до непрозрачной) и при этом повернется на 360 градусов.

Анимациям можно выставлять разную длительность, например поставим duration=5000 у анимации rotate. Теперь картинка будет поворачиваться гораздо медленнее, а прозрачность меняется все-также за секунду.

С помощью startOffset , можно сделать анимации последовательными. Добавим к rotate атрибут startOffset="1000" (то есть сделаем задержку равную длительности первой анимации). Теперь картинка вначале за 1 секунду станет видимой, а затем только повернется на 360 градусов.

Несколько анимаций можно объединять в наборы тегом . Один такой тег будет в файле всегда и является корневым. Для набора можно задавать следующие атрибуты:
- duration (длительность), repeatMode (режим повторения) - эти атрибуты будут применяться для каждой анимации в наборе
- interpolator - определяет интерполятор анимации и shareInterpolator - будет ли этот интерполятор применятся для каждой анимации в наборе (возможные значения «true» и «false»)
- startOffset (задержка) - задержка для всего набора анимаций.
К сожалению, к набору нельзя применить атрибут repeatCount , то есть повторить несколько раз набор анимаций не получится.
Наборы могут быть любой вложенности.

10. Создание анимации без xml
Анимацию можно создать и без использования xml, непосредственно в коде программы. Для этого используются классы наследники Animation:
1) AlphaAnimation для создания анимации alpha. Конструктор класса имеет вид
AlphaAnimation(float fromAlpha, float toAlpha) где fromAlpha и toAlpha соответственно начальное и конечное значение прозрачности (от 0.0 до 1.0)

11. Создадим в коде анимацию, которая по нажатию на кнопку будет поворачивать картинку на случайный угол (от 0 до 360) и увеличивать до случайного размера (не более чем в два раза). Я для этого добавила еще одну кнопку randomButton
randomButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Random random = new Random (); //1 RotateAnimation rotate = new RotateAnimation (0, (float )random.nextInt(360), Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); //2 rotate.setDuration(1000); //3 rotate.setRepeatMode(Animation.REVERSE); //4 rotate.setRepeatCount(1); //5 long duration = rotate.computeDurationHint(); //6 float size = random.nextFloat() + 1.0; //7 ScaleAnimation scale = new ScaleAnimation(1.0f, size, 1.0f, size, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); //8 scale.setDuration(1000); scale.setStartOffset(duration); //9 AnimationSet set = new AnimationSet (false ); //10 set.addAnimation(rotate); //11 set.addAnimation(scale); image.startAnimation(set); //12 } });
1) Создаем объект Random, для генерации случайных чисел. Про Random подробнее можно прочитать в документации, сейчас нас интересуют методы int nextInt(int n) - генерирующий целое число в диапозоне от 0 до n. И метод float nextFloat() - генерирующий вещественное число от 0 до 1.
2) Создаем анимацию вращения. Начальный угол = 0, конечный угол = случайному числу от 0 до 360. Animation.RELATIVE_TO_SELF означает, что точку центра поворота мы будем указывать в процентах относительно ширины элемента. Не забываем, что значение 1.0 соотвествует 100%, а значит 0.5f - это 50%. Значит точка центра поврота будет посередине картинки.
3) Задаем длительность анимации 1000 миллисекунд (это 1 секунда)
4) Определяем режим повторения как Animation.REVERSE, то есть при повторении анимация пойдем в обратном порядке.
5) Задаем кол-во дополнительных повторений = 1. Значит всего анимация повторится два раза, один раз в прямом порядке и один в обратном.
6) Метод long computeDurationHint() расчитывает сколько суммарно будет продолжаться анимация. Есть метод getDuration(), но он просто возвращает значение длительности, которое мы задали методом setDuration(). В нашем случае мы задали значение длительности 1000 и метод getDuration() вернет 1000 и не учтет, что анимация будет повторяться два раза, а значит в действительности будет продолжаться 2000 миллисекунд. Метод computeDurationHint() рассчитает длительность с учетом повторов и задержек.
7) Расчитываем новый размер картинки. Значение 1.0 это текущий масштаб картинки, значит значение 2.0 - увеличение картинки в два раза. Мы генерируем число от 0.0 до 1.0 и приплюсовываем 1, значит получаем число от 1.0 до 2.0
8) Создаем анимацию масштабирования от текущего размера картинки до случайно сгенерированного число от 1.0 до 2.0
9) Задаем задержку равную суммарной длительности анимации вращения. Чтобы вторая анимация начиналась сразу после окончания первой
10) Создаем набор анимаций.
11) Добавляем две созданные анимации в набор
12) Применяем набор анимаций к картинке

12. Еще один интересный метод класса Animation
setAnimationListener (Animation.AnimationListener listener) - устанавливает слушателя изменений состояний анимации. Интерфейс Animation.AnimationListener определяет следующие методы:
onAnimationStart (Animation animation) - вызывается при старте анимации
onAnimationRestart (Animation animation) - вызывается при повторе анимации
onAnimationEnd (Animation animation) - вызывается по окончании анимации

Например:
anim = AnimationUtils.loadAnimation(this, R.anim.anim); anim.setAnimationListener(new AnimationListener () { @Override public void onAnimationEnd(Animation animation) { Log.d("MY" , "animation end" ); } @Override public void onAnimationRepeat(Animation animation) { Log.d("MY" , "animation repeat" ); } @Override public void onAnimationStart(Animation animation) { Log.d("MY" , "animation start" ); } });
Ничего полезного мы при изменении состояния анимации не делаем, просто пишем это в лог.

На этом все. Основное я рассказала, остальное лучше изучать экспериментами:)

Исходники можно скачать тут

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

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

Если вы хотите попробовать сделать эти анимации сами, каждый из этих примеров упакован в Android приложении на Github.

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

Falcon Pro: Даже едва заметные движения могут показать огромную разницу в пользовательском опыте

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

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

Все в деталях

Мы можем использовать эти эффекты движения разными способами:

  • Переводить пользователей через навигационный контекст
  • Усиливать элементарную иерархию
  • Объяснять изменения между компонентами, отображаемыми на экране

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

Обратная связь по сигналу касания

Обеспечение обратной связи, когда пользователь касается экрана, помогает общаться в визуальной форме, чтобы осуществилось взаимодействие. Эти анимации не должны отвлекать пользователя, но должны его веселить, вносить ясность и поощрять дальнейшее изучение.

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

Android:attr/selectableItemBackground - Показывает пульсирующий эффект в пределах границ образа.

Пульсация начинается в точке касания, заполняя фон представленного образа

?android:attr/selectableItemBackgroundBorderless - Показывает пульсирующий эффект, выходящий за границы представленного образа.

Круговой пульсирующий эффект начинается в точке касания, заполняя радиус, выходящий за пределы представленного образа

View Property Animator

View Property Animator был введен на уровне API 12, что позволяет просто и эффективно выполнять анимационные операции (параллельно) на некоторых свойствах образа, используя один экземпляр Animator.

Здесь я делаю анимации на все свойства, представленные ниже.

alpha ()- Устанавливает значение альфа, чтобы сделать анимацию
ScaleX ()& ScaleY () - Уравновешивает обзор на своей Х и / или Y оси
translationZ ()- Переводит обзор на своей оси Z
setDuration ()- Устанавливает продолжительность анимации
setStartDelay ()- Устанавливает задержку анимации
setInterpolator ()- Устанавливает интерполяции анимации
setListener ()- Устанавливает слушателя, чтобы знать, когда анимация начинается, заканчивается, повторяется или отменена.
Примечание: Когда слушатель был установлен в данном изображении, и если вы осуществляете иные анимации на этой же точке и не хотите использовать эту функцию обратного вызова, то вы должны установить слушателя на NULL.

Это также просто и аккуратно реализовать программно:

mButton.animate():

TranslationZ(10f)

SetInterpolator(new FastOutSlowInInterpolator())

SetStartDelay(200)

SetListener(new Animator.AnimatorListener() {

public void onAnimationStart(Animator animation) { }

public void onAnimationEnd(Animator animation) { }

public void onAnimationCancel(Animator animation) { }

public void onAnimationRepeat(Animator animation) { }

Примечание: Мы на самом деле не должны вызывать start () на нашем анимационном застройщике, так как анимация запускается автоматически, как только мы одновременно прекращаем заявлять об анимации. Если это так, то анимация не начнется до следующего обновления из очереди событий инструментария интерфейса.

Примечание: Для обеспечения обратной совместимости, вы можете использовать класс ViewCompat для реализации ViewPropertyAnimator из Android API версии 4 и выше.

Как и View Property Animator, Object Animator позволяет нам выполнять анимацию на различных свойствах целевого изображения (как в коде так и ресурсах XML-файлов). Тем не менее, есть несколько отличий:

Object Animator только позволяет анимации существовать в единственном состоянии на один образец, например, за шкалой X следует шкала Y
Тем не менее, она позволяет анимации существовать в обычном состоянии, например, цвет переднего плана образа.
Используя пользовательские Свойства или Состояния, чтобы сделать анимацию масштабирования изображения и изменения цвета переднего плана, мы можем получить следующее:

Используя Custom Property, мы можем создать один экземпляр Object Animator вызвав ObjectAnimator.оfInt (), где мы заявляем:

Вид- Вид, чтобы применить анимацию
Свойство- Свойство для анимации
Первоначальный цвет- Цвет, с которого анимационный вид начинается
Целевой цвет- Цвет, с которым данный образ должен ожить
private void animateForegroundColor(@ColorInt final int targetColor) {
ObjectAnimator animator =
ObjectAnimator.ofInt(YOUR_VIEW, FOREGROUND_COLOR, Color.TRANSPARENT, targetColor);
animator.setEvaluator(new ArgbEvaluator());
animator.setStartDelay(DELAY_COLOR_CHANGE);
animator.start();
}
Затем мы устанавливаем оценщика (мы используем ArgbEvaluator, так как мы делаем анимацию между значениями цвета), устанавливаем задержку и начинаем () анимацию.

Мы создаем экземпляр ObjectAnimator используя оfFloat (), потому что мы не делаем анимацию целочисленных значений при работе с размерами образов
Вместо custom property, мы используем свойства изображения - как View.SCALE_X так и View. SCALE_Y
private void resizeView() {
final float widthHeightRatio = (float) getHeight() / (float) getWidth();
resizeViewProperty(View.SCALE_X, .5f, 200);
resizeViewProperty(View.SCALE_Y, .5f / widthHeightRatio, 250);
}
private void resizeViewProperty(Property property,
float targetScale,
int durationOffset) {
ObjectAnimator animator = ObjectAnimator.ofFloat(this, property, 1f, targetScale);
animator.setInterpolator(new LinearOutSlowInInterpolator());
animator.setStartDelay(DELAY_COLOR_CHANGE + durationOffset);
animator.start();
}
Наконец, нам надо «оживить» наш образ с измененным размером вне экрана. В этом случае, мы используем AdapterViewFlipper, чтобы вместить наши образы, которые мы анимируем вне экрана. Использование этого означает, что мы можем вызвать showNext () на образце ViewFlipper и он займется анимационными образами вне экрана используя анимацию, которую мы определили. Затем, следующий образ автоматически будет оживать на экране, также используя входящую анимацию, которую мы также определили.

Интерполяторы

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

  • Без Интерполятора - Вид оживает без вариаций в скорости изменения
  • Быстро - Вне линии - Внутри

Образ начинает анимацию и заканчивается линейным движением

  • Быстро - Медленно - Внутри

Образ начинает анимацию быстро и замедляется к концу

  • Линейно - Медленно - Внутри

Образ начинается с линейных движений и замедляется к концу

  • Ускорение - Замедление

Образ начинает появляется с ускорения в начале анимации, и постепенно замедляется, когда подходит к концу

  • Ускорение- Образ постепенно ускоряется, пока анимация не закончится
  • Торможение- Образ постепенно замедляется до тех пор, пока анимация не закончится
  • Опережение- Образ начинается с небольшого поворота указанной анимации, прежде чем она станет двигаться стандартным образом
  • Предвидеть - Проскакивать- Также как и в Опережении, но движение «тянущее назад», которое происходит во время анимации немного более преувеличено
  • Прыгающий Интерполятор- Образ оживает в эффекте «отскок» до того, как дойдет до финиша
  • Линейный Интерполятор- Образ оживает от начала до конца линейным и плавным движением
  • Проскакивающий Интерполятор- Образ «оживляет» преувеличение данного значения, втягиваясь обратно до требуемого значения

Круговое оживление

Анимация CircularReveal или Круговое оживление использует отсеченный круг, чтобы либо раскрыть, либо скрыть группу элементов пользовательского интерфейса. Кроме помощи в обеспечении визуальной преемственности, это также приятное взаимодействие, для того чтобы помочь усилить его с пользователем.

Как было показано выше, мы начинаем с использования View Property Animator, чтобы скрыть кнопку Floating Action перед началом оживления анимации на наших глазах. Настройка нашего круга оживления требует определить всего лишь несколько атрибутов:

  • startView- вид, с которого CircularReveal начнется с (т.е. сжатый вид)
  • centerX- Центр координирует для X-оси в нажатом виде
  • centerY- Центр координирует для Y-оси в нажатом виде
  • targetView- Вид, который надо создать
  • finalRadius- Радиус отсечения круга, равный гипотенузе наших значений - centerX и centerY

int centerX = (startView.getLeft() + startView.getRight()) / 2;
int centerY = (startView.getTop() + startView.getBottom()) / 2;
float finalRadius = (float) Math.hypot((double) centerX, (double) centerY);
Animator mCircularReveal = ViewAnimationUtils.createCircularReveal(
targetView, centerX, centerY, 0, finalRadius);

Оконные Переходы

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

  • вход- Определяет, как образы транзакции выходят на сцену
  • выход- Определяет, как образы транзакции уходят со сцены
  • войти снова- Определяет, как транзакция входит снова после ранее сделанного выхода
  • общие элементы- Определяет, как обмениваются образы переходов между транзакциями

Как и в 21 уровне API, появилось, и было введено несколько новых переходов:

Взрывной переход позволяет образам выходить со всех сторон экрана, создавая взрывной эффект в нажатом виде.

Взрывной эффект работает очень хорошо на макетах, основанных на сетке.

Этот эффект прост в реализации - начнем с того, что вам нужно создать следующий переход в RES res/transition каталоге.

android:duration=“300“/>

Все, что мы сделали здесь:

  • Объявили взрывной переход
  • Установили продолжительность в 300 миллисекунд

Либо программно:

Transition explode = TransitionInflater.from(this).inflateTransition(R.transition.explode);
getWindow().setEnterTransition(explode);

Слайд

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

Слайд переход позволяет последовательно скользить в дочерние образы

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

android:interpolator=“@android:interpolator/decelerate_cubic“
android:slideEdge=“end“/>

Здесь мы:

  • Объявляем слайд переход
  • Устанавливаем переходный slideEdge,чтобы закончить на этом (справа), таким образом, слайды идут справа - нижний слайд должен быть установлен внизу

Угасание

Угасающий переход позволяет перейти к транзакции внутри или снаружи, используя угасающий эффект.

Угасающий переход прост, хотя переход с угасанием приятен глазу.

Создать его даже проще, чем предыдущие переходы:

android:duration=“300“/>

Здесь мы:

  • Объявляем угасающийпереход
  • Устанавливаем продолжительность до 300 миллисекунд

Оптимизация переходов

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

Разрешение переходам содержимого окон - вы должны включить следующий атрибут в темах, которые унаследованы из материальной темы:

true

Включение / выключение совпадающих переходов - При переходе, может быть задержка, когда одно действие ждет, пока другое завершит свой переход, прежде чем он сможет начать свой собственный. В зависимости от случая использования, переходы, как правило, выглядят более гибкими и естественными, если вы включите эти атрибуты:

true

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

android:duration=“200“>




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

Продолжительность перехода - Вы не хотите, чтобы пользователь ждал слишком долго, но вы также не хотите создавать компоненты, появляющиеся на скорости света. Это зависит от перехода, который вы используете, так что лучше поэкспериментировать, но я обнаружил, что длительность 200-500 мс работает в большинстве случаев.

Общие элементы переходов

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

Здесь, образ из нашего первого действия масштабируется и переводится в образ заголовка в нашем втором действии

В наших макетах, мы должны связать любые общие образы с использованием атрибута transitionName - он устанавливает переходные отношения между образами. Ниже приведены общие образы из выше данной анимации:

Это общие образы, что означает - они будут оживать друг с другом во время перехода действий

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




android:transitionName=“@string/transition_view“/>



android:id=“@+id/view_shared_transition“
android:transitionName=“@string/transition_view“/>
android:id=“@+id/view_separator“/>
android:id=“@+id/text_detail“/>
android:id=“@+id/text_close“/>

После того, как это будет сделано, мы создаем Pair объект в действии 1), содержащий наш переходный образ, и его transitionName. Затем мы передаем его в примерные варианты транзакций, например (ActivityOptionsCompat), так чтобы оба действия были в курсе общих компонентов. Оттуда мы начнем свою транзакцию, через примерный вариант:

Pair participants = new Pair<>(mSquareView, ViewCompat.getTransitionName(mSquareView));
ActivityOptionsCompat transitionActivityOptions =
ActivityOptionsCompat.makeSceneTransitionAnimation(
SharedTransitionsActivity.this, participants);
ActivityCompat.startActivity(SharedTransitionsActivity.this,
intent, transitionActivityOptions.toBundle());

Разделение этих образов, в то время как происходит переход, действительно помогает завершить переход

Вот переход между этими двумя образами, а что касается образов во втором действии, который скользит снизу?

(Те что слева)

Я рад, что вы спросили! Этого также просто достичь, как показано ниже:

Slide slide = new Slide(Gravity.BOTTOM);
slide.addTarget(R.id.view_separator);
slide.addTarget(R.id.text_detail);
slide.addTarget(R.id.text_close);
getWindow().setEnterTransition(slide);
Как вы видите, мы создаем новый образец Слайд перехода, добавив целевые представления для перехода и установив слайд в качестве перехода входа транзакции.

Пользовательские Переходы

У нас также есть возможность создавать свои собственные переходы, используя любую анимацию из API, которую мы рассмотрели до сих пор. Например, мы можем взять Shared Element переходы на один шаг дальше, чтобы превратиться в переходный образ - это может пригодиться, когда мы хотим отобразить диалоги (или аналогичные всплывающие образы), как показано ниже:

Это движение помогает направлять внимание пользователя между составными состояниями

Давайте коротко остановимся на том, что происходит здесь:

  • Мы начинаем с создания SharedTransition, проходя в нажатом состоянии вместе с именем перехода, чтобы ссылаться на общий компонент
  • Далее мы создаем экземпляр ArcMotion, это позволяет нам создать изогнутый эффект движения при переходе между двумя образами
  • Затем мы расширяем ChangeBounds, чтобы создать пользовательский переход и трансформировать две формы (у нас есть отдельный класс для кнопки и FAB). Здесь мы переопределяем различные методы из класса, так что мы можем оживить требуемые свойства. Мы воспользуемся ViewPropertyAnimator, чтобы оживить прозрачность диалоговых образов, ObjectAnimator, чтобы оживить образы между двумя видами цвета и образец AnimatorSet, чтобы мы смогли анимировать оба этих эффекта вместе.

Анимированный вектор вводимого коэффициента

По API версии 21 (Lollipop), AnimatedVectorDrawable может быть использован для анимации VectorDrawable свойств для получения анимации drawable.

Теперь легко сделать несколько различных видов анимации на вводимом коэффициенте

Но как мы это делаем? Ну, давайте взглянем на это:

Он состоит из нескольких различных файлов, и мы начинаем с создания наших двух отдельных векторных файлов каждый из которых имеет несколько свойств:

  • Высота и ширина- Фактический размер векторного изображения
  • Viewport высоты и ширины- Объявляет размер виртуального холста, на котором нарисованы векторные дорожки
  • Название группы- Объявите группу, к которой принадлежит дорожка
  • Сводные X & Y- Объявите стержень, используемый для групповой шкалы и вращения
  • Траектория цвета заливки- Цвет заливки траектории вектора
  • Траектория данных- Объявите данные векторного пути, используемого для рисования вектора

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

android:height=“56dp“
android:width=“56dp“

android:viewportWidth=“24.0“>

android:pivotX=“12“
android:pivotY=“12“>

android:pathData=“@string/path_add“/>

Вектор генерируется из нашего ic_add.xml файла (ниже)

android:height=“56dp“
android:width=“56dp“
android:viewportHeight=“24.0“
android:viewportWidth=“24.0“>
android:name=“@string/groupAddRemove“
android:pivotX=“12“
android:pivotY=“12“>
Android:fillColor=“@color/stroke_color“
android:pathData=“@string/path_remove“/>

Вектор генерируется из нашего ic_remove.xml файла (ниже)

Далее мы заявляем файлы Animated Vector Drawable , которые устанавливают как Вектор Drawable так и анимации, используемые для каждого «растяжимого» состояния (Добавить или Удалить). Глядя на добавленный или удаленный анимационный вектор, мы объявляем цель:

Анимация из одного состояния в другое
Анимация вращения вводимого коэффициента

android:drawable=“@drawable/ic_add“>
android:name=“@string/add“
android:animation=“@animator/add_to_remove“ />
android:name=“@string/groupAddRemove“
android:animation=“@animator/rotate_add_to_remove“ />

Затем мы должны создать каждый из файлов, упомянутый в этих целях.

Изменение состояния вводимого коэффициента

В add_to_remove.xml мы используем ObjectAnimator, чтобы трансформироваться между формами, используя следующие свойства:

  • PropertyName- Свойство анимации
  • valueFrom- Начальное значение для векторного пути
  • valueTo- Целевое значение для векторного пути
  • Продолжительность- Продолжительность анимации
  • интерполятор- Интерполятор, используемый для анимации
  • ValueType- Тип значения, который мы оживляем

xmlns:android=“//schemas.android.com/apk/res/android“
android:propertyName=“pathData“
android:valueFrom=“@string/path_add“
android:valueTo=“@string/path_remove“

android:interpolator=“@android:interpolator/fast_out_slow_in“
android:valueType=“pathType“ />

Поворачиваем форму

Мы применяем аналогичный подход, чтобы повернуть форму, используя свойство вращения и величины:

xmlns:android=“//schemas.android.com/apk/res/android“
android:propertyName=“rotation“
android:valueFrom=“-180“
android:valueTo=“0“
android:duration=“@integer/duration“
android:interpolator=“@android:interpolator/fast_out_slow_in“ />
Анимация обратного (от Убрать до Добавить) работает также, только со значениями анимации вспять.

Наш завершенный Анимационный Вектор вводимого коэффициента выглядит классно, не так ли!

И в заключении…

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

Если вам понравилась эта статья, то, пожалуйста, нажмите «Рекомендую»!

Я хотел бы услышать ваши мысли по этому поводу и где вы используете эти анимации - пожалуйста, оставьте отзыв или напишите мне в твиттере!

Начиная с Android 4.4 в арсенале разработчиков появился дополнительный инструмент для создания анимаций - Transitions Framework. Изначально он предназначался для создания анимаций изменения состояния приложения путём манипулирования несколькими View. С выходом Android 5.0 набор доступных для использования анимаций был расширен, чтобы соответствовать представленной тогда же концепции Material Design.

Transitions Framework позволяет быстро и безболезненно создавать различные анимации. Поэтому в процессе работы над iFunny было невозможно пройти мимо этого инструментария. Вниманию читателей предлагается частный случай использования Transitions API - создание анимации перехода между Activity с эффектом «бесшовности».

С визуальной точки зрения представленные в Transitions Framework анимации переходов между Activity можно условно разделить на два типа: обычные анимации и анимации с общим элементом. Концепт анимации с общим элементом продемонстрирован на честно украденном с сайта developer.android.com рис. 1. На нём в роли общих элементов выступают аватар и имя контакта.

Рис. 1. Анимация перехода между Activity с общими элементами

Но никто не любит длинные вступления, поэтому сразу перейдём к рассказу о том, как создавались анимации данного типа в приложении iFunny. В качестве первого примера рассмотрим анимацию, показанную на рис. 2. Для её использования нам потребуется Android версии 5.0 и выше.


Рис. 2. Анимация перехода между Activity на экране аутентификации пользователя

С точки зрения пользователя, здесь нет ничего необычного: один экран, простенькая анимация. Но, как вы уже могли догадаться, «под капотом» - переход между двумя экранами с одним общим элементом.

Первым шагом к созданию подобного перехода является, как ни странно, выбор этого самого элемента и определение его местоположения в вёрстке обеих Activity. После этого в описание каждого View, отображающего выбранный элемент, нужно добавить атрибут android:transitionName, а также назначить им android:id, если таковой отсутствует.

В нашем случае это обычные ImageView следующего вида:

Здесь стоит отметить два важных момента. Во-первых, в обоих ImageView необходимо установить одинаковые transitionName, что логично. Во-вторых, коль скоро мы используем ImageView, то и содержимое у них должно быть одним и тем же, поскольку использование двух отличающихся ресурсов может привести к неожиданным последствиям (как минимум к морганию анимируемого View в начале и конце анимации).

На втором шаге необходимо добавить опции для запускаемой (второй) Activity, сообщающие о том, что при её запуске должна быть запущена анимация.

Примечание. Под «второй» подразумевается запускаемая Activity, переход к которой должен быть осуществлён, а под «первой» - запускающая Activity.

Делается это следующим образом:

Bundle bundle = null; if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { View v = activity.findViewById(R.id.auth_logo); if (v != null) { ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity, v, activity.getString(R.string.email_auth_transition)); bundle = options.toBundle(); } } Intent intent = new Intent(activity, SecondActivity.class); if (bundle == null) { activity.startActivity(intent); } else { activity.startActivity(intent, bundle); }

В приведённом листинге:

  • R.id.auth_logo - ImageView из первой Activity, используемый в анимации;
  • activity - первая Activity;
  • R.string.email_auth_transition - метка, ранее оставленная в вёрстке обоих ImageView;
  • SecondActivity.class - вторая Activity.

И сейчас внимательный читатель может испытать недоумение: во вступлении речь шла об использовании API level 19, в примере фигурировал API level 21, а в листинге выше стоит ограничение на API level 22. К сожалению, при написании кода выяснилось, что анимации перехода с общим элементом могут вести себя некорректно на телефонах c API level 21. Проявляется это в виде подтормаживаний анимации в целом и артефактов на анимируемом View в частности. Если вы уже знакомы с темой, знаете причины подобного поведения и/или способы решения описанной проблемы - расскажите нам об этом в комментариях.

На третьем шаге необходимо описать анимацию перехода, т.е. указать путь, проходимый анимируемым View, и трансформацию самого View. Для этого создадим отдельный файл projectName/src/main/res/transitions/email_auth_transition.xml со следующим содержимым:

Немного теории. Тег transitionSet предназначен для описания сразу нескольких трансформаций, применяемых к анимируемому View. Параметр transitionOrdering отвечает за порядок применения этих трансформаций. В нашем случае они применяются одновременно. Существует несколько типов готовых трансформаций, представленных в Transitions Framework. С полным списком можно ознакомиться на этой странице . Мы же остановимся на двух конкретных: changeBounds и changeImageTransform.

Первая предназначена для трансформации размера View. Вторая работает только с ImageView и в связке с первой позволяет изменять не только размер, но и форму ImageView. Использовав данные трансформации, получаем на выходе анимацию изменения размера изображения, представленную на рис. 2. Если не указывать тип движения анимируемого View, то он будет двигаться по кратчайшему пути. Более интересный способ передвижения рассмотрим во втором примере.

Последним шагом создания анимации является её объявление в темах обеих Activity. Для этого отредактируем описание тем следующим образом (или создадим новые в папке projectName/src/main/res/values-v22/theme.xml):

  • android:windowActivityTransitions разрешает выполнение анимации перехода;
  • android:windowSharedElementEnterTransition указывает на файл с описанием анимации перехода от первой Activity ко второй;
  • android:windowSharedElementExitTransition указывает на файл с описанием анимации перехода при возвращении из второй Activity в первую.

Следует отметить, что для версий ОС ниже 5.1 необходимо создать темы с идентичными стилями, чтобы избежать вполне ожидаемых последствий в виде падения приложения. Например, поместим их в файл projectName/src/main/res/values/theme.xml: