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

Popular posts from this blog

Simple car racing android game in Sketchware

How to enable upload from webview in Sketchware?

Simple Audio recorder app in Sketchware

Retrieve contact list in Sketchware

Custom dialog box in Sketchware using CustomView