Точный контроль над андроидами VectorDrawable анимации
ОБНОВЛЕНИЕ: Решение найдено! Прокрутите вниз мой принятый ответ!
Я хочу анимировать несколько элементов одного изображения и связать анимацию с позицией ViewPagers (так что несколько элементов морфируют или летают в/из в зависимости от текущей страницы, перетаскиваемой).
Итак, есть ли способ точно контролировать текущий кадр анимации? Например, допустим, что у меня есть этот набор:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="@android:anim/decelerate_interpolator"
android:duration="800"
android:propertyName="scaleY"
android:valueFrom="0"
android:valueTo="1" />
<objectAnimator
android:interpolator="@android:anim/decelerate_interpolator"
android:duration="800"
android:propertyName="scaleX"
android:valueFrom="0"
android:valueTo="1" />
<objectAnimator
android:interpolator="@android:anim/decelerate_interpolator"
android:duration="800"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
</set>
Анимированный векторный файл:
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/pre_signup_1" >
<target
android:name="plus_button"
android:animation="@anim/pre_signup_1_plus_container" />
<target
android:name="plus"
android:animation="@anim/pre_signup_1_plus_sign" />
</animated-vector>
Java-код для запуска анимации:
ImageView mImage1 = (ImageView) findViewById(R.id.image_1);
AnimatedVectorDrawableCompat animated = (AnimatedVectorDrawableCompat) mImage1.getDrawable();
animated.start();
Есть ли способ управлять анимацией, например setCurrentDuration(400)
, которая предположительно установит для нее текущее состояние анимации? Может быть, есть способ разделить этот вектор на слои и анимировать их программно? Спасибо заранее!
Ответы
Ответ 1
Похоже, что возможно доступ к путям и группам внутри VectorDrawableCompat
и анимация/морфинг, но вы хотите!
После некоторого исследования я закончил дублирование следующих классов из пакета android.support.graphics.drawable
: AndroidResources
, PathParser
, TypedArrayUtils
, VectorDrawableCommon
и VectorDrawableCompat
.
Далее нам нужно сделать следующий класс и классы общедоступными внутри класса VectorDrawableCompat
: getTargetByName
, VGroup
и VFullPath
.
Далее в классе VectorDrawableCompat
удалите блок, который проверяет версию Android (Build.VERSION.SDK_INT >= 23
). Не знаю, почему, но если вы этого не сделаете, анимация не будет работать на API Android 23 и выше (нужно больше исследований).
Возможно, я пропустил несколько частных методов, но это просто вопрос их публикации, если вы столкнулись с проблемами.
Итак, теперь у нас есть доступ к слоям нашего VectorDrawable
! Вот небольшой пример масштабирования векторной группы в зависимости от положения ViewPager
:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="276dp"
android:height="359dp"
android:viewportWidth="276"
android:viewportHeight="359">
<group
android:pivotX="205.5"
android:pivotY="214.5"
android:name="my_group">
<path
android:strokeColor="#4D394B"
android:strokeWidth="7"
android:strokeLineJoin="bevel"
android:fillColor="#1ED761"
android:pathData="M206.5,180 C186.9,180,171,195.9,171,215.5 S186.9,251,206.5,251
C226.1,251,242,235.1,242,215.5 S226.1,180,206.5,180 Z" />
<path
android:fillColor="#4D394B"
android:pathData="M210,211 L210,190 L202,190 L202,211 L181,211 L181,219 L202,219 L202,241 L210,241
L210,219 L232,219 L232,211 Z" />
</group>
</vector>
И это код, используемый для анимации группы:
ImageView myImageView;
VectorDrawableCompat.VGroup myVectorGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
myImageView = (ImageView) findViewById(R.id.image_1);
VectorDrawableCompat vectorDrawable = VectorDrawableCompat.create(getResources(), R.drawable.my_vector_drawable, null);
vectorDrawable.setAllowCaching(false); // Important to allow image updates
myVectorGroup = (VectorDrawableCompat.VGroup) vectorDrawable.getTargetByName("my_group");
myImageView.setImageDrawable(vectorDrawable);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(myVectorGroup != null && position < 1) {
myVectorGroup.setScaleX(1f - positionOffset);
myVectorGroup.setScaleY(1f - positionOffset);
myImageView.invalidate();
}
}
});
}
Мне нужно еще некоторое тестирование для определения совместимости, но оно работает сейчас!
Ответ 2
Что касается документации для AnimatedVectorDrawables, нет способа отслеживать состояние анимации или переходить в любое состояние, отличное от animationStart и animationEnd.
Возможно, вам придется использовать специальное решение для достижения этого эффекта. Это сообщение в блоге объясняет способ использования отслеживания состояния виджета Androids для достижения аналогичного эффекта.
Ответ 3
Я считаю, что вы не можете сделать это с помощью AnimatedVectorDrawable/AnimatedVectorDrawableCompat программно.
Обходным путем является использование списка уровней, отображаемых, и анимировать его уровни :
но вам нужно много изображений для уровней, чтобы имитировать плавный переход, я использую только три уровня здесь для демонстрации.
Сначала вы определите список доступных типов: res/drawable/my_level_list.xml
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/level0"
android:maxLevel="0"/>
<item android:drawable="@drawable/level1"
android:maxLevel="1"/>
<item android:drawable="@drawable/level2"
android:maxLevel="2"/>
</level-list>
Затем в файле макета:
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/my_level_list" />
Затем в вашей деятельности:
int MIN_LEVEL=0;
int MAX_LEVEL=2;
ImageView img=(ImageView)findViewById(R.id.img);
Drawable myLevelSetDrawable= img.getDrawable();
ObjectAnimator anim=ObjectAnimator.ofInt(myLevelSetDrawable,"level",MIN_LEVEL,MAX_LEVEL);
anim.setInterpolator(new BounceInterpolator());
AnimatorSet set=new AnimatorSet();
set.play(anim);
set.setDuration(1000);
set.start();
Надеюсь, что это поможет.