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:
- Draw the WIFI view effect with full signal first, i.e. static view
- 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.
First draw the static effect graph and dissect which parts of the whole graph are composed of: (two circles, four arcs)
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