Статьи

Добавление анимации на основе физики в приложения для Android

Анимации, которые кажутся плавными и реалистичными, делают пользовательские интерфейсы более привлекательными. Неудивительно, что Material Design уделяет им столько внимания!

Однако, если вы когда-либо пытались создавать такие анимации, вы знаете, что простые аниматоры и интерполяторы, предлагаемые в Android SDK, часто бывают недостаточно хорошими. Вот почему последние версии библиотеки поддержки Android поставляются с физическим модулем под названием Dynamic Animation .

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

Чтобы следовать, убедитесь, что у вас есть следующее:

  • Android Studio 3.0 Canary 4 или выше
  • Устройство или эмулятор под управлением Android 4.4 или выше

Чтобы использовать динамическую анимацию в своем проекте, вы должны добавить ее в качестве зависимости implementation в файл build.gradle модуля app :

1
implementation ‘com.android.support:support-dynamic-animation:26.0.0-beta2’

В этом уроке мы собираемся анимировать виджет ImageView . Конечно, ему придется отображать некоторые изображения, поэтому откройте Vector Assets Studio и добавьте следующие значки материалов в ваш проект:

  • нейтральные настроения
  • настроение очень доволен

Вот как они выглядят:

Две материальные иконки

Для достижения наилучших результатов я предлагаю вам установить размер значков 56 x 56 dp .

Когда вы бросаете объект в реальный мир, вы даете ему большой импульс. Поскольку импульс — это не что иное, как произведение массы и скорости, объект изначально будет иметь высокую скорость. Однако постепенно, благодаря трению, он замедляется, пока не перестанет полностью двигаться. Используя класс FlingAnimation Dynamic Animation, вы можете смоделировать это поведение в своем приложении.

Для демонстрации, давайте теперь создадим макет, содержащий перекидной виджет ImageView , отображающий значок ic_sentiment_neutral_black_56dp , и пользователи виджета Button могут нажимать, чтобы активировать анимацию перемещения. Если вы поместите их оба в виджет RelativeLayout , ваш XML-файл макета будет выглядеть следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version=»1.0″ encoding=»utf-8″?>
<RelativeLayout
    xmlns:android=»http://schemas.android.com/apk/res/android»
    xmlns:tools=»http://schemas.android.com/tools»
    android:layout_width=»match_parent»
    android:layout_height=»match_parent»
    android:layout_margin=»16dp»>
 
    <ImageView
        android:layout_width=»wrap_content»
        android:layout_height=»wrap_content»
        android:src=»@drawable/ic_sentiment_neutral_black_56dp»
        android:id=»@+id/emoji»
        android:layout_centerInParent=»true»
        />
 
    <Button
        android:layout_width=»wrap_content»
        android:layout_height=»wrap_content»
        android:text=»Fling»
        android:id=»@+id/flinger»
        android:layout_alignParentTop=»true»
        android:layout_alignParentLeft=»true»
        android:onClick=»flingIt» />
         
</RelativeLayout>

В приведенном выше коде вы можете видеть, что виджет Button имеет атрибут onClick . Нажав на красный значок лампочки, рядом с которым отображается Android Studio, вы можете сгенерировать связанный обработчик события щелчка внутри класса Activity :

1
2
3
public void flingIt(View view) {
    // more code here
}

Теперь вы можете создать новый экземпляр класса FlingAnimation используя его конструктор, который ожидает объект View и имя анимируемого свойства. Динамическая анимация поддерживает несколько анимируемых свойств, таких как масштаб, перемещение, вращение и альфа.

Следующий код показывает, как создать экземпляр FlingAnimation который может анимировать X-координату ImageView нашего макета:

1
2
3
4
5
6
// Get a reference to the view
ImageView emoji = (ImageView)findViewById(R.id.emoji);
 
// Pass it to the constructor
FlingAnimation flingAnimation
        = new FlingAnimation(emoji, DynamicAnimation.X);

По умолчанию экземпляр FlingAnimation настроен на использование 0 пикселей в секунду в качестве начальной скорости. Это означает, что анимация остановится, как только она начнется. Чтобы симулировать реалистичный бросок, вы всегда должны помнить, чтобы вызывать метод setStartVelocity() и передавать ему большое значение.

Кроме того, вы должны понимать, что без трения анимация не остановится. Следовательно, вы также должны вызвать метод setFriction() и передать ему небольшое число.

Следующий код настраивает экземпляр FlingAnimation таким образом, чтобы ImageView не вылетал за пределы экрана пользователя:

1
2
flingAnimation.setStartVelocity(500f);
flingAnimation.setFriction(0.5f);

На этом этапе вы можете просто вызвать метод start() чтобы запустить анимацию.

1
flingAnimation.start();

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

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

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

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

1
2
3
4
5
6
7
<Button
   android:layout_width=»wrap_content»
   android:layout_height=»wrap_content»
   android:text=»Bounce»
   android:layout_alignParentTop=»true»
   android:layout_alignParentRight=»true»
   android:onClick=»bounce» />

