Reveal для всех!

в 13:37, , рубрики: android, library, open source, Разработка под android

Reveal для всех! - 1

Здравствуйте, Хабровчане !
Я расскажу как я реализовал Circual Reveal анимацию из Lollipop, опустив порог до Gingerbread.

В Lollipop появились нативные классы HardwareCanvas, RenderNodeAnimator для более плавной анимации и отрисовки, на нем построена Ripple и Reveal анимация. Теперь многие элементы рисуются и анимируются на уровне железа.

Родная анимация

	public static Animator createCircularReveal(View view,
            int centerX,  int centerY, float startRadius, float endRadius) {
        return new RevealAnimator(view, centerX, centerY, startRadius, endRadius);
    }

Как описанно в документации, RevealAnimator — это анимированная обрезка круга. Он в свою очередь произошел от RenderNodeAnimator и вызывает нативный метод.

    private static native long nCreateRevealAnimator(
            int x, int y, float startRadius, float endRadius);

Там уже и идет обрезка круга, через:

RevealAnimator::RevealAnimator(int centerX, int centerY,
        float startValue, float finalValue)
        : BaseRenderNodeAnimator(finalValue)
        , mCenterX(centerX)
        , mCenterY(centerY) {
    setStartValue(startValue);
}

float RevealAnimator::getValue(RenderNode* target) const {
    return target->properties().getRevealClip().getRadius();
}

// здесь и происходит обрезка
void RevealAnimator::setValue(RenderNode* target, float value) {
    target->animatorProperties().mutableRevealClip().set(true,
            mCenterX, mCenterY, value);
}

Решение

Мое решение относительно простое: я создал свои кастомные Layout'ы (Frame | Linear, оригинальные, почти нетронутые), изменив всего лишь метод, где рисуются дети


    @Override
    protected boolean drawChild(@NonNull Canvas canvas, @NonNull View child, long drawingTime) {
        if(!mClipOutlines && child != mTarget)
            return super.drawChild(canvas, child, drawingTime);

        final int state = canvas.save();

        mRevealPath.reset();
        mRevealPath.addCircle(mCenterX, mCenterY, mRadius, Path.Direction.CW);

        canvas.clipPath(mRevealPath);

        boolean isInvalidated = super.drawChild(canvas, child, drawingTime);

        canvas.restoreToCount(state);

        return isInvalidated;
    }

Я лишь прошу canvas обрезать определненную область во время анимации. Анимация реализуется через ObjectAnimator (thanks to Jake Wharton, nineoldsandroid).

Статья получилась немного скомканной, если есть вопросы пишите в комментарии, дополню статью :)

Спасибо за внимание!
Github

Автор: 03uk

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js