Освой программирование играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Класс TranslateAnimation наследуется от класса Animation и отвечает за анимацию перемещения объекта.
Один из конструкторов класса TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) создаёт анимацию путём указания начальных и конечных координат.
Также есть схожий конструктор TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, int fromYType, float fromYValue, int toYType, float toYValue), где дополнительно можно указать тип координат. Тип может принимать одно из следующих значений:
Простейшая анимация сдвига ImageView слева направо.
ImageView image = findViewById(R.id.image);
TranslateAnimation animation = new TranslateAnimation(-400, 50, 50, 50);
animation.setDuration(1000);
animation.setFillAfter(true);
image.startAnimation(animation);
Пример для кнопки на Kotlin
button.startAnimation(TranslateAnimation(0f, 300f,
0f, 0f)
.apply {
duration = 1000
})
Рассмотрим пример перемещения объекта ImageView сверху вниз (падать). В папке res/anim создадим файл falling.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator" >
<translate
android:duration="2000"
android:fromYDelta="-100%"
android:toYDelta="700" />
</set>
Разместим на форме кнопку и ImageView и добавим код:
package ru.alexanderklimov.testapp;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
public class TestAppActivity extends Activity {
private ImageView mImageView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mImageView = findViewById(R.id.imageView1);
Button startFallingButton = (Button) findViewById(R.id.starttranslate);
final Animation fallingAnimation = AnimationUtils.loadAnimation(this,
R.anim.falling);
startFallingButton.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
mImageView.startAnimation(fallingAnimation);
}});
}
}
Запускайте проект и любуйтесь эффектом.
Усложним проект и заставим падать несколько объектов по очереди. Создадим два новых файла анимации:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromYDelta="-50%"
android:interpolator="@android:anim/accelerate_interpolator"
android:toYDelta="0.0" />
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromYDelta="0.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toYDelta="50%" />
Подготовим разметку из трёх невидимых ImageView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<ImageView
android:id="@+id/image1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="@drawable/ic_launcher"
android:visibility="invisible" />
<ImageView
android:id="@+id/image2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="@drawable/ic_launcher"
android:visibility="invisible" />
<ImageView
android:id="@+id/image3"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="@drawable/ic_launcher"
android:visibility="invisible" />
</LinearLayout>
Напишем код:
package ru.alexanderklimov.falling;
import ...
public class MainActivity extends Activity {
private ImageView mImageView1, mImageView2, mImageView3;
private Animation fallingAnimation, upAnimation;
private ImageView curFallingImageView;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_main);
mImageView1 = (ImageView) findViewById(R.id.image1);
mImageView2 = (ImageView) findViewById(R.id.image2);
mImageView3 = (ImageView) findViewById(R.id.image3);
fallingAnimation = AnimationUtils.loadAnimation(this,
R.anim.falling_down);
upAnimation = AnimationUtils.loadAnimation(this, R.anim.falling_up);
fallingAnimation.setAnimationListener(animationFallingListener);
upAnimation.setAnimationListener(animationUpListener);
curFallingImageView = mImageView1;
mImageView1.startAnimation(fallingAnimation);
mImageView1.setVisibility(View.VISIBLE);
}
@Override
protected void onPause() {
super.onPause();
mImageView1.clearAnimation();
mImageView2.clearAnimation();
mImageView3.clearAnimation();
}
AnimationListener animationFallingListener = new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
// TODO Auto-generated method stub
if (curFallingImageView == mImageView1) {
mImageView1.startAnimation(upAnimation);
} else if (curFallingImageView == mImageView2) {
mImageView2.startAnimation(upAnimation);
} else if (curFallingImageView == mImageView3) {
mImageView3.startAnimation(upAnimation);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationStart(Animation animation) {
}
};
AnimationListener animationUpListener = new AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
if (curFallingImageView == mImageView1) {
curFallingImageView = mImageView2;
mImageView2.startAnimation(fallingAnimation);
mImageView1.setVisibility(View.INVISIBLE);
mImageView2.setVisibility(View.VISIBLE);
mImageView3.setVisibility(View.INVISIBLE);
} else if (curFallingImageView == mImageView2) {
curFallingImageView = mImageView3;
mImageView3.startAnimation(fallingAnimation);
mImageView1.setVisibility(View.INVISIBLE);
mImageView2.setVisibility(View.INVISIBLE);
mImageView3.setVisibility(View.VISIBLE);
} else if (curFallingImageView == mImageView3) {
curFallingImageView = mImageView1;
mImageView1.startAnimation(fallingAnimation);
mImageView1.setVisibility(View.VISIBLE);
mImageView2.setVisibility(View.INVISIBLE);
mImageView3.setVisibility(View.INVISIBLE);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationStart(Animation animation) {
}
};
}
Запустив проект, вы увидите, как сверху будут сыпаться картинки.
По такому же принципу можно организовать движение по диагонали, изменяя одновременно значения X и Y. Создадим ещё один файл res/anim/slide_diagonal.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="100%p"/>
<translate
android:fromXDelta="0"
android:toXDelta="100%p"/>
</set>
Хотя эта запись избыточна, можно и так:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="100%p"
android:toYDelta="100%p" />
</set>
Разместите в углу экрана ImageView внутри контейнера LinearLayout, чтобы у него была возможность двигаться из угла в угол. Напишем код.
package ru.alexanderklimov.test;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
public class MainActivity extends Activity {
private ImageView mImageView;
private Animation mDiagonalAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = findViewById(R.id.imageView);
mDiagonalAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_diagonal);
mDiagonalAnimation.setDuration(3000);
mDiagonalAnimation.setAnimationListener(animationListener);
mImageView.startAnimation(mDiagonalAnimation);
}
Animation.AnimationListener animationListener = new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
mImageView.startAnimation(mDiagonalAnimation);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
};
}
Рассмотрим пример анимации, которая создаёт эффект дрожания. Подобный эффект можно применять в играх и даже в серьёзных приложениях для привлечения внимания.
Суть анимации состоит в циклическом повторении операции перемещения объекта за короткий промежуток времени, что создаёт иллюзию дрожания. Создадим папку res/anim, в которой надо создать два файла: shake.xml и cycle_7.xml.
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0"
android:interpolator="@anim/cycle_7"
android:toXDelta="10" />
<?xml version="1.0" encoding="utf-8"?>
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:cycles="7" />
В этом примере мы сдвигаем объект на 10 пикселей и повторяем подобное смещение 7 раз. Этого вполне достаточно для примера. Вы можете придумать свою анимацию.
Разместим на поверхности активности кнопку и заставим её дрожать при нажатии.
package ru.alexanderklimov.shake;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
public class ShakeDemoActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Button shakeButton = findViewById(R.id.button1);
final Animation shakeAnimation = AnimationUtils.loadAnimation(this, R.anim.shake);
shakeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
shakeButton.startAnimation(shakeAnimation);
}
});
}
}
Ничего нам не мешает применить эффект дрожания и к целому экрану (разметка LinearLayout). Добавим пару строчек кода
final View linear = (View) findViewById(R.id.linear);
linear.startAnimation(shakeAnimation);
// shakeButton.startAnimation(shakeAnimation);
Как видите, ничего сложного здесь нет. Кстати, в документации к Android также используется подобный приём. Предположим, у нас есть два текстовых поля для ввода логина и пин-кода. Если пользователь вводит пин-код, длиной меньше или больше, чем четыре символа — окно ввода сотрясается. При правильном вводе пин-кода переходим на следующее окно.
final EditText editLogin = (EditText) this.findViewById(R.id.login);
final EditText editPin = (EditText) this.findViewById(R.id.pin);
shakeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// linear.startAnimation(shakeAnimation);
// shakeButton.startAnimation(a);
// Копируем пин-код который ввел пользователь в переменную pass
String pass = editPin.getText().toString();
// Проверяем равна ли длина 4
if (pass.length() == 4) {
// Выводим всплывающее окно
Toast toast = Toast.makeText(getApplicationContext(),
"Добро пожаловать, "
+ editLogin.getText().toString(),
Toast.LENGTH_SHORT);
toast.show();
// Создаем намерение для перехода на другую активность
// Intent intent = new Intent();
// intent.setClass(ShakeDemoActivity.this,
// WelcomeAndroid.class);
// startActivity(intent);
// // Закрываем текущий Activity
// finish();
} else {
// Если пин-код неправильной длины, то применияем анимацию
editPin.startAnimation(shakeAnimation);
// Выводем всплывающее окно "Wrong pin, must be 4 digits"
Toast toast = Toast.makeText(getApplicationContext(),
"Неверный пин-код, должно быть четыре цифры",
Toast.LENGTH_SHORT);
toast.show();
// Очищаем форму ввода пин кода
editPin.setText("");
}
}
});
Эффект слайд-шоу, когда один элемент выталкивает другой элемент, реализуется двумя анимациями. Вот пример движения слайдов слева направо. Разместите в папке res/anim файлы:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%"
android:toXDelta="100%"
android:fromYDelta="0%"
android:toYDelta="0%"
android:duration="500"
/>
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="-100%"
android:toXDelta="0%"
android:fromYDelta="0%"
android:toYDelta="0%"
android:duration="500"
/>
</set>
Такую анимацию можно увидеть в примере с элементом ViewFlipper
По значениям атрибутов android:fromXDelta и android:toXDelta можно видеть, что идёт смещение первого объекта от 0% до 100%, а второго объекта от -100% до 0.
Если вы хотите использовать анимацию в противоположном направлении, то поменяйте значения на нужные:
<!-- out.xml -->
android:fromXDelta="0%"
android:toXDelta="-100%"
<!-- in.xml -->
android:fromXDelta="100%"
android:toXDelta="0%
В Android есть уже предустановленные ресурсы для анимации эффекта слайд-шоу, поэтому необязательно создавать собственные XML-файлы, если вас устраивают системные параметры для показа слайдов. Код изменится незначительно (из примера Анимация переходов):
// Вместо
// Animation animationFlipIn = AnimationUtils.loadAnimation(this,
// R.anim.flipin);
// Animation animationFlipOut = AnimationUtils.loadAnimation(this,
// R.anim.flipout);
// Можно использовать
final Animation animationFlipIn = AnimationUtils.loadAnimation(this,
android.R.anim.slide_in_left);
final Animation animationFlipOut = AnimationUtils.loadAnimation(this,
android.R.anim.slide_out_right);
В этом примере задействованы ресурсы android.R.anim.slide_out_right и android.R.anim.slide_in_left, которые создают эффект слайд-шоу слева-направо.