AnimatedDotsView Example
1. In java/kotlin manager, create a java class file AnimatedDotsView.java. Put following codes in it.
package com.my.newproject14; //Change to your own package name
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class AnimatedDotsView extends View {
//com.my.newproject14.AnimatedDotsView
// Particle/ Dot model
private static class Particle {
float x, y;
float vx, vy;
float radius;
float alpha; // 0..1
float life; // total life (ms)
float age; // current age (ms)
boolean isAlive() {
return alpha > 0 && age < life;
}
}
private final Paint dotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final List<Particle> particles = new ArrayList<>();
private final Random random = new Random();
// Background gradient shader
private Shader bgShader;
private int[] gradientColors = new int[]{
Color.BLACK,
Color.parseColor("#0D47A1"),
Color.parseColor("#FF4081")
};
// Configurables
private int maxParticles = 180;
private float spawnChancePerFrame = 0.10f; // per frame probability to spawn one (can spawn more)
private float spawnBurst = 1.2f; // average particles per spawn
private float minRadius = 4f; // dp-ish units (interpreted relative to density)
private float maxRadius = 14f;
private float minSpeed = 10f; // px per second
private float maxSpeed = 120f; // px per second
private float minLife = 900f; // ms
private float maxLife = 2200f; // ms
// Frame timing
private long lastTimeNs = 0L;
private boolean running = false;
public AnimatedDotsView(Context context) {
super(context);
init();
}
public AnimatedDotsView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public AnimatedDotsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// White dot paint
dotPaint.setStyle(Paint.Style.FILL);
dotPaint.setColor(Color.WHITE);
}
@Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
super.onSizeChanged(w, h, oldW, oldH);
// Create a left-to-right gradient: black -> deep blue -> pink
createShader(w, h);
}
private void createShader(int w, int h) {
bgShader = new LinearGradient(
0, 0, w, h,
gradientColors, // <- use current color array
new float[]{0.0f, 0.6f, 1.0f},
Shader.TileMode.CLAMP
);
}
//Public method: dynamically set gradient colors
//Accepts 2 or 3 colors (will blend accordingly)
public void setGradientColors(int... colors) {
if (colors == null || colors.length == 0) return;
this.gradientColors = colors;
createShader(getWidth(), getHeight()); // recreate shader with new colors
invalidate(); // redraw immediately
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
start();
}
@Override
protected void onDetachedFromWindow() {
stop();
super.onDetachedFromWindow();
}
//Start the animation loop.
public void start() {
if (!running) {
running = true;
lastTimeNs = System.nanoTime();
postInvalidateOnAnimation();
}
}
//Stop the animation loop and clear particles.
public void stop() {
running = false;
particles.clear();
// ensure redraw to clear screen
postInvalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw blended background
Paint bgPaint = new Paint();
bgPaint.setShader(bgShader);
canvas.drawRect(0, 0, getWidth(), getHeight(), bgPaint);
long nowNs = System.nanoTime();
float deltaMs = (lastTimeNs == 0) ? 16f : (nowNs - lastTimeNs) / 1_000_000f;
lastTimeNs = nowNs;
// Randomly spawn new particles (respecting max limit)
if (random.nextFloat() < spawnChancePerFrame && particles.size() < maxParticles) {
// spawn between 1 and around spawnBurst particles
int toSpawn = Math.max(1, Math.round(spawnBurst + (random.nextFloat() - 0.5f)));
for (int i = 0; i < toSpawn && particles.size() < maxParticles; i++) {
float x = random.nextFloat() * getWidth();
float y = random.nextFloat() * getHeight();
spawnParticle(x, y);
}
}
// Update particles
float deltaSec = Math.max(0.0001f, deltaMs / 1000f);
Iterator<Particle> it = particles.iterator();
while (it.hasNext()) {
Particle p = it.next();
// age and life
p.age += deltaMs;
float lifeProgress = p.age / p.life;
// alpha fades nonlinearly (ease out)
p.alpha = Math.max(0f, 1f - lifeProgress);
// update position
p.x += p.vx * deltaSec;
p.y += p.vy * deltaSec;
// slight drag for smoother motion
p.vx *= 0.995f;
p.vy *= 0.995f;
// Remove when dead or off-canvas for long
if (!p.isAlive() || p.x < -p.radius * 2 || p.x > getWidth() + p.radius * 2
|| p.y < -p.radius * 2 || p.y > getHeight() + p.radius * 2) {
it.remove();
}
}
// Draw particles (white dots with alpha)
for (Particle p : particles) {
int a = Math.round(p.alpha * 255f);
a = Math.max(0, Math.min(255, a));
dotPaint.setAlpha(a);
canvas.drawCircle(p.x, p.y, p.radius, dotPaint);
}
// schedule next frame
if (running) {
postInvalidateOnAnimation();
}
}
// Helper to spawn one particle with randomized properties
private void spawnParticle(float x, float y) {
Particle p = new Particle();
p.x = x;
p.y = y;
// random speed and direction
float speed = lerp(minSpeed, maxSpeed, random.nextFloat()); // px/sec
double angle = random.nextDouble() * Math.PI * 2.0;
p.vx = (float) (Math.cos(angle) * speed);
p.vy = (float) (Math.sin(angle) * speed * 0.75f); // slightly elliptical movement
// radius in px (scale to view density optionally)
p.radius = lerp(minRadius, maxRadius, random.nextFloat());
// life in ms
p.life = lerp(minLife, maxLife, random.nextFloat());
p.age = 0f;
p.alpha = 1f;
particles.add(p);
}
// Utility: linear interpolation
private static float lerp(float a, float b, float t) {
return a + (b - a) * t;
}
public void clearParticles() {
particles.clear();
postInvalidate();
}
}
2. In main.xml add a LinearLayout linear1. Change id to animatedDotsView. Convert the LinearLayout to com.my.newproject14.AnimatedDotsView. Here change the package name to package name of your project. Set padding to 0.
3. In onCreate, set the gradient colors for the AnimatedDotsView.
binding.animatedDotsView.setGradientColors(Color.RED, Color.parseColor("#22dd22"), Color.BLUE);
4. To set this View as background of your Activity:
- Suppose linear1 is main view of your Activity, containing TextViews, Buttons etc. Set height of linear1 to wrap content
- Add a new LinearLayout. Set width and height to match parent. Convert it to FrameLayout. Change id to frame1. Set padding to 0.
- Inside this Frame layout frame1, add a LinearLayout. Set padding of this linear layout to 0. Change id to animatedDotsView. Convert the LinearLayout to com.my.newproject14.AnimatedDotsView. Set width to match parent and height to wrap content.
- Place linear1 below animatedDotsView. Set height of linear1 to match parent.
- Set height of animatedDotsView to match parent.
In the end it should be frame1 containing animatedDotsView and linear1.

Comments
Post a Comment