Android Custom Animation 1

Keywords: Android github Attribute Mobile

Android Custom Animation

In current mobile products, whether it is app or web pages, a cool page always attracts people's attention for the first time, so for android developers, to achieve a good page, they must master the technology of custom control and custom animation.

1.Android Native Animation

Several forms of native animation have been provided under Android:

(1) Interstitial animation

Translation: TranslateAnimation

Rotate: RotateAnimation

Zoom: ScaleAnimation

Gradient: AlphaAnimation

(2) Attribute animation

ObjectAnimatior: translation(x or y),rotation(x or y),scale(x or y)
ValueAnimator: Parent of ObjectAnimatior, Value Animation

(3) Frame animation

AniamteDrawable
  • Note the biggest difference between complementary and attribute animations:
    • The interpolation animation only changes the display of the View and does not really change the properties of the View
    • Property animations are properties that really change the View, such as the panning effect.
    • Attribute animation was introduced with Android 3.0

2.Android Custom Animation - Form One

So what is a custom animation?In fact, it is not obvious that the animation effect is defined according to your needs.Because in actual development, most of the sophisticated and cool animation effects can't be satisfied with the animations we've provided natively with Android, so we need to define them ourselves.

So this article will show you the first way to show custom animation in three cases - how to draw with custom controls

Then let's go through some small demo cases to demonstrate how to achieve some custom effects that native animations can't achieve.

(1) WIFI effect

First, look at the picture:

1) Ideas

Based on the effect diagram above, we can see that android native animation can not be achieved, so we need to customize the control to draw such effect dynamically, then the idea can be divided into the following two steps:

  1. Draw the WIFI view effect with full signal first, i.e. static view
  2. By using handler's postDelayed method, the invalidate() method of view can be executed in an endless loop to achieve the effect of dynamic drawing (several signals need to be controlled for each drawing)

2) Specific implementation

So because we need to draw this sector and arc, we first need to create a custom view to override its onDraw() method, which initializes the brush when the view is created before drawing.

The difficulty is how to draw dynamically in the second step. You can define a specific value, such as shouldExistSignalSize, to control which signal is drawn each time you draw. From the very beginning, only the first signal (i.e. sector) is drawn. Then the first and second signals need to be drawn the second time, followed by the first and second signals.Signal, third signal; remember to reset shouldExistSignalSize when all four grids are drawn.

The code is as follows:

/**
 * Created by PeiHuan on 2017/6/24.
 * <p>
 * WIFI control
 */
public class WIFIView extends View {

    public WIFIView(Context context) {
        this(context, null);
    }

    public WIFIView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WIFIView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private Paint paint;
    /**
     * Initialization preparation
     */
    private void init() {
        paint = new Paint();
        //stroke color
        paint.setColor(Color.BLACK);
        //Brush thickness
        paint.setStrokeWidth(6);
        //Anti-Aliasing
        paint.setAntiAlias(true);
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                invalidate();
                handler.postDelayed(this,500);
            }
        },500);
    }
    private Handler handler = new Handler();

    /**WIFI Length of the smaller side of the control*/
    private int wifiLength;
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        wifiLength = Math.min(w, h);
    }

    /**
     * Start angle
     */
    private float startAngle = -135;
    /**
     * Rotation angle of a sector or arc
     */
    private float sweepAngle = 90;
    /**
     * Signal size, default 4
     */
    private int signalSize = 4;

    /**The number of signals that should be drawn at a time*/
    private float shouldExistSignalSize = 0f;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        shouldExistSignalSize++;
        if(shouldExistSignalSize>4){
            shouldExistSignalSize=1;
        }
        canvas.save();
        //Calculate the radius of the circle where the smallest sector signal is located
        float signalRadius = wifiLength/2/signalSize;
        //Pan down the canvas to ensure that the drawing is fully displayed
        canvas.translate(0,signalRadius);
        for (int i = 0; i < signalSize; i++) {
            if(i>=signalSize-shouldExistSignalSize) {
                //Define the radius of the circle in which each signal is located
                float radius = signalRadius * i;
                RectF rectf = new RectF(radius, radius, wifiLength - radius, wifiLength - radius);
                if (i < signalSize - 1) {
                    paint.setStyle(Paint.Style.STROKE);
                    canvas.drawArc(rectf, startAngle, sweepAngle, false, paint);
                } else {
                    paint.setStyle(Paint.Style.FILL);
                    canvas.drawArc(rectf, startAngle, sweepAngle, true, paint);
                }
            }
        }
        canvas.restore();
    }
}

