import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.Timer;
import javax.swing.*;
public class SwitchButton extends JComponent {
private String onLabel = "ON";
private String offLabel = "OFF";
private int gap = 10;
private static final int DEFAULT_TRACK_WIDTH = 100;
private static final int KNOB_DIAMETER = 20;
private int trackHeight = 24;
private int duration = 200;
private int animX = -1;
private Animator animator;
private Color trackColor = Color.WHITE;
private Color knobColor = Color.GREEN;
private boolean on = false;
public SwitchButton() {
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (animator == null || animator.timer == null) {
animator = new Animator(duration);
}
}
});
}
@Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
FontMetrics fm = getFontMetrics(getFont());
Insets insets = getInsets();
int w = insets.left + insets.right;
w += onLabelWidth() + offLabelWidth();
w += 2 * gap + DEFAULT_TRACK_WIDTH;
int h = insets.top + insets.bottom +
Math.max(trackHeight, Math.max(
KNOB_DIAMETER, fm.getHeight()));
return new Dimension(w, h);
}
private int onLabelWidth() {
FontMetrics fm = getFontMetrics(getFont());
return fm.stringWidth(onLabel);
}
private int offLabelWidth() {
FontMetrics fm = getFontMetrics(getFont());
return fm.stringWidth(offLabel);
}
private int trackWidth() {
Insets insets = getInsets();
return getWidth() - insets.left - insets.right -
2*gap - onLabelWidth() - offLabelWidth();
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
@Override
protected void paintComponent(Graphics g) {
Insets insets = getInsets();
int lx = insets.left;
int ty = insets.top;
int h = getHeight() - insets.top - insets.bottom - 1;
int w = getWidth() - insets.left - insets.right - 1;
g.setColor(getBackground());
g.fillRect(lx, ty, w, h);
g.setFont(getFont());
FontMetrics fm = g.getFontMetrics();
int onWidth = onLabelWidth();
int offWidth = offLabelWidth();
int cy = h/2;
int x = lx;
g.setColor(getForeground());
g.drawString(offLabel, x, cy + fm.getAscent() / 2);
x += offWidth + gap;
int onX = lx + w - onWidth;
g.drawString(onLabel, onX, cy + fm.getAscent() / 2);
int trackX = x;
int trackWidth = onX - gap - x;
g.setColor(trackColor);
g.fillRect(x, ty, trackWidth, h);
int knobX;
int maxKnobX = trackWidth - KNOB_DIAMETER;
if (animX != -1) {
knobX = Math.min(Math.max(0, animX), maxKnobX);
} else {
knobX = on ? maxKnobX : 0;
}
int knobY = cy - KNOB_DIAMETER/2;
g.setColor(knobColor);
g.fillOval(trackX + knobX, knobY, KNOB_DIAMETER, KNOB_DIAMETER);
}
private void setAnimX(int x) {
animX = x;
repaint();
}
private class Animator {
private Timer timer;
private int direction;
private int max;
private long millis;
private int duration;
public Animator(int duration) {
this.duration = duration;
max = trackWidth();
direction = on ? -1 : 1;
millis = System.currentTimeMillis();
timer = new Timer(15, e -> animate());
timer.start();
}
private void animate() {
int time = (int)(System.currentTimeMillis() - millis);
if (time < duration) {
setAnimX((direction < 0 ? max : 0) + direction * max * time / duration);
} else {
on = !on;
setAnimX(-1);
timer.stop();
timer = null;
}
}
}
public static final void main(String[] args) {
JPanel panel = new JPanel();
panel.add(new SwitchButton());
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}