При программировании под андроид можно использовать уже ряд готовых анимаций переходов между экранами. Но что если дизайнер придумал более изощренные переходы? Рассмотрим какие есть возможности реализовать нестандартные смены экранов.
Активити и фрагменты
От того, используются ли переходы между Activity или все происходит в рамках разных фрагментов зависит и то, какие инструменты предлагает система для переходов между экранами.
Приложение с несколькими Activity
Когда используете несколько Activity, то можно использовать простые анимации переходов или создать красивые эффекты с shared element.
Простой переход между Activity
Можно использовать либо готовые переходы, которые предоставляет Android, либо определить свой, например
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromXDelta="100%p"
android:toXDelta="0%p"/>
Можно либо переопределить переходы для всех Acitivty в теме приложения
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorPrimary</item>
<item name="android:windowAnimationStyle">@style/CustomActivityAnimation</item>
...
<style name="CustomActivityAnimation" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/slide_in_right</item>
<item name="android:activityOpenExitAnimation">@anim/slide_out_left</item>
<item name="android:activityCloseEnterAnimation">@anim/slide_in_left</item>
<item name="android:activityCloseExitAnimation">@anim/slide_out_right</item>
...
Либо же переопределить переход в рамках отдельной Acitivty, вызвав
overridePendingTransition(R.anim.slide_in, R.anim.slide_out);
в onCreate вызываемой Activity.
Суть shared element transition в том, что общий элемент перетекает между двумя экранами, создавая видимость целостности двух Activity между собой.
Берете View с одного экрана. Говорите системе, что view с тем же id есть на следующем. Запускаете переход.
// get the element that receives the click event
val imgContainerView = findViewById<View>(R.id.img_container)
// get the common element for the transition in this activity
val androidRobotView = findViewById<View>(R.id.image_small)
// define a click listener
imgContainerView.setOnClickListener( {
val intent = Intent(this, Activity2::class.java)
// create the transition animation - the images in the layouts
// of both activities are defined with android:transitionName="robot"
val options = ActivityOptions
.makeSceneTransitionAnimation(this, androidRobotView, "robot")
// start the new activity
startActivity(intent, options.toBundle())
})
Остальное Android OS все сделает за вас.
Single activity application
Суть single activity application в его названии. Ваше приложение будет состоять из одной активности, а экраны внутри будут реализованы как фрагменты. Такой подход придает гибкость и контролируемость переходам. В рамках View можно делать абсолютно что угодно.
Вы можете управлять любой View, применять любые transition и т.д. С Activity такое не прокатит, потому что слушать или влиять на скорость отработки закрытия и открытия активности на прямую не получится.
Архитектура переходов между фрагментами одной activity
Сделать базовую логику переходов между экранами просто. Определяем интерфейс
interface AnimatedTransitionFragment {
fun exit() : Single<Boolean>
}
Не обязательно использовать Single из RxJava, можно установить любой тип callback. Метод exit() должен вернуть нечто, что можно "слушать" на предмет исполнения. Fragment, который должен поддерживать "выход" будет реализовывать интерфейс, приведенный выше.
Далее необходимо при переходе между фрагментами вызвать exit(). Подождать, когда Fragment исполнит необходимую анимацию, а затем перейти на новый Fragment, который может запустить анимацию "старта".
Если вы используете, например Cicerone, то можно переопределить свой AnimatedSupportAppNavigator
override fun applyCommands(commands: Array<out Command>?) {
val topFragment = _fragmentManager?.fragments?.lastOrNull()
val command = commands?.let { if (it.isNotEmpty()) it[it.size - 1] else null }
(topFragment as? AnimatedTransitionFragment)?.let {
if (shouldAnimatedTransition) {
it.exit().subscribe { _: Boolean? ->
super.applyCommands(commands)
}
} else {
super.applyCommands(commands)
}
} ?: let {
super.applyCommands(commands)
}
}
Происходит все тоже самое, что в Cicerone, только теперь, если Fragment поддерживает анимацию выхода, сначала проиграется она.
Анимация открытия и закрытия Fragment
Анимация выхода из Fragment
Сам Fragment реализуя exit() проигрывает анимацию выхода и отдает управление дальше.
override fun exit(): Single<Boolean> {
exitAnimResult = SingleSubject.create()
val views = getViewsToAnimate().filterNotNull() //Crashlytics will disagree with you...
AdditiveAnimator()
.setDuration(ANIMATION_DURATION)
.targets(views).rotationY(ROTATION_FACTOR).scaleX(SCALE_FACTOR).scaleY(SCALEY_FACTOR)
.setInterpolator(AccelerateInterpolator(2f))
.addListener(object : SimpleAnimatorListener() {
override fun onAnimationEnd(animation: Animator?) {
exitAnimResult?.onSuccess(true)
exitAnimResult = null
}
})
.start()
return exitAnimResult!!
}
Для простоты используется AdditiveAnimation. Он позволяет легко управлять анимацией разных элементов и слушать окончание проигрывания.
Анимация входа во Fragment
При необходимости в onViewCreated можно запустить анимацию "входа".
private fun entryScreenAnimation() {
var shift = 500f
for (view in listOf<View>(tvTitle, etFirstName, etLastName, btnNext)) {
view.alpha = 0f
view.translationY = shift
shift += 100f
}
AdditiveAnimator()
.setDuration(600)
.targets(tvTitle, etFirstName, etLastName, btnNext).translationY(0f)
.alpha(1f)
.start()
}
Это лишь база, необходимая для того, чтобы понимать как можно осуществлять навигацию между Activity и Fragment. Более сложные примеры зависят уже от конкретной задачи и прихоти дизайнера. Имея доступ к анимации на уровне View можно сделать любую мыслимую и не очень анимацию. Например можно запустить полноэкранную Lottie анимацию.
О Lottie можно почитать в мой статье инструменты Android разработчика.
Оригинал статьи размещен здесь: https://dimlix.com/animated-transition-android/