...

четверг, 3 октября 2013 г.

[Из песочницы] Кастомизация переходных анимаций между 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.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий