Animation by Canvas

Animation in Android
Sometimes animating only View's subclasses is not sufficient. If we want to draw all the stuff, it may be a good idea to use Canvas, inside onDraw() method of the view. It allows drawing anything from simple circle to bezier curves or text.

Example 1: Create a new project with an empty template. To create a new custom view extend the view class as shown below. It is very important to have an exact look at the View's life cycle described in custom view in android.

public class CViewN extends View {
int width;
int height;
int gapV;
int x;
int y;
Paint vaweP;
Paint cPaint1;
Paint cPaint2;
Paint cPaint3;
Paint cPaint4;
int alpha = 0;
RectF rectCircle;
float initialRadius;
float radiusOffset;
ValueAnimator anim = ValueAnimator.ofFloat(0, 35);
public CViewN(Context context){
//this(context, null, 0);
super(context);
}
public CViewN(Context context, AttributeSet attrs){
//this(context, attrs, 0);
super(context, attrs);
init(context, attrs);
}
public CViewN(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public CViewN(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int desiredWidth = 1000;
int desiredHeight = 1500;
int width;
int height;
//width
if(widthMode == MeasureSpec.EXACTLY){
//Must be this size
width = widthSize;
}else if(widthMode == MeasureSpec.AT_MOST){
//Can't be bigger than...
width = Math.min(widthSize, desiredWidth);
}else{
//Be whatever you want
width = desiredWidth;
}
//height
if(heightMode == MeasureSpec.EXACTLY){
//Must be this size
height = heightSize;
}else if
(heightMode == MeasureSpec.AT_MOST){
//Can't be bigger than...
height = Math.min(heightSize, desiredHeight);
}else{
//Be whatever you want
height = desiredHeight;
}
//MUST CALL THIS
setMeasuredDimension(width, height);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
x = width/2;
y = height/2;
}
protected void init(Context context, @Nullable AttributeSet attrs) {
vaweP = new Paint(Paint.ANTI_ALIAS_FLAG);
vaweP.setStyle(Paint.Style.STROKE);
vaweP.setColor(Color.RED);
vaweP.setStrokeWidth(5);
gapV = 30;
}
@Override
protected void onAttachedToWindow(){
super.onAttachedToWindow();
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
radiusOffset = (float) valueAnimator.getAnimatedValue();
alpha = (int) ((float) valueAnimator.getAnimatedValue());
postInvalidate();
}
});
anim.setDuration(1000);
anim.setInterpolator(new LinearInterpolator());
anim.setRepeatMode(ValueAnimator.RESTART);
anim.setRepeatCount(100);
anim.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float radius = initialRadius + radiusOffset;
for(int i = 0; i < 10;i++ ){
canvas.drawCircle(x, y, radius, vaweP);
radius = radius + 35;
}
rectCircle = new RectF(0,0, width, height);
cPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
cPaint1.setColor(Color.GREEN);
cPaint1.setAlpha(30);
cPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
cPaint2.setColor(Color.RED);
cPaint2.setAlpha(30);
cPaint3 = new Paint(Paint.ANTI_ALIAS_FLAG);
cPaint3.setColor(Color.BLUE);
cPaint3.setAlpha(30);
cPaint4 = new Paint(Paint.ANTI_ALIAS_FLAG);
cPaint4.setColor(Color.YELLOW);
cPaint4.setAlpha(30);
canvas.drawArc(rectCircle, alpha, 90, true, cPaint1);
canvas.drawArc(rectCircle, alpha + 90, 90, true, cPaint2);
canvas.drawArc(rectCircle, alpha + 180, 90, true, cPaint3);
canvas.drawArc(rectCircle, alpha + 270, 90, true, cPaint4);
}
}
view raw CView.java hosted with ❤ by GitHub

Then edit the activity_main.xml.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.msh_n.myp.canvasanim1.CViewN
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

The MainActivity.java is shown below.

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

You can see the result here.


Comments