Refer to the GitHub repository for more code:
https://github.com/zphuanlove/AnimationProject

(2) MUSIC effect

Next, let's look at a very common effect, which is also achieved by using custom controls to dynamically draw ondraw(); this is also the effect of some music-related app s that you can now see, as shown below:

1) Ideas

The process and thought are similar to the first demo, but the strategy for animating is slightly different.

  1. First draw the static effect graph and dissect which parts of the whole graph are composed of: (two circles, four arcs)

  2. Animate by drawing continuously (changing the starting angle of four arcs)

WIFI demo We used handler's postDelayed method to create an infinite loop
This time we can animate by calling invalidate in the onDraw method; however, be aware that when the interface is closed and destroyed, do not execute invalidate anymore, causing the view to occupy memory that is not reclaimed.

2) Specific implementation

The implementation also initializes the brush at the beginning of the constructor, then draws a big circle, a small circle, and four arcs in the ondraw() method, where the four arcs can be divided into two parts and two relative parts, each consisting of one big arc and one small arc, with an interval of 180 degrees between the two parts.To draw an arc is to confirm the upper left and lower right of the circumferential rectangle of the circle in which the arc is located. The calculation in the following figure makes it easy to calculate the upper left and lower right of the rectangle in which the large arc is located:

The code is as follows:

/**
 * Created by PeiHuan on 2017/6/24.
 * <p>
 * Music control
 */
public class MusicView extends View {
    private Paint paint;
    private int length;

    public MusicView(Context context) {
        this(context, null);
    }

    public MusicView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MusicView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    /**
     * Initialization operation
     */
    private void init() {
        paint = new Paint();
        //stroke color
        paint.setColor(Color.BLACK);
        //Brush thickness
        paint.setStrokeWidth(2);
        //Anti-Aliasing
        paint.setAntiAlias(true);
        //No Fill
        paint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        length = Math.min(w, h);
        bigCircleRadius = length / 2;
        bigAngelRadius = length / 3;
        smallAngelRadius = length / 4;
    }

    /**
     * Radius of a great circle
     */
    private float bigCircleRadius;
    /**
     * The radius of a small circle
     */
    private float smallCircleRadius = 5f;
    /**
     * Radius of two large arcs
     */
    private float bigAngelRadius;
    /**
     * Radius of two small arcs
     */
    private float smallAngelRadius;

    private float startAngle1 = 0;
    private float startAngle2 = 180;
    private float sweepAngle = 60;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //Draw two circles
        canvas.drawCircle(bigCircleRadius, bigCircleRadius, bigCircleRadius - smallCircleRadius, paint);
        //Small circle thicker
        paint.setStrokeWidth(3);
        canvas.drawCircle(bigCircleRadius, bigCircleRadius, smallCircleRadius, paint);

        //Draw four arcs
        //Two large arcs, 180 degrees apart
        RectF rectF = new RectF(bigCircleRadius-bigAngelRadius,bigCircleRadius-bigAngelRadius,bigCircleRadius+bigAngelRadius,bigCircleRadius+bigAngelRadius);
        canvas.drawArc(rectF,startAngle1,sweepAngle,false,paint);
        canvas.drawArc(rectF,startAngle2,sweepAngle,false,paint);
        //Two small arcs, 180 degrees apart
        RectF rectFSmaller = new RectF(bigCircleRadius-smallAngelRadius,bigCircleRadius-smallAngelRadius,bigCircleRadius+smallAngelRadius,bigCircleRadius+smallAngelRadius);
        canvas.drawArc(rectFSmaller,startAngle1,sweepAngle,false,paint);
        canvas.drawArc(rectFSmaller,startAngle2,sweepAngle,false,paint);

        startAngle1+=5;
        startAngle2+=5;
        if(!isDetached) {
            invalidate();
        }
    }

    /**
     * Whether the custom control is detached from the form
     */
    private boolean isDetached;

    /**
     * Custom controls will be destroyed when they leave the form
     */
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        isDetached = true;
    }
}

Refer to the GitHub repository for more code:
https://github.com/zphuanlove/AnimationProject

Posted by ironside82 on Wed, 19 Jun 2019 09:19:19 -0700