Reveal for everyone!



    Hello, Khabrovsk citizens !
    I'll tell you how I implemented the Circual Reveal animation from Lollipop, lowering the threshold to Gingerbread.

    Lollipop introduced native classes HardwareCanvas, RenderNodeAnimator for smoother animation and rendering, Ripple and Reveal animation are built on it. Now many elements are drawn and animated at the iron level.



    Native animation



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


    As described in the documentation, RevealAnimator is an animated cropping of a circle. It, in turn, descended from the RenderNodeAnimator and calls the native method.

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


    There is already cropping a circle, through:

    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);
    }
    


    Decision



    My solution is relatively simple: I created my own custom Layouts (Frame | Linear, original, almost untouched), changing only the method where children are drawn

    
        @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;
        }
    


    I just ask canvas to crop a specific area during the animation. Animation is implemented through ObjectAnimator (thanks to Jake Wharton, nineoldsandroid).



    The article turned out to be a little crumpled, if you have questions, write in the comments, I will supplement the article :)

    Thanks for your attention!
    Github

    Also popular now: