Transitions-Everywhere
Transitions-Everywhere copied to clipboard
Custom transitions
Привет! На одном из моих проектов мне надо было анимировать смену TextView::textSize и я написал вот такой Transition. Может еще кому-то понадобится и поэтому решил создать pull request с ним (Он был написан на котлине. Но для ПР я его переписал на java)
import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.util.TypedValue
import android.view.ViewGroup
import android.widget.TextView
import androidx.transition.Transition
import androidx.transition.TransitionValues
class ChangeTextSize : Transition {
constructor() : super()
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
private fun captureValues(transitionValues: TransitionValues) {
if (transitionValues.view is TextView) {
transitionValues.values[PROP_NAME] = (transitionValues.view as TextView).textSize
}
}
override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
if (startValues == null || endValues == null || startValues.view !is TextView || endValues.view !is TextView)
return null
val start = startValues.values[PROP_NAME] as? Float ?: return null
val end = endValues.values[PROP_NAME] as? Float ?: return null
if (start == end) return null
val view = endValues.view as TextView
return ValueAnimator.ofFloat(start, end)
.apply { addUpdateListener { view.setTextSize(TypedValue.COMPLEX_UNIT_PX, it.animatedValue as Float) } }
}
companion object {
private const val PROP_NAME = "ChangeTextSize.textSize"
}
}
Неплохая идея собрать библиотеку с разными кастомными Transition! Жду Вашего фид-бэка!
Привет! Спасибо за желание поделиться своим кастомным транзишном!
К сожалению конкретно про этот у меня есть сомнения и я не хотел бы иметь его частью библиотеки. Причина простая: не стоит анимировать вызов setTextSize, потому что он в том числе изменяет размер вьюхи и влечет за собой вызов requestLayout и весь парент лейаут и все следующие зависящие от его размера лейауты будут обязаны пройти через measure/layout цикл на каждом фрейме анимации и это не может работать оптимально и давать плавную анимацию. На быстрых флагманах это может быть незаментно и если конкретно под требования вашего проекта такая реализация подходит то все равно не стоит её продвигать как рекомендованную. Как бы я заимплементил подобное: создал бы кастомную вьюху где рисовал нужный текст прям на канвасе и на каждый шаг анимации только вызывал invalidate, где в onDraw применял новый размер, но это не меняло бы размер View.
Еще раз спасибо!
Привет еще раз! Не хотел создавать отдельный ишю ведь тема та же. Я написал еще один простенький транзишн.
Как я писал вверху идея собрать либку с кастомными транзишнами очень хороша. Таким образом, если каждый хто писал транзишины кинет свои кастомные версии, другие разработчики не будут тратить время, а будуть иметь большой выбор транзишнов под рукой.
Вот он:
import android.animation.Animator
import android.animation.ValueAnimator
import android.view.ViewGroup
import android.widget.ProgressBar
import androidx.transition.Transition
import androidx.transition.TransitionValues
class ChangeProgress : Transition() {
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
fun captureValues(values: TransitionValues) {
(values.view as? ProgressBar)?.apply {
values.values[PROP_NAME] = progress
}
}
override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
if (startValues == null || endValues == null || startValues.view !is ProgressBar || endValues.view !is ProgressBar)
return null
val start = startValues.values[PROP_NAME] as? Int ?: return null
val end = endValues.values[PROP_NAME] as? Int ?: return null
if (start == end) return null
val view = endValues.view as ProgressBar
return ValueAnimator.ofInt(start, end)
.apply { addUpdateListener { view.progress = it.animatedValue as Int } }
}
companion object {
const val PROP_NAME = "android:progress:progress"
}
}
Создавать пулл реквест не тороплюсь, а буду ждать от Вас фидбека!
P.S.
Я знаю о том что есть метод ProgressBar.setProgress(progress: Int, animated: Boolean)
. Но он доступен только с 24 API.
Привет! Я думаю в этом случае правильнее было бы использовать этот транзишн для версий до 24 Api, а начиная с него официальную функциональность. Причина простая: setProgress(animated = true) будет работать гораздо плавнее, он внутри анимирует прогресс как float, то есть не просто скачет по целым числам и прогресс выглядит плавнее. особенно это будет заметно когда прогресс меняется не слишком сильно, например с 10 до 15, если duration 300, то анимация будет состоять из примерно 18 фреймов, а реальных значений только 5. Поэтому может не стоит делать и этот транзишн частью библиотеки, потому что я хочу в ней иметь только подходы, которые могу назвать рекомендованными. Здесь я бы или использовал этот транзишн до 24 апи, или вообще не имел никакой анимации до 24 апи, возможно это не критично для бизнеса, в виду малого количества пользователей на этих версиях. Спасибо!