How to Create Custom Radio Buttons with Animations in Java Netbeans
In this Java Tutorial we will see How To Create a collection of five unique custom radio buttons using Java Swing, each with its own distinctive style and smooth animations In Java Using Netbeans.
Project Overview
This project demonstrates Java Swing techniques to create five radio buttons with different styles: Neon, Gradient, Minimal, Cyberpunk, Neumorphic.
What We Are Gonna Use In This Project:
- Java Programming Language.- NetBeans Editor.
Project Source Code:
/**
*
* @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);
});
}
}
The Final Result:
More Java Projects:
Download Projects Source Code


