Добрый день! В этой статье мы будем рассматривать процесс создания кастомных анимаций переходов между 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 необходимы нам для анимации.
Для анимации нам необходимо предпринять следующие шаги:
- Создать Bitmap из байтового массива, переданного из первыой Activity
- Разделить Bitmap на 2 части и загрузить их в соответствующий ImageView
- При помощи 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.
Автор: ra1ph