Кастомизация переходных анимаций между Activity в ОС Android

от автора

Добрый день! В этой статье мы будем рассматривать процесс создания кастомных анимаций переходов между Activity в Android при помощи ObjectAnimator и AnimatorSet. Всем, кому это интересно — добро пожаловать под кат.

PS: статья написана в основном для начинающих разработчиков.

Весь исходный код доступен на GitHub

Мы рассмотрим 2 вида переходных анимаций:

  • анимируем старую Activity на фоне новой
  • анимируем новую Activity на фоне старой

Для демонстрации были выбраны 2 типа анимации: одновременная анимация нескольких объектов (створки двери) и последовательная анимация нескольких объектов (сворачивание листа бумаги)

Анимация старой Activity


Итак, создадим 3 Activity: FirstActivity, SecondActivity и ThirdActivity.

Код FirstActivity:

first.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"               android:id="@+id/click_layout"               android:orientation="vertical"               android:layout_width="fill_parent"               android:layout_height="fill_parent"               android:background="#00ff00"         >     <TextView             android:layout_gravity="center"             android:gravity="center"             android:layout_width="wrap_content"             android:layout_height="fill_parent"             android:textColor="#FFFFFF"             android:textSize="40sp"             android:text="First"             /> </LinearLayout> 

FirstActivity.java:

public class FirstActivity extends Activity {          @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.first);          LinearLayout click = (LinearLayout) findViewById(R.id.click_layout);         click.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 Bitmap bmp = getBitmap();                  ByteArrayOutputStream stream = new ByteArrayOutputStream();                 bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);                 byte[] byteArray = stream.toByteArray();                 Intent intent = new Intent(FirstActivity.this, SecondActivity.class);                 intent.putExtra("picture", byteArray);                 startActivity(intent);                 overridePendingTransition(0,0);             }         });     }      private Bitmap getBitmap(){         View root = getWindow().getDecorView().findViewById(android.R.id.content);         root.setDrawingCacheEnabled(true);         return root.getDrawingCache();     } } 

Функция getBitmap возвращает Bitmap текущего окна. При клике по основному элементу Activity создаем новый Intent, и в качестве Extra задаем наш Bitmap, преобразованный в байтовый массив. После чего запускаем SecondActivity.

Код SecondActivity:

second.xml:

<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"               android:orientation="vertical"               android:layout_width="fill_parent"               android:layout_height="fill_parent"               android:id="@+id/click_layout"               android:background="#ff0000"         >     <TextView             android:layout_gravity="center"             android:gravity="center"             android:layout_width="wrap_content"             android:layout_height="fill_parent"             android:textColor="#FFFFFF"             android:textSize="40sp"             android:text="Second"             />     <LinearLayout android:orientation="horizontal"                   android:weightSum="100"             android:layout_width="fill_parent"                android:layout_height="fill_parent">         <ImageView android:layout_weight="50"                    android:layout_width="0dip"                    android:layout_height="fill_parent"                 android:id="@+id/left_image"/>         <ImageView android:layout_weight="50"                    android:layout_width="0dip"                    android:layout_height="fill_parent"                    android:id="@+id/right_image"/>             </LinearLayout> </FrameLayout> 

В качестве главного элемента нашей Activity мы задаем FrameLayout, который содержит в себе 2 LinearLayout. В первом размещается весь необходимый контент(в нашем случае это TextView). Во втором находятся 2 ImageView, которые делят экран пополам. Онb необходимы нам для анимации.

Для анимации нам необходимо предпринять следующие шаги:

  1. Создать Bitmap из байтового массива, переданного из первыой Activity
  2. Разделить Bitmap на 2 части и загрузить их в соответствующий ImageView
  3. При помощи AnimatorSet одновременно воспроизвести анимацию поворота обоих ImageView
