How to Create Interactive Charts with Animation Effects in Java Netbeans
In this Java Tutorial we will see How To Create an interactive chart application using Java Swing that can display your data as bar charts, line charts, or scatter plots with animations and hover effects In Java Using Netbeans.
FEATURES:
- Three chart types: Bar, Line, and Scatter
- Smooth transition animations
- Interactive hover tooltips
- Modern dark theme design
- Professional gradient effects
- Responsive layout
What We Are Gonna Use In This Project:
- Java Programming Language.- NetBeans Editor.
Project Source Code:
/**
*
* @author 1BestCsharp
*/
/**
* Interactive Chart Application
* This class creates a window displaying an interactive chart that can switch between
* bar chart, line chart, and scatter plot visualizations.
*/
public class InteractiveChart extends JFrame {
// The panel that contains the actual chart visualization
private ChartPanel chartPanel;
// The panel containing buttons to switch between chart types
private JPanel controlPanel;
// Buttons for selecting different chart types
private JButton barButton;
private JButton lineButton;
private JButton scatterButton;
public InteractiveChart(){
// Set up the window title and behavior
setTitle("Interactive Chart");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Makes the application close when the X button is clicked
setLayout(new BorderLayout()); // Use BorderLayout to position components
// Create sample data for the chart
List<DataPoint> data = new ArrayList<>();
data.add(new DataPoint("A", 30, "alpha"));
data.add(new DataPoint("B", 50, "beta"));
data.add(new DataPoint("C", 80, "gamma"));
data.add(new DataPoint("D", 40, "delta"));
data.add(new DataPoint("E", 60, "epsilon"));
// Create the chart panel with our data and add it to the center of the window
chartPanel = new ChartPanel(data);
add(chartPanel, BorderLayout.CENTER);
// Create the control panel with chart type buttons and add it to the bottom of the window
createControlPanel();
add(controlPanel, BorderLayout.SOUTH);
// Set window size and center it on the screen
setSize(650, 500);
setLocationRelativeTo(null); // Centers the window on the screen
}
/**
* Creates the bottom panel with buttons for selecting chart types
*/
private void createControlPanel() {
// Create a panel with a dark background
controlPanel = new JPanel();
controlPanel.setBackground(new Color(24, 28, 43)); // Set dark blue background color
controlPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); // Add padding
controlPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 15, 0)); // Center buttons with spacing
// Create the three chart type buttons
barButton = createButton("Bar Chart");
lineButton = createButton("Line Chart");
scatterButton = createButton("Scatter Plot");
// Set the bar button as active by default
setActiveButton(barButton);
// Add action listeners to each button to change the chart type when clicked
barButton.addActionListener(e -> {
chartPanel.setChartType(ChartType.BAR); // Change chart to bar type
setActiveButton(barButton); // Update button appearances
});
lineButton.addActionListener(e -> {
chartPanel.setChartType(ChartType.LINE); // Change chart to line type
setActiveButton(lineButton); // Update button appearances
});
scatterButton.addActionListener(e -> {
chartPanel.setChartType(ChartType.SCATTER); // Change chart to scatter type
setActiveButton(scatterButton); // Update button appearances
});
// Add buttons to the control panel
controlPanel.add(barButton);
controlPanel.add(lineButton);
controlPanel.add(scatterButton);
}
/**
* Creates a styled button with consistent appearance
* @param text The text to display on the button
* @return A configured JButton instance
*/
private JButton createButton(String text) {
JButton button = new JButton(text);
// Set button font and appearance
button.setFont(new Font("Segoe UI", Font.BOLD, 13));
button.setCursor(new Cursor(Cursor.HAND_CURSOR)); // Changes cursor to hand when hovering
button.setFocusPainted(false); // Removes the focus outline
// Set button colors
button.setBackground(new Color(38, 41, 56)); // Dark background
button.setForeground(new Color(200, 205, 225)); // Light text color
// Create borders with padding
button.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(48, 51, 66), 1), // Border
BorderFactory.createEmptyBorder(8, 16, 8, 16) // Padding inside button
));
// Add hover effect using mouse listeners
button.addMouseListener(new MouseAdapter() {
// When mouse enters (hovers over) the button
@Override
public void mouseEntered(MouseEvent e) {
// Only change color if it's not the active button
if (button.getForeground().getRGB() != new Color(255, 158, 100).getRGB()) {
button.setBackground(new Color(48, 151, 66)); // Lighter green background on hover
}
}
// When mouse exits the button
@Override
public void mouseExited(MouseEvent e) {
// Only change color if it's not the active button
if (button.getForeground().getRGB() != new Color(255, 158, 100).getRGB()) {
button.setBackground(new Color(38, 41, 56)); // Return to normal background
}
}
});
return button;
}
/**
* Updates the appearance of buttons to show which chart type is currently active
* @param activeButton The button to set as active
*/
private void setActiveButton(JButton activeButton) {
// Define colors for inactive buttons
Color inactiveBackground = new Color(38, 41, 56);
Color inactiveForeground = new Color(200, 205, 225);
Color inactiveBorder = new Color(48, 51, 66);
// Reset all buttons to inactive state
for (JButton button : new JButton[]{barButton, lineButton, scatterButton}) {
button.setBackground(inactiveBackground);
button.setForeground(inactiveForeground);
button.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(inactiveBorder, 1),
BorderFactory.createEmptyBorder(8, 16, 8, 16)
));
}
// Set the active button's style with orange highlight
activeButton.setBackground(new Color(30, 33, 45));
activeButton.setForeground(new Color(255, 158, 100)); // Orange text
activeButton.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(255, 158, 100), 2), // Orange border
BorderFactory.createEmptyBorder(7, 15, 7, 15)
));
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(() -> {
InteractiveChart chart = new InteractiveChart();
chart.setVisible(true); // Make the window visible
});
}
/**
* Enum defining the available chart types
*/
enum ChartType {
BAR, LINE, SCATTER
}
/**
* Class representing a single data point on the chart
*/
static class DataPoint {
String label; // The label shown on the x-axis
int value; // The numeric value represented by this point
String category; // A category grouping for this data point
// Constructor to initialize the data point
public DataPoint(String label, int value, String category) {
this.label = label;
this.value = value;
this.category = category;
}
}
/**
* Custom tooltip panel that appears when hovering over data points
*/
class ChartTooltip extends JPanel {
private String label;
private String category;
private int value;
/**
* Creates a tooltip with data point information
*/
public ChartTooltip(String label, String category, int value) {
this.label = label;
this.category = category;
this.value = value;
// Style the tooltip
setBackground(new Color(36, 40, 59)); // Dark background
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(255, 158, 100), 2, false), // Orange border
BorderFactory.createEmptyBorder(8, 12, 8, 12) // Padding
));
}
/**
* Custom painting of the tooltip content
* This method is automatically called when the tooltip needs to be drawn
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g; // Cast to Graphics2D for more advanced drawing
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Smooth edges
// Draw the label and category with light gray color
g2.setColor(new Color(207, 201, 194));
g2.setFont(new Font("Segoe UI", Font.BOLD, 14));
g2.drawString(label + " (" + category + ")", 10, 20);
// Draw the value with orange color
g2.setColor(new Color(255, 158, 100));
g2.setFont(new Font("Segoe UI", Font.BOLD, 18));
g2.drawString(String.valueOf(value), 10, 45);
}
/**
* Define the preferred size of the tooltip
*/
@Override
public Dimension getPreferredSize() {
return new Dimension(120, 60);
}
}
/**
* The main chart visualization panel
* This is where the actual chart drawing happens
*/
class ChartPanel extends JPanel {
private List<DataPoint> data; // The data to display
private ChartType chartType = ChartType.BAR; // The current chart type
private ChartTooltip tooltip; // Tooltip to show on hover
// Animation properties
private Timer animationTimer; // Timer that controls animation speed
private float animationProgress = 0; // Current animation progress (0.0 to 1.0)
private float animationIncrement = 0.05f; // How much to increase progress each step
// Chart dimensions and margins
private int leftMargin = 60; // Space on the left for y-axis labels
private int rightMargin = 40; // Space on the right
private int topMargin = 40; // Space at the top for title
private int bottomMargin = 60; // Space at the bottom for x-axis labels
// Interactive elements
private int hoverIndex = -1; // Index of data point being hovered (-1 means none)
/**
* Creates a chart panel with the given data
*/
public ChartPanel(List<DataPoint> data) {
this.data = data;
setBackground(new Color(26, 27, 38)); // Dark background
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); // Add padding
// Create tooltip (initially invisible)
tooltip = new ChartTooltip("", "", 0);
tooltip.setVisible(false);
setLayout(null); // Use absolute positioning for tooltip
add(tooltip);
// Add mouse listeners for interaction
// This detects when the mouse moves over the chart
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
checkHover(e.getX(), e.getY()); // Check if mouse is over a data point
}
});
// This detects when the mouse exits the chart area
addMouseListener(new MouseAdapter() {
@Override
public void mouseExited(MouseEvent e) {
hoverIndex = -1; // No point is being hovered
tooltip.setVisible(false); // Hide tooltip
repaint(); // Redraw the chart
}
});
// Start with animation
startAnimation();
}
/**
* Changes the chart type and restarts the animation
*/
public void setChartType(ChartType chartType) {
this.chartType = chartType;
animationProgress = 0; // Reset animation
startAnimation(); // Start new animation
}
/**
* Starts the animation for chart transitions
*/
private void startAnimation() {
// Stop existing animation if running
if (animationTimer != null && animationTimer.isRunning()) {
animationTimer.stop();
}
// Reset and start new animation
animationProgress = 0;
// Create a timer that fires multiple times to create animation
animationTimer = new Timer(20, e -> { // Fire every 20 milliseconds
animationProgress += animationIncrement; // Increase progress
if (animationProgress >= 1) {
animationProgress = 1; // Cap at 100%
animationTimer.stop(); // Stop animation
}
repaint(); // Redraw the chart with new progress
});
animationTimer.start(); // Start the timer
}
/**
* Checks if the mouse is hovering over a data point
* Updates the tooltip position and visibility
*/
private void checkHover(int mouseX, int mouseY) {
int chartWidth = getWidth() - leftMargin - rightMargin;
int chartHeight = getHeight() - topMargin - bottomMargin;
int barWidth = chartWidth / data.size();
hoverIndex = -1; // Start with no hover
// Check each data point to see if mouse is over it
for (int i = 0; i < data.size(); i++) {
DataPoint point = data.get(i);
int x = leftMargin + i * barWidth + barWidth / 2;
int y = topMargin + chartHeight - (int)(chartHeight * point.value / 100.0);
// Different hit testing based on chart type
if (chartType == ChartType.BAR) {
// For bar charts, check if mouse is inside the bar rectangle
Rectangle barRect = new Rectangle(
leftMargin + i * barWidth + 5,
y,
barWidth - 10,
chartHeight - (chartHeight - (int)(chartHeight * point.value / 100.0))
);
if (barRect.contains(mouseX, mouseY)) {
hoverIndex = i;
break;
}
} else{
// For line and scatter, check distance to point
double distance = Math.sqrt(Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2));
if (distance < 15) { // Within 15 pixels
hoverIndex = i;
break;
}
}
}
// Update tooltip if hovering over a data point
if (hoverIndex != -1) {
DataPoint point = data.get(hoverIndex);
tooltip.label = point.label;
tooltip.category = point.category;
tooltip.value = point.value;
int x = leftMargin + hoverIndex * barWidth + barWidth / 2;
int y = topMargin + getHeight() - topMargin - bottomMargin -
(int)((getHeight() - topMargin - bottomMargin) * point.value / 100.0);
// Position tooltip above the data point
tooltip.setBounds(x - 60, y - 70, 120, 60);
tooltip.setVisible(true);
} else {
tooltip.setVisible(false); // Hide tooltip if not hovering over a point
}
repaint(); // Redraw to show highlighting
}
/**
* Main method to paint the chart components
* This is called automatically whenever the chart needs to be redrawn
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g; // Cast to Graphics2D for more advanced drawing
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Smooth edges
int width = getWidth();
int height = getHeight();
int chartWidth = width - leftMargin - rightMargin;
int chartHeight = height - topMargin - bottomMargin;
// Draw title
g2.setColor(new Color(207, 201, 194)); // Light gray color
g2.setFont(new Font("Segoe UI", Font.BOLD, 20));
String title = "Performance Metrics";
int titleWidth = g2.getFontMetrics().stringWidth(title);
g2.drawString(title, (width - titleWidth) / 2, 30); // Center the title
// Draw title underline
g2.setColor(new Color(255, 158, 100)); // Orange color
g2.fillRect((width - 40) / 2, 35, 40, 3);
// Draw axes
g2.setColor(new Color(169, 177, 214, 60)); // Semi-transparent blue-gray
g2.drawLine(leftMargin, topMargin, leftMargin, height - bottomMargin); // Y-axis
g2.drawLine(leftMargin, height - bottomMargin, width - rightMargin, height - bottomMargin); // X-axis
// Draw grid lines and y-axis labels
g2.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
10, new float[]{5, 5}, 0)); // Dashed line
// Draw 5 horizontal grid lines and labels
for (int i = 0; i <= 5; i++) {
int y = topMargin + (chartHeight * i) / 5;
g2.drawLine(leftMargin, y, width - rightMargin, y); // Grid line
// Y-axis label
g2.setColor(new Color(169, 177, 214));
g2.setFont(new Font("Segoe UI", Font.PLAIN, 12));
String label = String.valueOf(100 - i * 20); // 100, 80, 60, 40, 20, 0
g2.drawString(label, leftMargin - 30, y + 5);
g2.setColor(new Color(169, 177, 214, 60)); // Back to transparent for next grid line
}
// Draw x-axis labels (one for each data point)
g2.setFont(new Font("Segoe UI", Font.PLAIN, 12));
g2.setColor(new Color(169, 177, 214));
int barWidth = chartWidth / data.size();
for (int i = 0; i < data.size(); i++) {
String label = data.get(i).label;
int labelWidth = g2.getFontMetrics().stringWidth(label);
g2.drawString(label, leftMargin + i * barWidth + barWidth / 2 - labelWidth / 2,
height - bottomMargin + 25); // Center under each data point
}
// Draw the appropriate chart based on current type
switch (chartType) {
case BAR:
drawBarChart(g2, chartWidth, chartHeight);
break;
case LINE:
drawLineChart(g2, chartWidth, chartHeight);
break;
case SCATTER:
drawScatterChart(g2, chartWidth, chartHeight);
break;
}
}
/**
* Draws a bar chart visualization
*/
private void drawBarChart(Graphics2D g2, int chartWidth, int chartHeight) {
int barWidth = chartWidth / data.size();
// Create gradient paint for bars
GradientPaint gradient = new GradientPaint(
0, topMargin, new Color(255, 122, 147), // Pink at top
0, getHeight() - bottomMargin, new Color(255, 158, 100) // Orange at bottom
);
// Draw each bar
for (int i = 0; i < data.size(); i++) {
DataPoint point = data.get(i);
// Apply animation to bar height
int barHeight = (int)(chartHeight * point.value / 100.0 * animationProgress);
int x = leftMargin + i * barWidth + 5;
int y = getHeight() - bottomMargin - barHeight;
int width = barWidth - 10;
// Use different color for hovered bar
if (i == hoverIndex) {
g2.setColor(new Color(255, 122, 147)); // Pink when hovered
} else {
g2.setPaint(gradient); // Gradient for normal bars
}
// Create rounded rectangle for bar
RoundRectangle2D.Double bar = new RoundRectangle2D.Double(x, y, width, barHeight, 8, 8);
g2.fill(bar);
// Draw value on top of bar if animation is complete
if (animationProgress >= 0.95) {
g2.setColor(new Color(207, 201, 194)); // Light gray text
g2.setFont(new Font("Segoe UI", Font.BOLD, 12));
String value = String.valueOf(point.value);
int stringWidth = g2.getFontMetrics().stringWidth(value);
g2.drawString(value, x + width / 2 - stringWidth / 2, y - 10); // Center above bar
}
}
}
/**
* Draws a line chart visualization
*/
private void drawLineChart(Graphics2D g2, int chartWidth, int chartHeight) {
int barWidth = chartWidth / data.size();
// Set line properties
g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); // Thick line with round caps
g2.setColor(new Color(255, 158, 100)); // Orange line
// Calculate point coordinates
int[] xPoints = new int[data.size()];
int[] yPoints = new int[data.size()];
for (int i = 0; i < data.size(); i++) {
DataPoint point = data.get(i);
xPoints[i] = leftMargin + i * barWidth + barWidth / 2;
// Apply animation to y-values
float valueProgress = point.value * animationProgress;
yPoints[i] = getHeight() - bottomMargin - (int)(chartHeight * valueProgress / 100.0);
}
// Create path for the line
Path2D path = new Path2D.Double();
path.moveTo(xPoints[0], yPoints[0]); // Start at first point
// Draw smooth line between points using quadratic curves
for (int i = 1; i < data.size(); i++) {
// Calculate control point for smooth curve
double controlX = xPoints[i-1] + (xPoints[i] - xPoints[i-1]) / 2.0;
// First curve: from previous point to midpoint
path.quadTo(controlX, yPoints[i-1], controlX, yPoints[i-1] + (yPoints[i] - yPoints[i-1]) / 2.0);
// Second curve: from midpoint to current point
path.quadTo(controlX, yPoints[i], xPoints[i], yPoints[i]);
}
// Draw the path
g2.draw(path);
// Draw points
for (int i = 0; i < data.size(); i++) {
// Only draw points if they've "appeared" in the animation
if (i <= (data.size() - 1) * animationProgress) {
int x = xPoints[i];
int y = yPoints[i];
// Outer circle (stroke)
g2.setColor(new Color(24, 28, 43)); // Dark blue
g2.fillOval(x - 7, y - 7, 14, 14);
// Inner circle (fill) - different color when hovered
if (i == hoverIndex) {
g2.setColor(new Color(255, 122, 147)); // Pink when hovered
} else {
g2.setColor(new Color(255, 158, 100)); // Orange normally
}
g2.fillOval(x - 5, y - 5, 10, 10);
// Draw values if animation complete
if (animationProgress >= 0.95) {
g2.setColor(new Color(207, 201, 194)); // Light gray text
g2.setFont(new Font("Segoe UI", Font.BOLD, 12));
String value = String.valueOf(data.get(i).value);
int stringWidth = g2.getFontMetrics().stringWidth(value);
g2.drawString(value, x - stringWidth / 2, y - 15); // Center above point
}
}
}
}
/**
* Draws a scatter chart visualization
*/
private void drawScatterChart(Graphics2D g2, int chartWidth, int chartHeight) {
int barWidth = chartWidth / data.size();
// Save the original stroke and composite for later restoration
Stroke originalStroke = g2.getStroke();
Composite originalComposite = g2.getComposite();
// Draw connecting lines to axis - more subtle than before
g2.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
10, new float[]{3, 3}, 0)); // Finer dashed line
g2.setColor(new Color(169, 177, 214, 60)); // Very subtle blue-gray
// Create a drop shadow effect for the points
for (int i = 0; i < data.size(); i++) {
// Only draw if this point has "appeared" in the animation
if (i <= (data.size() - 1) * animationProgress) {
DataPoint point = data.get(i);
int x = leftMargin + i * barWidth + barWidth / 2;
// Apply animation to point position
float valueProgress = point.value * animationProgress;
int y = getHeight() - bottomMargin - (int)(chartHeight * valueProgress / 100.0);
// Draw vertical line to x-axis if animation has progressed
if (animationProgress > 0.3) {
// Draw line from x-axis to slightly below the point
g2.drawLine(x, getHeight() - bottomMargin, x, y + 7);
}
}
}
// Reset stroke for the points
g2.setStroke(originalStroke);
// Draw each data point with shadow first, then the actual point
for (int i = 0; i < data.size(); i++) {
// Only draw if this point has "appeared" in the animation
if (i <= (data.size() - 1) * animationProgress) {
DataPoint point = data.get(i);
int x = leftMargin + i * barWidth + barWidth / 2;
// Apply animation to point position
float valueProgress = point.value * animationProgress;
int y = getHeight() - bottomMargin - (int)(chartHeight * valueProgress / 100.0);
// Standard point size - consistent but slightly larger for higher values
int pointSize = 14 + (point.value / 20);
// Draw shadow first (slightly offset and transparent)
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
g2.setColor(new Color(0, 0, 0, 100)); // Transparent black
g2.fillOval(x - pointSize/2 + 3, y - pointSize/2 + 3, pointSize, pointSize);
// Reset composite for main point drawing
g2.setComposite(originalComposite);
// Apply a gradient color based on data point value
if (i == hoverIndex) {
// Special bright highlight color when hovered
g2.setColor(new Color(255, 122, 147)); // Bright pink
// Draw glow effect for hovered point
for (int glow = 3; glow > 0; glow--) {
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f));
g2.fillOval(x - pointSize/2 - glow, y - pointSize/2 - glow,
pointSize + glow*2, pointSize + glow*2);
}
g2.setComposite(originalComposite);
} else {
// Gradient from orange to blue based on value
Color pointColor;
float hue = 0.1f + (0.6f * point.value / 100.0f); // Range from orange (0.1) to blue (0.7)
pointColor = Color.getHSBColor(hue, 0.8f, 0.9f);
g2.setColor(pointColor);
}
// Draw the filled circle
g2.fillOval(x - pointSize/2, y - pointSize/2, pointSize, pointSize);
// Draw border around circle
g2.setColor(new Color(24, 28, 43)); // Dark outline
g2.setStroke(new BasicStroke(1.5f));
g2.drawOval(x - pointSize/2, y - pointSize/2, pointSize, pointSize);
g2.setStroke(originalStroke);
// Draw values if animation is complete or nearly complete
if (animationProgress >= 0.95) {
// Create a small white background for text to improve readability
String value = String.valueOf(point.value);
g2.setFont(new Font("Segoe UI", Font.BOLD, 12));
int stringWidth = g2.getFontMetrics().stringWidth(value);
int stringHeight = g2.getFontMetrics().getHeight();
// Draw text background
g2.setColor(new Color(24, 28, 43, 180)); // Semi-transparent dark background
g2.fillRoundRect(x - stringWidth/2 - 4, y - pointSize/2 - stringHeight - 2,
stringWidth + 8, stringHeight + 2, 6, 6);
// Draw value text
g2.setColor(new Color(255, 255, 255)); // White text
g2.drawString(value, x - stringWidth/2, y - pointSize/2 - 5);
}
}
}
// Restore original settings
g2.setStroke(originalStroke);
g2.setComposite(originalComposite);
}
}
}
The Final Result:
if you want the full source code click on the download button below
disclaimer: you will get the source code, and to make it work in your machine is your responsibility and to debug any error/exception is your responsibility this project is for the students who want to see an example and read the code not to get and run.
More Java Projects:
Download Projects Source Code







