Android Animate Rectangle in Custom View
Asked Answered
C

2

7

I have created a class that extends View which I shall reference within a layout for it to be drawn on the screen.

This class simply represents a Rectangle where I wish to make the length of the rectangle decrease all the way to 0.

Constructor:

public CardAnimationNew(Context context, AttributeSet attrs) {
    super(context, attrs);
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(getResources().getColor(R.color.card_grey_underline));
}

onMeasure:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (mRectangle == null) {
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        mRectangle = new Rect(0, 0, mWidth, mHeight);
        animateRectangle();
    }
}

animateRectangle:

private void animateRectangle() {
    mObjectAnimator = ObjectAnimator.ofFloat(mRectangle, "width", 5);
    mObjectAnimator.setDuration(1000);
    mObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            invalidate();
        }
    });
    mObjectAnimator.start();
}

onDraw:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawRect(mRectangle, mPaint);
}

The problem is that the Rect isn't animating at all. It stays the same length. Where am I going wrong?

Curcio answered 27/3, 2014 at 16:53 Comment(2)
Updated question to include a missing method (animateRectangle).Curcio
post your logcat since Rect object doesnt have setWidth methodSullivan
A
10

According to the documentation the property need a setter in the class you are applying the animator.

This name will be used to derive a setter function that will be called to set animated values.

The point is that Rect doesn't have a setWidth() or a setHeight().

Moreover Rect doesn't have a width and height property, by the way you can animate its four coordinates (top, left, right, bottom) and achieve the effect you need. (just consider width as Math.abs(left - right) and height as Math.abs(top - bottom)).

In order to have it working on a Rect (or a RectF like in the example below - if you need it on a Rect just use int setters instead of floats) you need to subclass it by adding the setters of the property you will need:

private class AnimatableRectF extends RectF{
        public AnimatableRectF() {
            super();
        }

        public AnimatableRectF(float left, float top, float right, float bottom) {
            super(left, top, right, bottom);
        }

        public AnimatableRectF(RectF r) {
            super(r);
        }

        public AnimatableRectF(Rect r) {
            super(r);
        }

        public void setTop(float top){
            this.top = top;
        }
        public void setBottom(float bottom){
            this.bottom = bottom;
        }
        public void setRight(float right){
            this.right = right;
        }
        public void setLeft(float left){
            this.left = left;
        }

    }

Then you can animate it the normal way... here an example to animate an AnimatableRectF called mCurrentRect to perform a translation:

float translateX = 50.0f;
float translateY = 50.0f;
ObjectAnimator animateLeft = ObjectAnimator.ofFloat(mCurrentRect, "left", mCurrentRect.left, mCurrentRect.left+translateX);
ObjectAnimator animateRight = ObjectAnimator.ofFloat(mCurrentRect, "right", mCurrentRect.right, mCurrentRect.right+translateX);
ObjectAnimator animateTop = ObjectAnimator.ofFloat(mCurrentRect, "top", mCurrentRect.top, mCurrentRect.top+translateY);
ObjectAnimator animateBottom = ObjectAnimator.ofFloat(mCurrentRect, "bottom", mCurrentRect.bottom, mCurrentRect.bottom+translateY);
animateBottom.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator valueAnimator) {
                            postInvalidate();
                        }
                    });
AnimatorSet rectAnimation = new AnimatorSet();
rectAnimation.playTogether(animateLeft, animateRight, animateTop, animateBottom);
rectAnimation.setDuration(1000).start();

I know it's not a copy/paste solution to solve your problem but I hope this code will help you to understand how to fix your issue!

Alhambra answered 20/1, 2015 at 17:15 Comment(2)
This is a pretty solid solution. Thanks for the help!Merrymaking
Thanks a lot for this answer!! I was looking for something like this for aeons!!Megalomania
G
3

A RectEvaluator is the way to do this. Basically you can use an ObjectAnimator on a Rect and give it a start and end rect and it will evaluate the steps in between. This is only api >= 18 though. A decent write-up is here http://cogitolearning.co.uk/?p=1451

Gott answered 4/3, 2016 at 18:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.