Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to animate the drawing of arc in android

I am new to Android and I am using the drawArc function to show the progress of some task to the user but now I want to animate this so that it looks as if it is growing.

I use the following code but not working:

   new Thread(new Runnable() {
    int i=0;
    float startAngle =0;
    float swipeAngle = 40.7f;
    public void run() {
        while (i < swipeAngle) {
            canvas.drawArc(rectF, startAngle, i, false, paint);

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        i++;
    }
}).start();

Can Someone Please suggest whats wrong in this or Can suggest some other idea to animate.

like image 570
FiXiT Avatar asked Jun 08 '16 17:06

FiXiT


2 Answers

  1. Better create a custom view class say "ArcView" which will extend View class. Then add your draw code in onDraw method.
    1. For Animation, you can write separate class which will extend Animation class
    2. pass your custom view to Animation class and under applyTransformation() method, calculate angle then set it to your custom view and invalide the view.

You are done with Arc with nice animation.

View Class

public class ArcView extends View {

private final Paint mPaint;
private final RectF mRect;
private float arcAngle;

    public ArcView(Context context, AttributeSet attrs) {

    super(context, attrs);

    // Set Angle to 0 initially
    arcAngle = 0;

    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(20);
    mPaint.setColor(Color.RED);
    mRect = new RectF(20, 20, 220, 220);

    }

    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawArc(mRect, 90, arcAngle, false, mPaint);
    }

    public float getArcAngle() {
    return arcAngle;
    }

    public void setArcAngle(float arcAngle) {
    this.arcAngle = arcAngle;
    } 
}

Animation Class

public class ArcAngleAnimation extends Animation {

private ArcView arcView;

private float oldAngle;
private float newAngle;

    public ArcAngleAnimation(ArcView arcView, int newAngle) {
    this.oldAngle = arcView.getArcAngle();
    this.newAngle = newAngle;
    this.arcView = arcView;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation transformation) {
    float angle = 0 + ((newAngle - oldAngle) * interpolatedTime);

    arcView.setArcAngle(angle);
    arcView.requestLayout();
    }
}

Inside Activity, you can have below code to instantiate arc view.

 ArcView arcView = (ArcView) findViewById(R.id.arcView);
    ArcAngleAnimation animation = new ArcAngleAnimation(arcView, 360);
    animation.setDuration(1000);
    arcView.startAnimation(animation);

Inside layout,

    <com.graph.app.Test.ArcView
       android:id="@+id/arcView"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
like image 191
Prashant Avatar answered Oct 13 '22 13:10

Prashant


One of the most important rules of programming for Android is that you should limit things you do in onDraw as much as you can. Ideally you shouldn't be allocating anything in onDraw method. Just drawing on canvas.

And unfortunately that's only the first thing that is wrong with your code.

You should think about it this way: a single canvas object is only viable during a single (simple) onDraw method call. So you cannot reuse it inside a separate thread.

For your case you should start a separate thread (just like you did in your example), but inside it you should only update a View's field that represents the arc's angle and ask for your View to redraw (the invalidate() method).

public class TestView extends View {

    private float currentAngle;

    //... some needed methods (be sure to init rectF and paint somewhere here)

    public void startAnimatingArc(final float maxAngle) {
        currentAngle = 0;
        new Thread(new Runnable() {
            public void run() {
                while (currentAngle < maxAngle) {
                    invalidate();
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                currentAngle++;
            }
        }).start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawArc(rectF, 0, currentAngle, false, paint);
        // you should try not to add anything else in this call
        // (unless you want to draw something else as well)
    }
}

The whole "animation" code architecture could be much better as well. But the main problem is with the canvas interaction, so that's what I corrected in your code.

like image 34
Bartek Lipinski Avatar answered Oct 13 '22 14:10

Bartek Lipinski