Reveal для всех!



    Здравствуйте, Хабровчане !
    Я расскажу как я реализовал 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
    • +17
    • 10.3k
    • 6
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 6

      0
      А такую анимацию нынче по гайдам на все клики вешать принято?
        0
        Нет вы что, он полезен если вы скрываете / показываете группу UI элементов
          0
          Ага, я значит не совсем про то подумал.
          Просто сейчас наблюдаю на android 5.0 практически на всех элементах в гугловских material приложения такой вот расползающийся круг при кликах/лонг-кликах. При этом лонг-клики делают тоже, что и обычные, потому не ясна суть этих кругов %)
            0
            это ripple эффект :)
        0
        По-моему, у анимации очень неестественный easing.
          0
          AccelerateInterpolator указан

        Only users with full accounts can post comments. Log in, please.