Чтобы создать анимацию на основе SpringAnimation , вы должны использовать класс SpringAnimation . Его конструктор также ожидает объект View и свойство анимации. Следующий код создает экземпляр SpringAnimation настроенный для анимации x-координаты ImageView :

1
2
3
4
5
6
// Get a reference to the view
final ImageView emoji = (ImageView)findViewById(R.id.emoji);
 
// Pass it to the constructor
SpringAnimation springAnimation
        = new SpringAnimation(emoji, DynamicAnimation.X);

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

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

1
2
3
4
SpringForce springForce = new SpringForce();
springForce.setFinalPosition(emoji.getX());
springForce.setDampingRatio(SpringForce.DAMPING_RATIO_HIGH_BOUNCY);
springForce.setStiffness(SpringForce.STIFFNESS_LOW);

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

Теперь вы можете связать пружину с экземпляром SpringAnimation с помощью setSpring() .

1
springAnimation.setSpring(springForce);

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

1
2
springAnimation.setStartVelocity(2000f);
springAnimation.start();

Если вы запустите приложение сейчас, вы должны увидеть что-то вроде этого:

Анимация, созданная с использованием библиотеки динамической анимации, всегда должна запускаться из потока пользовательского интерфейса. Вы также можете быть уверены, что он запустится, как только вы вызовете метод start() . Тем не менее, он работает асинхронно. Поэтому, если вы хотите получать уведомление о его завершении, вы должны прикрепить к OnAnimationEndListener объект OnAnimationEndListener используя метод addEndListener() .

Чтобы увидеть слушателя в действии, давайте изменим значок «Материал», который ImageView отображает каждый раз, когда запускается и заканчивается анимация на основе пружины, которую мы создали на предыдущем шаге. Я предлагаю вам использовать значок ic_sentiment_very_satisfied_black_56dp при запуске анимации и значок ic_sentiment_neutral_black_56dp, когда она заканчивается. Следующий код показывает вам, как:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
// Change icon before animation starts
emoji.setImageResource(
        R.drawable.ic_sentiment_very_satisfied_black_56dp);
 
// Start animation
springAnimation.start();
 
springAnimation.addEndListener(
    new DynamicAnimation.OnAnimationEndListener() {
        @Override
        public void onAnimationEnd(DynamicAnimation animation,
                                boolean canceled,
                                float value, float velocity) {
            // Change icon after animation ends
            emoji.setImageResource(
                    R.drawable.ic_sentiment_neutral_black_56dp);
        }
});

С помощью приведенного выше кода анимация будет выглядеть следующим образом:

SpringAnimation классов FlingAnimation и SpringAnimation могут принимать только одно анимируемое свойство. Если вы хотите анимировать несколько свойств одновременно, вы можете создать несколько экземпляров классов, которые могут стать громоздкими, или создать новое пользовательское свойство, которое инкапсулирует все нужные вам свойства.

Для создания настраиваемого анимируемого свойства необходимо создать подкласс класса FloatPropertyCompat , который имеет два абстрактных метода: setValue() и getValue() . Как вы уже догадались, вы можете обновить значения всех ваших требуемых анимируемых свойств внутри setValue() . Однако внутри getValue() вы должны возвращать текущее значение только одного свойства. Из-за этого ограничения обычно вам нужно убедиться, что значения инкапсулированных свойств не являются полностью независимыми друг от друга.

Например, в следующем коде показано, как создать настраиваемое свойство с именем scale , которое может одинаково анимировать свойства виджета SCALE_X и SCALE_Y :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
FloatPropertyCompat<View> scale =
       new FloatPropertyCompat<View>(«scale») {
           @Override
           public float getValue(View view) {
               // return the value of any one property
               return view.getScaleX();
           }
 
           @Override
           public void setValue(View view, float value) {
               // Apply the same value to two properties
               view.setScaleX(value);
               view.setScaleY(value);
           }
       };

Теперь, когда пользовательское свойство готово, вы можете использовать его как любое другое анимируемое свойство. Следующий код показывает вам, как создать объект SpringAnimation с ним:

1
2
SpringAnimation stretchAnimation =
           new SpringAnimation(emoji, scale);

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

1
2
stretchAnimation.setMinimumVisibleChange(
               DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);

Вот как выглядит анимация пользовательских свойств:

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

Чтобы узнать больше о динамической анимации, обратитесь к официальной документации . А пока ознакомьтесь с другими нашими недавними публикациями о разработке приложений для Android!

  • Android SDK
    Создайте интеллектуальное приложение с Google Cloud Speech и API на естественном языке
  • Android
    Обеспечение высокого качества кода Android с помощью инструментов статического анализа
    Чике Мгбемена
  • Android SDK
    Бессерверные приложения с облачными функциями Firebase
    Чике Мгбемена
  • Android
    Как решить 13 самых распространенных сообщений об ошибках Android
    Джессика Торнсби