Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
mstorm
7 лет назад

Android анимации. Часть 4. Управление анимацией VectorDrawable

В прошлой статье мы смогли анимировать SVG изображение, то есть vector, в нашем приложении. Однако встал вопрос, возможно ли управлять анимацией VectorDrawable?

Простая анимация. Animator - первая статья
Простая анимация. State View - вторая статья
Vector анимация. Стандартные решения – третья статья
VectorDrawable анимация. Управление анимацией. Хардкор – четвертая статья

Возьмем уже знакомые нам анимации из прошлой статьи и постараемся привязать их к прогрессу SeekBar. Контрол будем использовать из support библиотеки. Кроме этого нам понадобится сторонняя библиотека – RichPathView.

В activity_main.xml добавляем код:

<com.richpath.RichPathView
    android:id="@+id/richPathView"
    …
    app:vector="@drawable/ic_play" />

<android.support.v7.widget.AppCompatSeekBar
    android:id="@+id/seekBar"
    …
    android:max="100"
    android:progress="0" />

Переходим в MainActivity.java. Дальше мы будем работать только там. Вставляем код:

RichPath richPauseTop = imageRichPath.findRichPathByName("pause1");
if (richPauseTop != null) {
    pauseTop = new DynamicPath(richPauseTop,
            "M 44 32 L 44 64 L 100 64 L 100 64 Z",
            "M 32 40 L 32 56 L 96 56 L 96 40 Z",
            90);
}

RichPath richPauseBottom = imageRichPath.findRichPathByName("pause2");
if (richPauseBottom != null) {
    pauseBottom = new DynamicPath(richPauseBottom,
            "M 44 96 L 44 64 L 100 64 L 100 64 Z",
            "M 32 88 L 32 72 L 96 72 L 96 88 Z",
            90);
}

Давайте рассмотрим, что мы здесь делаем. Вызовом функции imageRichPath.findRichPathByName("pause1") мы проводим поиск Path в ic_play.xml (в activity_main.xml мы его указали, как app:vector) по android:name. Единственный минус библиотеки RichPathView, он умеет работать только с Path, с Group работать не умеет.

Дальше мы проверяем, получилось ли найти Path, если да, то создаем DynamicPath для анимации. В параметрах мы передаем RichPath, а после несколько настроек – pathFrom, pathTo и rotation. Их мы берем из xml файлов, которые мы храним в res/animator.

Далее отслеживаем изменение ползунка seekBar:

seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
        if (pauseTop == null && pauseBottom == null)
            return;

        pauseTop.animate(progress);
        pauseBottom.animate(progress);
    }
    …
});

Здесь все просто и понятно. Теперь самое интересное. Где нам взять DynamicPath? Этот класс я написал сам. Пока он в сыром виде, возможно, когда-то я доведу его до ума и даже сделаю библиотеку для совсем простой работы с подобной анимацией. Вставляем код перед последней ‘}’ в MainActivity.java.

private class DynamicPath {
    private final String regEx = "((\\d+\\.\\d+)|(\\d+))";
    private final Locale locale = Locale.ENGLISH;

    private final RichPath richPath;

    private int rotation;
    private boolean isRotationAnimation = false;

    private final String dynamicPathFrom;
    private List<Float> pathDataFrom = new ArrayList<>();

    private final String dynamicPathTo;
    private List<Float> pathDataTo = new ArrayList<>();

    DynamicPath(@NonNull RichPath richPath, String pathFrom, String pathTo, @Nullable Integer rotation) {
        this.richPath = richPath;

        if (rotation != null) {
            this.rotation = rotation;
            isRotationAnimation = true;
        }

        dynamicPathFrom = getDynamicPath(pathFrom, pathDataFrom);
        dynamicPathTo = getDynamicPath(pathTo, pathDataTo);
    }

    private String getDynamicPath(String pathFrom, List<Float> pathDataFrom) {
        Matcher m = Pattern.compile(regEx)
                .matcher(pathFrom);
        while (m.find()) {
            try {
                pathDataFrom.add(
                        new DecimalFormat("0.#", DecimalFormatSymbols.getInstance(locale))
                                .parse(m.group())
                                .floatValue());
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return m.replaceAll("%f");
    }

    public void animate(int progress) {
        if (dynamicPathFrom.length() > 0 &&
                dynamicPathFrom.length() == dynamicPathTo.length() &&
                pathDataFrom.size() > 0 &&
                pathDataTo.size() > 0) {

            if (!dynamicPathFrom.equals(dynamicPathTo))
                throw new IllegalArgumentException("This animation not supported");

            Float[] pathFrom = pathDataFrom.toArray(new Float[pathDataFrom.size()]);
            Float[] pathTo = pathDataTo.toArray(new Float[pathDataTo.size()]);

            List<Float> args = new ArrayList<>();
            for (int i = 0; i < pathDataTo.size(); i++) {
                args.add(calcMorph(pathFrom, pathTo, i, progress));
            }

            richPath.setPathData(String.format(locale, dynamicPathTo, args.toArray()));
        }

        if (isRotationAnimation)
            richPath.setRotation((float) (richPath.getRotation() + (rotation * progress * 0.01)));

    }

    private float calcMorph(Float[] from, Float[] to, int position, int progress) {
        return from[position] - ((from[position] - to[position]) * progress * 0.01f);
    }
}

Здесь немного хардкора. Можете просто это скопировать :) Что получилось?
ezgif.com-video-to-gif.gif

Если у вас есть идеи по улучшению или вопросы, пишите. Постараюсь ответить.

28
0.000 GOLOS
На Golos с February 2018
Комментарии (1)
Сортировать по:
Сначала старые