/**
*
* @author 1BestCsharp
*/
public class RadioButtons_Collection extends JFrame {
// Reference to the currently selected radio button
private BaseRadioButton selectedButton = null;
public RadioButtons_Collection(){
// Set the title of the window
setTitle("Radio Buttons Collection");
// Make the application close when the window is closed
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Set the initial size of the window (width=500, height=600 pixels)
setSize(550, 600);
// Center the window on the screen
setLocationRelativeTo(null);
// Create the main panel that will hold all components
JPanel mainPanel = new JPanel();
// Set the layout to stack components vertically
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
// Set the background color to dark gray (RGB: 26, 26, 26)
mainPanel.setBackground(new Color(26, 26, 26));
// Add a 20-pixel empty border around all sides of the panel
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// Create the radio buttons
BaseRadioButton neonRadio = new NeonRadioButton();
BaseRadioButton gradientRadio = new GradientRadioButton();
BaseRadioButton minimalRadio = new MinimalRadioButton();
BaseRadioButton cyberpunkRadio = new CyberpunkRadioButton();
BaseRadioButton neumorphicRadio = new NeumorphicRadioButton();
// Group them (only one can be selected at a time)
setupRadioGroup(
neonRadio,
gradientRadio,
minimalRadio,
cyberpunkRadio,
neumorphicRadio
);
// Add radio button panels with different styles and vertical spacing between them
mainPanel.add(createRadioButtonPanel("Neon", neonRadio));
mainPanel.add(Box.createVerticalStrut(30));
mainPanel.add(createRadioButtonPanel("Gradient", gradientRadio));
mainPanel.add(Box.createVerticalStrut(30));
mainPanel.add(createRadioButtonPanel("Minimal", minimalRadio));
mainPanel.add(Box.createVerticalStrut(30));
mainPanel.add(createRadioButtonPanel("Cyberpunk", cyberpunkRadio));
mainPanel.add(Box.createVerticalStrut(30));
mainPanel.add(createRadioButtonPanel("Neumorphic", neumorphicRadio));
// Add the main panel to the JFrame window
add(mainPanel);
}
// Method to set up the radio button group behavior
private void setupRadioGroup(BaseRadioButton... radioButtons) {
for (BaseRadioButton radio : radioButtons) {
radio.addActionListener(e -> {
// Deselect the previous button
if (selectedButton != null && selectedButton != radio) {
selectedButton.setSelected(false);
}
// Update the selected button reference
selectedButton = radio.isSelected() ? radio : null;
});
}
}
// Method that creates a panel containing a label, radio button, and status label
private JPanel createRadioButtonPanel(String label, JRadioButton radioButton) {
// Create a new panel to hold the components
JPanel panel = new JPanel();
// Use FlowLayout to arrange components horizontally with 20-pixel horizontal gaps
panel.setLayout(new FlowLayout(FlowLayout.CENTER, 20, 0));
// Set the panel's background color to dark gray
panel.setBackground(new Color(26, 26, 26));
// Create a label with the specified text
JLabel nameLabel = new JLabel(label);
// Set the label's text color to white
nameLabel.setForeground(Color.WHITE);
// Set the font to Segoe UI, normal style, 24-point size
nameLabel.setFont(new Font("Segoe UI", Font.PLAIN, 24));
// Set a fixed size for the label
nameLabel.setPreferredSize(new Dimension(140, 30));
// Create a label that will show the radio button state ("Selected" or "Unselected")
JLabel statusLabel = new JLabel("Unselected");
// Set the status label's text color to white
statusLabel.setForeground(Color.WHITE);
// Set the font to Segoe UI, normal style, 20-point size
statusLabel.setFont(new Font("Segoe UI", Font.PLAIN, 20));
// Set a fixed size for the status label
statusLabel.setPreferredSize(new Dimension(120, 30));
// Check if the radio button is an instance of our custom BaseRadioButton class
if (radioButton instanceof BaseRadioButton) {
// Cast the radio button to BaseRadioButton to access its specific methods
BaseRadioButton baseRadioButton = (BaseRadioButton) radioButton;
// Set a callback function that updates the status label when the radio button state changes
baseRadioButton.setStatusUpdateCallback(isSelected -> {
// Update the text to show "Selected" or "Unselected"
statusLabel.setText(isSelected ? "Selected" : "Unselected");
// Change the text color to green when selected, white when unselected
statusLabel.setForeground(isSelected ? new Color(0, 255, 136) : Color.WHITE);
});
}
// Add the components to the panel
panel.add(nameLabel);
panel.add(radioButton);
panel.add(statusLabel);
return panel;
}
// Abstract base class for all custom radio buttons
private abstract class BaseRadioButton extends JRadioButton {
// Flag to track if the mouse is hovering over the radio button
protected boolean hover = false;
// Value between 0.0 (unselected) and 1.0 (selected) for animation progress
protected float animationProgress = 0.0f;
// Timer object to handle the animation
protected Timer animationTimer;
// Duration of the animation in milliseconds
protected final int ANIMATION_DURATION = 200;
// Number of steps in the animation
protected final int ANIMATION_STEPS = 20;
// Callback function that will be called when the radio button state changes
protected CustomRunnable statusUpdateCallback;
// Constructor for the base radio button
public BaseRadioButton() {
// Set the size of the radio button
setPreferredSize(new Dimension(70, 70));
// Remove the default styling
setBorderPainted(false);
setFocusPainted(false);
setContentAreaFilled(false);
// Add a listener to detect when the mouse enters/exits the radio button
addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
hover = true;
repaint();
}
@Override
public void mouseExited(MouseEvent e) {
hover = false;
repaint();
}
});
// Add a listener to detect when the radio button is clicked
addItemListener(e -> {
startAnimation(e.getStateChange() == ItemEvent.SELECTED);
});
}
// Method to set the callback function that updates the status label
public void setStatusUpdateCallback(CustomRunnable callback) {
this.statusUpdateCallback = callback;
}
// Method to start the animation when the radio button state changes
protected void startAnimation(boolean targetState) {
// Stop any existing animation
if (animationTimer != null && animationTimer.isRunning()) {
animationTimer.stop();
}
// Calculate animation parameters
final float targetProgress = targetState ? 1.0f : 0.0f;
final float startProgress = animationProgress;
final float progressStep = (targetProgress - startProgress) / ANIMATION_STEPS;
final int delay = ANIMATION_DURATION / ANIMATION_STEPS;
// Create a new timer for the animation
animationTimer = new Timer(delay, null);
animationTimer.addActionListener(new ActionListener() {
private int currentStep = 0;
@Override
public void actionPerformed(ActionEvent e) {
currentStep++;
if (currentStep <= ANIMATION_STEPS) {
animationProgress = startProgress + (progressStep * currentStep);
repaint();
} else {
animationProgress = targetProgress;
animationTimer.stop();
if (statusUpdateCallback != null) {
statusUpdateCallback.run(targetState);
}
}
}
});
// Start the animation timer
animationTimer.start();
}
// Interface for the status update callback
public interface CustomRunnable {
void run(boolean isSelected);
}
// Method to blend between two colors based on a ratio (0.0 to 1.0)
protected Color interpolateColor(Color c1, Color c2, float ratio) {
int red = Math.round(c1.getRed() * (1 - ratio) + c2.getRed() * ratio);
int green = Math.round(c1.getGreen() * (1 - ratio) + c2.getGreen() * ratio);
int blue = Math.round(c1.getBlue() * (1 - ratio) + c2.getBlue() * ratio);
return new Color(red, green, blue);
}
}
// Neon-style radio button implementation
private class NeonRadioButton extends BaseRadioButton {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
int size = Math.min(width, height) - 10;
int x = (width - size) / 2;
int y = (height - size) / 2;
// Define colors for unselected and selected states
Color bgOffColor = new Color(51, 51, 51);
Color bgOnColor = new Color(0, 255, 136);
Color currentBgColor = interpolateColor(bgOffColor, bgOnColor, animationProgress);
// Draw the outer circle
g2.setColor(currentBgColor);
g2.fill(new Ellipse2D.Float(x, y, size, size));
// Draw inner dot with animation
if (animationProgress > 0.0f) {
g2.setColor(Color.WHITE);
// Calculate the size of the inner dot based on animation progress
float dotSize = size * 0.5f * animationProgress;
float dotX = x + (size - dotSize) / 2;
float dotY = y + (size - dotSize) / 2;
g2.fill(new Ellipse2D.Float(dotX, dotY, dotSize, dotSize));
}
// Add glow effect that increases with animation progress
int alpha = Math.round(50 * animationProgress);
if (alpha > 0) {
g2.setColor(new Color(0, 255, 136, alpha));
g2.setStroke(new BasicStroke(4));
g2.draw(new Ellipse2D.Float(x, y, size, size));
}
g2.dispose();
}
}
// Gradient-style radio button implementation
private class GradientRadioButton extends BaseRadioButton {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
int size = Math.min(width, height) - 10;
int x = (width - size) / 2;
int y = (height - size) / 2;
// Define colors for unselected and selected states
Color offStartColor = new Color(255, 107, 107);
Color offEndColor = new Color(254, 202, 87);
Color onStartColor = new Color(72, 52, 212);
Color onEndColor = new Color(104, 109, 224);
// Calculate the current colors based on animation progress
Color currentStartColor = interpolateColor(offStartColor, onStartColor, animationProgress);
Color currentEndColor = interpolateColor(offEndColor, onEndColor, animationProgress);
// Create a gradient and apply it
GradientPaint gradient = new GradientPaint(
x, y, currentStartColor,
x + size, y + size, currentEndColor
);
g2.setPaint(gradient);
g2.fill(new Ellipse2D.Float(x, y, size, size));
// Draw inner dot with animation
if (animationProgress > 0.0f) {
g2.setColor(Color.WHITE);
// Calculate inner dot size (scales with animation)
float dotScale = 0.4f * animationProgress;
int dotSize = Math.round(size * dotScale);
int dotX = x + (size - dotSize) / 2;
int dotY = y + (size - dotSize) / 2;
// Add a soft highlight
float highlightSize = size * 0.6f;
float highlightX = x + (size - highlightSize) / 2;
float highlightY = y + (size - highlightSize) / 2;
RadialGradientPaint highlight = new RadialGradientPaint(
x + size/2, y + size/2, size/2,
new float[] {0.0f, 0.7f, 1.0f},
new Color[] {
new Color(255, 255, 255, 100),
new Color(255, 255, 255, 50),
new Color(255, 255, 255, 0)
}
);
g2.setPaint(highlight);
g2.fill(new Ellipse2D.Float(highlightX, highlightY, highlightSize, highlightSize));
// Draw the inner dot
g2.setColor(Color.WHITE);
g2.fill(new Ellipse2D.Float(dotX, dotY, dotSize, dotSize));
}
g2.dispose();
}
}
// Minimal-style radio button implementation
private class MinimalRadioButton extends BaseRadioButton {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
int size = Math.min(width, height) - 10;
int x = (width - size) / 2;
int y = (height - size) / 2;
// Define colors for unselected and selected states
Color offColor = Color.WHITE;
Color onColor = new Color(0, 255, 136);
Color currentColor = interpolateColor(offColor, onColor, animationProgress);
// Draw the border of the radio button
g2.setColor(currentColor);
g2.setStroke(new BasicStroke(2.5f));
g2.draw(new Ellipse2D.Float(x, y, size, size));
// Draw the inner dot with animation
if (animationProgress > 0.0f) {
// Calculate center and sizes
int centerX = x + size / 2;
int centerY = y + size / 2;
// Calculate inner dot size (scales with animation)
float dotScale = 0.5f * animationProgress;
int dotSize = Math.round(size * dotScale);
int dotX = centerX - dotSize / 2;
int dotY = centerY - dotSize / 2;
// Draw dot
g2.setColor(currentColor);
g2.fill(new Ellipse2D.Float(dotX, dotY, dotSize, dotSize));
}
g2.dispose();
}
}
// Cyberpunk-style radio button implementation
private class CyberpunkRadioButton extends BaseRadioButton {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
int size = Math.min(width, height) - 10;
int x = (width - size) / 2;
int y = (height - size) / 2;
// Define center point
int centerX = x + size/2;
int centerY = y + size/2;
// Draw the dark background circle
g2.setColor(new Color(15, 15, 22));
g2.fill(new Ellipse2D.Float(x, y, size, size));
// Define colors for the cyberpunk theme
Color magenta = new Color(255, 0, 139);
Color cyan = new Color(0, 255, 255);
Color yellow = new Color(255, 211, 0);
// Draw tech pattern inside (circuitry look)
g2.setColor(new Color(30, 30, 40));
g2.setStroke(new BasicStroke(1f));
// Draw circular rings
g2.drawOval(x + size/4, y + size/4, size/2, size/2);
g2.drawOval(x + size/8, y + size/8, size*3/4, size*3/4);
// Draw crosshair lines
g2.drawLine(centerX, y + 5, centerX, y + size - 5);
g2.drawLine(x + 5, centerY, x + size - 5, centerY);
// Add circuit node points at intersections
g2.setColor(new Color(50, 50, 70));
int nodeSize = 3;
// Center node
g2.fillRect(centerX - nodeSize/2, centerY - nodeSize/2, nodeSize, nodeSize);
// Nodes at the four directions
g2.fillRect(centerX - nodeSize/2, y + size/4 - nodeSize/2, nodeSize, nodeSize);
g2.fillRect(centerX - nodeSize/2, y + size*3/4 - nodeSize/2, nodeSize, nodeSize);
g2.fillRect(x + size/4 - nodeSize/2, centerY - nodeSize/2, nodeSize, nodeSize);
g2.fillRect(x + size*3/4 - nodeSize/2, centerY - nodeSize/2, nodeSize, nodeSize);
// Active circuit effect when selected or hovering
if (animationProgress > 0.0f || hover) {
float alpha = hover ? Math.max(0.3f, animationProgress) : animationProgress;
// Draw glowing circuit paths
g2.setStroke(new BasicStroke(1.5f));
g2.setColor(new Color(cyan.getRed(), cyan.getGreen(), cyan.getBlue(), (int)(180 * alpha)));
// Glowing circular rings
g2.drawOval(x + size/4, y + size/4, size/2, size/2);
g2.drawOval(x + size/8, y + size/8, size*3/4, size*3/4);
// Glowing crosshair
g2.drawLine(centerX, y + 5, centerX, y + size - 5);
g2.drawLine(x + 5, centerY, x + size - 5, centerY);
// Create a subtle inner glow using gradient paint
if (animationProgress > 0) {
Color innerGlowStart = new Color(magenta.getRed(),
magenta.getGreen(),
magenta.getBlue(),
(int)(40 * animationProgress)
);
Color innerGlowEnd = new Color(cyan.getRed(),
cyan.getGreen(),
cyan.getBlue(),
(int)(40 * animationProgress)
);
RadialGradientPaint glowGradient = new RadialGradientPaint(
centerX, centerY, size/2,
new float[] {0.0f, 0.8f},
new Color[] {innerGlowStart, innerGlowEnd}
);
g2.setPaint(glowGradient);
g2.fill(new Ellipse2D.Float(x + 2, y + 2, size - 4, size - 4));
}
}
// Draw border
Color borderColor1, borderColor2;
if (animationProgress > 0.0f) {
borderColor1 = magenta;
borderColor2 = cyan;
} else {
borderColor1 = new Color(70, 70, 90);
borderColor2 = new Color(120, 120, 140);
}
// Create two-tone border effect with gradient
RadialGradientPaint gradientBorder = new RadialGradientPaint(
centerX, centerY, size/2,
new float[] {0.7f, 1.0f},
new Color[] {borderColor1, borderColor2}
);
g2.setPaint(gradientBorder);
g2.setStroke(new BasicStroke(2.5f));
g2.draw(new Ellipse2D.Float(x, y, size, size));
// Adding cyberpunkish yellow corner accents
int accentSize = 5;
g2.setColor(yellow);
g2.setStroke(new BasicStroke(1.5f));
// Top accent
g2.drawLine(centerX - accentSize, y, centerX + accentSize, y);
// Bottom accent
g2.drawLine(centerX - accentSize, y + size, centerX + accentSize, y + size);
// Left accent
g2.drawLine(x, centerY - accentSize, x, centerY + accentSize);
// Right accent
g2.drawLine(x + size, centerY - accentSize, x + size, centerY + accentSize);
// Draw the inner dot for selected state
if (animationProgress > 0.0f) {
// Create a random seed for consistent yet randomized glitch effect
Random rand = new Random(System.currentTimeMillis() / 1000);
int glitchFactor = 2;
// Inner dot size calculation
float dotSize = size * 0.4f * animationProgress;
float dotX = centerX - dotSize/2;
float dotY = centerY - dotSize/2;
// Draw glitchy inner dots with offset shadows
// Cyan dot shadow
g2.setColor(new Color(cyan.getRed(),
cyan.getGreen(), cyan.getBlue(), (int)(200 * animationProgress)));
g2.fill(new Ellipse2D.Float(
dotX + rand.nextInt(glitchFactor) - glitchFactor/2,
dotY + rand.nextInt(glitchFactor) - glitchFactor/2,
dotSize, dotSize
));
// Magenta dot shadow
g2.setColor(new Color(magenta.getRed(),
magenta.getGreen(), magenta.getBlue(), (int)(150 * animationProgress)));
g2.fill(new Ellipse2D.Float(
(dotX - 2) + rand.nextInt(glitchFactor) - glitchFactor/2,
(dotY + 2) + rand.nextInt(glitchFactor) - glitchFactor/2,
dotSize, dotSize
));
// Main white inner dot
g2.setColor(new Color(255, 255, 255, (int)(255 * animationProgress)));
g2.fill(new Ellipse2D.Float(dotX, dotY, dotSize, dotSize));
// Add scanline effect
g2.setColor(new Color(255, 255, 255, 20));
g2.setStroke(new BasicStroke(1f));
for (int i = 0; i < size; i += 4) {
g2.drawLine(x, y + i, x + size, y + i);
}
}
g2.dispose();
}
}
// Neumorphic-style radio button implementation
private class NeumorphicRadioButton extends BaseRadioButton {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
int size = Math.min(width, height) - 10;
int x = (width - size) / 2;
int y = (height - size) / 2;
// Define the colors for the neumorphic effect
Color baseColor = new Color(26, 26, 26);
Color lightShadow = new Color(40, 40, 40);
Color darkShadow = new Color(10, 10, 10);
// Draw the base shape (slightly inset circle)
g2.setColor(baseColor);
g2.fill(new Ellipse2D.Float(x, y, size, size));
// Draw shadows to create the neumorphic effect
// Light shadow on top-left
g2.setColor(lightShadow);
g2.setStroke(new BasicStroke(2f));
g2.draw(new Arc2D.Float(x, y, size, size, 45, 180, Arc2D.OPEN));
// Dark shadow on bottom-right
g2.setColor(darkShadow);
g2.setStroke(new BasicStroke(2f));
g2.draw(new Arc2D.Float(x, y, size, size, 225, 180, Arc2D.OPEN));
// Draw the inner area
g2.setColor(new Color(22, 22, 22));
g2.fill(new Ellipse2D.Float(x + 4, y + 4, size - 8, size - 8));
// Colors for selected state
Color onStartColor = new Color(0, 255, 136);
Color onEndColor = new Color(0, 204, 106);
// Draw inner dot when selected
if (animationProgress > 0.0f) {
// Apply a soft glow based on selected state
Color glowColor = interpolateColor(baseColor, onStartColor, animationProgress * 0.5f);
g2.setColor(glowColor);
g2.fill(new Ellipse2D.Float(x + 6, y + 6, size - 12, size - 12));
// Draw the inner dot
float dotScale = 0.4f * animationProgress;
// Ensure dotSize is at least 1 pixel to avoid zero radius
int dotSize = Math.max(1, Math.round(size * dotScale));
int dotX = x + (size - dotSize) / 2;
int dotY = y + (size - dotSize) / 2;
// Create gradient for inner dot
// Ensure radius is at least 0.5f to avoid "Radius must be greater than zero" exception
float gradientRadius = Math.max(0.5f, dotSize / 2f);
RadialGradientPaint dotGradient = new RadialGradientPaint(
x + size/2, y + size/2, gradientRadius,
new float[] {0.0f, 1.0f},
new Color[] {
interpolateColor(baseColor, onStartColor, animationProgress),
interpolateColor(baseColor, onEndColor, animationProgress)
}
);
g2.setPaint(dotGradient);
g2.fill(new Ellipse2D.Float(dotX, dotY, dotSize, dotSize));
}
g2.dispose();
}
}
// Main method
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
// Create and display the application
new RadioButtons_Collection().setVisible(true);
});
}
}