public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.second);          Bundle extras = getIntent().getExtras();         byte[] byteArray = extras.getByteArray("picture");          Bitmap bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);         left = (ImageView) findViewById(R.id.left_image);         right = (ImageView) findViewById(R.id.right_image);         int centerWidth = bmp.getWidth()/2;         Bitmap bmpLeft,bmpRight;         bmpLeft = Bitmap.createBitmap(bmp,0,0,centerWidth,bmp.getHeight());         bmpRight = Bitmap.createBitmap(bmp,centerWidth,0,bmp.getWidth() - centerWidth,bmp.getHeight());          left.setImageBitmap(bmpLeft);         right.setImageBitmap(bmpRight);          ViewTreeObserver observer = left.getViewTreeObserver();         observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {             @Override             public boolean onPreDraw() {                 left.getViewTreeObserver().removeOnPreDrawListener(this);                 startEnterAnimation();                 return true;  //To change body of implemented methods use File | Settings | File Templates.             }         });     } 

В функции onCreate мы получаем Bitmap из Extras, делим его на 2 Bitmap при помощи Bitmap.createBitmap() и устанавливаем полученные изображения в ImageView. После этого регистрируем Observer, который будет следить за onDraw событием ImageView и вызываться только один раз перед первой отрисовкой объекта. В нем мы запускаем анимацию открытия Activity.

private void startEnterAnimation() {         left.setPivotY(left.getHeight()/2);         left.setPivotX(0);         right.setPivotY(left.getHeight()/2);         right.setPivotX(right.getWidth());         Animator leftAnim = ObjectAnimator.ofFloat(left, "rotationY", 0, 90);         Animator rightAnim = ObjectAnimator.ofFloat(right, "rotationY", 0, -90);         AnimatorSet set = new AnimatorSet();         set.setDuration(500);         set.playTogether(leftAnim, rightAnim);         set.start();     } 

Непосредственно сама анимация описана в функции startEnterAnimation(). При помощи функций setPivotX() и setPivotY() мы устанавливаем точки, вокруг которых будут вращаться наши изображения. Далее мы задаем сами анимации вращения при помощи объекта ObjectAnimator.
ObjectAnimator — компонент системы, появившийся с Android 3.0. Он помогает анимировать какое-либо свойство любого объекта. В данном случае, мы анимируем свойство rotationX типа float объекта left.
Далее, мы создаем набор анимаций AnimatorSet, задаем длительность анимации в 500 мс и говорим ему, что будем одновременно проигрывать 2 анимации.
Анимация закрытия Activity задается аналогично. Для ее запуска мы переопределяем функцию Activity onBackPressed(). В ней мы запускаем анимацию закрытия, не забыв добавить listener с функцией finish().

Анимация новой Activity


Для того, чтобы анимировать новую Activity на фоне старой, нам не нужно ничего передавать в новую Activity. Нужно просто сделать фон ACtivity прозрачным. Для этого мы изменяем в файле AndroidManifest тему ThirdActivity на Transparent, и добавляем эту тему в styles.xml

<style name="Transparent">         <item name="android:windowNoTitle">true</item>         <item name="android:windowIsTranslucent">true</item>         <item name="android:windowBackground">@android:color/transparent</item>     </style> 

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

  • при помощи наблюдателя OnPreDrawListener и функции getBitmap мы получаем Bitmap нашего Layout, после чего делаем его невидимым и запускаем анимацию открытия Activity
  • делим полученный Bitmap на 4 части, устанавливаем каждую часть в соответствующий Bitmap, после чего делаем невидимыми все изображения кроме начального
  • анимация открытия будет похожа на предыдущую, за исключением того, что после окончания анимации каждого куска изображения нам необходимо сделать видимым следующий кусок изображения
  • ну и в конце всей анимации мы делаем видимым Layout с контентов этой Activity, а изображения скрываем

Вся анимация запускается при помощи все того-же AnimatorSet, только задаются они при помощи функции playSequentially. Это означает, что все анимации будут запускаться последовательно друг за другом.

Анимация закрытия Activity делается аналогично, только в обратном порядке.

Заключение

Мы рассмотрели 2 способа создания переходной анимации между Activity. Весь код в статье адаптирован под версию системы выше 3.0, однако его легко можно адаптировать и под более ранние версии, начиная с 1.6, при помощи библиотеки NineOldAndroids от всем известного Jake Wharton’а. Единственное, о чем хотелось бы заострить внимание — это на установке относительных точек для поворота и увеличения. В этой библиотеке они устанавливаются при помощи объекта AnimatorProxy.

На этом все, если понравится эта статья — продолжу публикации на тему создания анимаций в ОС Android.

ссылка на оригинал статьи http://habrahabr.ru/post/196224/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *