Как сделать плавное изменение цвета символов на Jpanel в Jframe?

И так, у меня есть мой код который рисует матрицу как в фильме, при чём я указал шанс на то, что цифра выпадает с большим процентом. Мой вопрос заключается в том что я хочу что-бы все символы были не статичными, а переливались. То есть когда символ доходит до верхней цветовой границы зелёного (255) он начинал темнеть (до 50). Так же я бы хотел что-бы это происходило где-то за 10 секунд (в одну сторону).

import javax.swing.*;
import java.awt.*;
import java.util.Random;

import static java.awt.FlowLayout.CENTER;
import static java.lang.Math.floorDiv;

public class BigProject extends JFrame {
    public static void main(String[] args) {

        var random = new Random();

        var f = new JFrame();
        f.getContentPane().setBackground(Color.BLACK);
        f.setDefaultCloseOperation(EXIT_ON_CLOSE);
        f.setUndecorated(true);
        f.getContentPane().setLayout(new FlowLayout(CENTER, 0, 0));

        var screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        var labelSize = new Dimension(100, 60);
        var rw = screenSize.getWidth() / (int) labelSize.getWidth();
        var rh = screenSize.getHeight() / (int) labelSize.getHeight();
        var repeat = rw * rh;

        var digit = new String[]{
                "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
        };
        var letter = new String[]{
                "a", "b", "c", "d", "f", "g", "h", "i", "j", "k",
                "l", "m", "n", "o", "q", "r", "s", "t", "u", "y",
                "x", "z"
        };

        for (var i = 0; i < repeat; i++) {
            int a = random.nextInt(25);
            if (a < 5) {
                var number = random.nextInt(letter.length);
                var label = getLabelG(labelSize, random, letter[number]);
                f.add(label);
            }
            if (a >= 5) {
                var number = random.nextInt(digit.length);
                var label = getLabelG(labelSize, random, digit[number]);
                f.add(label);
            }
            f.setExtendedState(Frame.MAXIMIZED_BOTH);
        }
        f.setVisible(true);


    }
    private static JLabel getLabelG(Dimension labelSize, Random random, String letter) {
        var label = new JLabel(letter, SwingConstants.CENTER);
        label.setPreferredSize(labelSize);
        label.setFont(label.getFont().deriveFont(48.0F));
        label.setForeground(new Color(0, 50 + random.nextInt(206), 0));
        return label;
    }
}

Ответы (1 шт):

Автор решения: Nowhere Man

Чтобы обеспечить "мерцание", достаточно будет организовать изменение цвета всех лейблов при помощи javax.swing.Timer, а не в отдельном потоке Thread (благодарность talex за существенное замечание).

При этом также понадобится завести массив булевых флажков для различения режимов изменения цвета "темнее/светлее".

Вариант реализации с рандомизированным изменением цвета:

import java.awt.*;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.*;

import static java.awt.FlowLayout.CENTER;
import static java.lang.Math.floorDiv;

public class BigProject extends JFrame {
    private static int COLOR_DIFF = 21;

    public static void main(String[] args) {

        var random = new Random();

        var f = new JFrame();
        f.getContentPane().setBackground(Color.BLACK);
        f.setDefaultCloseOperation(EXIT_ON_CLOSE);
        f.setUndecorated(true);
        f.getContentPane().setLayout(new FlowLayout(CENTER, 0, 0));

        var screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        var labelSize = new Dimension(100, 60);
        var rw = floorDiv((int) screenSize.getWidth(), (int) labelSize.getWidth());
        var rh = floorDiv((int) screenSize.getHeight(), (int) labelSize.getHeight());
        var repeat = rw * rh;

        var digit = new String[]{
                "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
        };
        var letter = new String[]{
                "a", "b", "c", "d", "f", "g", "h", "i", "j", "k",
                "l", "m", "n", "o", "q", "r", "s", "t", "u", "y",
                "x", "z"
        };

        for (var i = 0; i < repeat; i++) {
            var value = random.nextInt(25) < 5 ? get(random, letter) : get(random, digit);
            var label = getLabelG(labelSize, random, value);
            f.add(label);
            f.setExtendedState(Frame.MAXIMIZED_BOTH);
        }
        var darker = new boolean[repeat];

        ActionListener glitter = ae -> {
            for (var i = 0; i < repeat; i++) {
                var label = f.getContentPane().getComponent(i);
                var currColor = label.getForeground();
                var green = currColor.getGreen() +  random.nextInt(21) * (darker[i] ? -1 : 1);
                if (green > 255) {
                    green = 255;
                    darker[i] = true;
                } else if (green < 50) {
                    green = 50;
                    darker[i] = false;
                }
                label.setForeground(new Color(currColor.getRed(), green, currColor.getBlue()));
            }
        };
        // запуск изменения цвета 20 раз в секунду
        new Timer(50, glitter).start();

        f.setVisible(true);
    }

    private static String get(Random random, String ... strings) {
        return strings[random.nextInt(strings.length)];
    }


    private static JLabel getLabelG(Dimension labelSize, Random random, String letter) {
        var label = new JLabel(letter, SwingConstants.CENTER);
        label.setPreferredSize(labelSize);
        label.setFont(label.getFont().deriveFont(48.0F));
        label.setForeground(new Color(0, 50 + random.nextInt(206), 0));
        return label;
    }
}
→ Ссылка