How to add to this round button metal background in java? - java

How to give this button background to be as jbutton
i want to give this button metal color or grediant color how to make that?
import java.awt.*;
import java.awt.event.*;
RoundButton - a class that produces a lightweight button.
Lightweight components can have "transparent" areas, meaning that
you can see the background of the container behind these areas.
#SuppressWarnings("serial")
public class RoundButton extends Component {
ActionListener actionListener; // Post action events to listeners
String label; // The Button's text
protected boolean pressed = false; // true if the button is detented.
/**
* Constructs a RoundButton with no label.
*/
public RoundButton() {
this("");
}
/**
* Constructs a RoundButton with the specified label.
* #param label the label of the button
*/
public RoundButton(String label) {
this.label = label;
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
/**
* gets the label
* #see setLabel
*/
public String getLabel() {
return label;
}
/**
* sets the label
* #see getLabel
*/
public void setLabel(String label) {
this.label = label;
invalidate();
repaint();
}
/**
* paints the RoundButton
*/
public void paint(Graphics g) {
int s = Math.min(getSize().width - 1, getSize().height - 1);
// paint the interior of the button
if(pressed) {
g.setColor(getBackground().darker().darker());
} else {
g.setColor(getBackground());
}
g.fillArc(0, 0, s, s, 0, 360);
// draw the perimeter of the button
g.setColor(getBackground().darker().darker().darker());
g.drawArc(0, 0, s, s, 0, 360);
// draw the label centered in the button
Font f = getFont();
if(f != null) {
FontMetrics fm = getFontMetrics(getFont());
g.setColor(getForeground());
g.drawString(label,
s/2 - fm.stringWidth(label)/2,
s/2 + fm.getMaxDescent());
}
}
/**
* The preferred size of the button.
*/
public Dimension getPreferredSize() {
Font f = getFont();
if(f != null) {
FontMetrics fm = getFontMetrics(getFont());
int max = Math.max(fm.stringWidth(label) + 40, fm.getHeight() + 40);
return new Dimension(max, max);
} else {
return new Dimension(100, 100);
}
}
/**
* The minimum size of the button.
*/
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
/**
* Adds the specified action listener to receive action events
* from this button.
* #param listener the action listener
*/
public void addActionListener(ActionListener listener) {
actionListener = AWTEventMulticaster.add(actionListener, listener);
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
/**
* Removes the specified action listener so it no longer receives
* action events from this button.
* #param listener the action listener
*/
public void removeActionListener(ActionListener listener) {
actionListener = AWTEventMulticaster.remove(actionListener, listener);
}
/**
* Determine if click was inside round button.
*/
public boolean contains(int x, int y) {
int mx = getSize().width/2;
int my = getSize().height/2;
return (((mx-x)*(mx-x) + (my-y)*(my-y)) <= mx*mx);
}
/**
* Paints the button and distribute an action event to all listeners.
*/
public void processMouseEvent(MouseEvent e) {
#SuppressWarnings("unused")
Graphics g;
switch(e.getID()) {
case MouseEvent.MOUSE_PRESSED:
// render myself inverted....
pressed = true;
// Repaint might flicker a bit. To avoid this, you can use
// double buffering (see the Gauge example).
repaint();
break;
case MouseEvent.MOUSE_RELEASED:
if(actionListener != null) {
actionListener.actionPerformed(new ActionEvent(
this, ActionEvent.ACTION_PERFORMED, label));
}
// render myself normal again
if(pressed == true) {
pressed = false;
// Repaint might flicker a bit. To avoid this, you can use
// double buffering (see the Gauge example).
repaint();
}
break;
case MouseEvent.MOUSE_ENTERED:
break;
case MouseEvent.MOUSE_EXITED:
if(pressed == true) {
// Cancel! Don't send action event.
pressed = false;
// Repaint might flicker a bit. To avoid this, you can use
// double buffering (see the Gauge example).
repaint();
// Note: for a more complete button implementation,
// you wouldn't want to cancel at this point, but
// rather detect when the mouse re-entered, and
// re-highlight the button. There are a few state
// issues that that you need to handle, which we leave
// this an an excercise for the reader (I always
// wanted to say that!)
}
break;
}
super.processMouseEvent(e);
}
}

java.awt.Component does not support opacity/transparency, only Swing components do.
You should take the time to read through
Performing Custom Painting
Graphics2D
Painting in AWT and Swing
Writing Event Listeners
You have a number issues to start with...
You should extend from a light weight (Swing) component, as they support transparency
There is no need to override processMouseEvent, you should use a MouseListener instead
Your contains method should take into consideration the "shape" of the button
You should use the inbuilt event management API when adding new listeners
You should favor paintComponent over paint when painting lightweight components
YOU MUST CALL super.paintXxx from any paint method you override, there is only a very small number of times when you wouldn't, and then you become responsible for taking over there work
You need to mark the component as transparent by call setOpaque(false)
Unpressed/Pressed
public class TestRoundButton {
public static void main(String[] args) {
new TestRoundButton();
}
public TestRoundButton() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setBackground(Color.RED);
frame.setLayout(new GridBagLayout());
frame.add(new RoundButton(":)"));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class RoundButton extends JPanel {
ActionListener actionListener; // Post action events to listeners
String label; // The Button's text
protected boolean pressed = false; // true if the button is detented.
private MouseListener listener;
/**
* Constructs a RoundButton with no label.
*/
public RoundButton() {
this("");
}
#Override
public void addNotify() {
super.addNotify();
if (listener == null) {
listener = new MouseHandler();
addMouseListener(listener);
}
}
#Override
public void removeNotify() {
removeMouseListener(listener);
super.removeNotify();
}
/**
* Constructs a RoundButton with the specified label.
*
* #param label the label of the button
*/
public RoundButton(String label) {
this.label = label;
//...
setOpaque(false);
// Use a mouse listener instead
// enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
/**
* gets the label
*
* #see setLabel
*/
public String getLabel() {
return label;
}
/**
* sets the label
*
* #see getLabel
*/
public void setLabel(String label) {
this.label = label;
invalidate();
repaint();
}
/**
* paints the RoundButton
*/
// Prefer paintComponent over paint...
#Override
protected void paintComponent(Graphics g) {
// YOU MUST CALL super.paintXxx THERE IS NO EXCUSE NOT TO, EVER!!
super.paintComponent(g);
int s = Math.min(getSize().width - 1, getSize().height - 1);
Graphics2D g2d = (Graphics2D) g.create();
// paint the interior of the button
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Color highlight = getBackground();
if (pressed) {
highlight = highlight.darker();
}
Color darklight = highlight.darker();
LinearGradientPaint lgp = new LinearGradientPaint(
new Point(0, 0),
new Point(0, s),
new float[]{0, 1f},
new Color[]{highlight, darklight});
Ellipse2D shape = new Ellipse2D.Float(0, 0, s, s);
g2d.setPaint(lgp);
g2d.fill(shape);
// draw the perimeter of the button
g2d.setColor(getBackground().darker().darker().darker());
g2d.draw(shape);
// draw the label centered in the button
Font f = getFont();
if (f != null) {
FontMetrics fm = getFontMetrics(getFont());
g2d.setColor(getForeground());
g2d.drawString(label,
s / 2 - fm.stringWidth(label) / 2,
s / 2 + fm.getMaxDescent());
}
g2d.dispose();
}
/**
* The preferred size of the button.
*/
public Dimension getPreferredSize() {
Font f = getFont();
if (f != null) {
FontMetrics fm = getFontMetrics(getFont());
int max = Math.max(fm.stringWidth(label) + 40, fm.getHeight() + 40);
return new Dimension(max, max);
} else {
return new Dimension(100, 100);
}
}
/**
* The minimum size of the button.
*/
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
/**
* Adds the specified action listener to receive action events from this
* button.
*
* #param listener the action listener
*/
public void addActionListener(ActionListener listener) {
// actionListener = AWTEventMulticaster.add(actionListener, listener);
// enableEvents(AWTEvent.MOUSE_EVENT_MASK);
listenerList.add(ActionListener.class, listener);
}
/**
* Removes the specified action listener so it no longer receives action
* events from this button.
*
* #param listener the action listener
*/
public void removeActionListener(ActionListener listener) {
// actionListener = AWTEventMulticaster.remove(actionListener, listener);
listenerList.add(ActionListener.class, listener);
}
/**
* Determine if click was inside round button.
*/
public boolean contains(int x, int y) {
// This needs to work more on the actual painted shape...
int mx = getSize().width / 2;
int my = getSize().height / 2;
return (((mx - x) * (mx - x) + (my - y) * (my - y)) <= mx * mx);
}
/**
* Paints the button and distribute an action event to all listeners.
*/
// public void processMouseEvent(MouseEvent e) {
// #SuppressWarnings("unused")
// Graphics g;
// switch (e.getID()) {
// case MouseEvent.MOUSE_PRESSED:
// // render myself inverted....
// pressed = true;
//
// // Repaint might flicker a bit. To avoid this, you can use
// // double buffering (see the Gauge example).
// repaint();
// break;
// case MouseEvent.MOUSE_RELEASED:
// if (actionListener != null) {
// actionListener.actionPerformed(new ActionEvent(
// this, ActionEvent.ACTION_PERFORMED, label));
// }
// // render myself normal again
// if (pressed == true) {
// pressed = false;
//
// // Repaint might flicker a bit. To avoid this, you can use
// // double buffering (see the Gauge example).
// repaint();
// }
// break;
// case MouseEvent.MOUSE_ENTERED:
//
// break;
// case MouseEvent.MOUSE_EXITED:
// if (pressed == true) {
// // Cancel! Don't send action event.
// pressed = false;
//
// // Repaint might flicker a bit. To avoid this, you can use
// // double buffering (see the Gauge example).
// repaint();
//
// // Note: for a more complete button implementation,
// // you wouldn't want to cancel at this point, but
// // rather detect when the mouse re-entered, and
// // re-highlight the button. There are a few state
// // issues that that you need to handle, which we leave
// // this an an excercise for the reader (I always
// // wanted to say that!)
// }
// break;
// }
// super.processMouseEvent(e);
// }
public class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
pressed = true;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
pressed = false;
repaint();
}
}
}
}
Now, having done all that, you might like to take a serious look at javax.swing.AbstractButton as you base component

Related

Make running (marquee ) of JLabel in Netbeans [duplicate]

How can I implement Marquee effect in Java Swing
Here's an example using javax.swing.Timer.
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
/** #see http://stackoverflow.com/questions/3617326 */
public class MarqueeTest {
private void display() {
JFrame f = new JFrame("MarqueeTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String s = "Tomorrow, and tomorrow, and tomorrow, "
+ "creeps in this petty pace from day to day, "
+ "to the last syllable of recorded time; ... "
+ "It is a tale told by an idiot, full of "
+ "sound and fury signifying nothing.";
MarqueePanel mp = new MarqueePanel(s, 32);
f.add(mp);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
mp.start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MarqueeTest().display();
}
});
}
}
/** Side-scroll n characters of s. */
class MarqueePanel extends JPanel implements ActionListener {
private static final int RATE = 12;
private final Timer timer = new Timer(1000 / RATE, this);
private final JLabel label = new JLabel();
private final String s;
private final int n;
private int index;
public MarqueePanel(String s, int n) {
if (s == null || n < 1) {
throw new IllegalArgumentException("Null string or n < 1");
}
StringBuilder sb = new StringBuilder(n);
for (int i = 0; i < n; i++) {
sb.append(' ');
}
this.s = sb + s + sb;
this.n = n;
label.setFont(new Font("Serif", Font.ITALIC, 36));
label.setText(sb.toString());
this.add(label);
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
#Override
public void actionPerformed(ActionEvent e) {
index++;
if (index > s.length() - n) {
index = 0;
}
label.setText(s.substring(index, index + n));
}
}
I know this is a late answer, but I just saw another question about a marquee that was closed because it was considered a duplicate of this answer.
So I thought I'd add my suggestion which takes a approach different from the other answers suggested here.
The MarqueePanel scrolls components on a panel not just text. So this allows you to take full advantage of any Swing component. A simple marquee can be used by adding a JLabel with text. A fancier marquee might use a JLabel with HTML so you can use different fonts and color for the text. You can even add a second component with an image.
Basic answer is you draw your text / graphic into a bitmap and then implement a component that paints the bitmap offset by some amount. Usually marquees / tickers scroll left so the offset increases which means the bitmap is painted at -offset. Your component runs a timer that fires periodically, incrementing the offset and invalidating itself so it repaints.
Things like wrapping are a little more complex to deal with but fairly straightforward. If the offset exceeds the bitmap width you reset it back to 0. If the offset + component width > bitmap width you paint the remainder of the component starting from the beginning of the bitmap.
The key to a decent ticker is to make the scrolling as smooth and as flicker free as possible. Therefore it may be necessary to consider double buffering the result, first painting the scrolling bit into a bitmap and then rendering that in one go rather than painting straight into the screen.
Here is some code that I threw together to get you started. I normally would take the ActionListener code and put that in some sort of MarqueeController class to keep this logic separate from the panel, but that's a different question about organizing the MVC architecture, and in a simple enough class like this it may not be so important.
There are also various animation libraries that would help you do this, but I don't normally like to include libraries into projects only to solve one problem like this.
public class MarqueePanel extends JPanel {
private JLabel textLabel;
private int panelLocation;
private ActionListener taskPerformer;
private boolean isRunning = false;
public static final int FRAMES_PER_SECOND = 24;
public static final int MOVEMENT_PER_FRAME = 5;
/**
* Class constructor creates a marquee panel.
*/
public MarqueePanel() {
this.setLayout(null);
this.textLabel = new JLabel("Scrolling Text Here");
this.panelLocation = 0;
this.taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
MarqueePanel.this.tickAnimation();
}
}
}
/**
* Starts the animation.
*/
public void start() {
this.isRunning = true;
this.tickAnimation();
}
/**
* Stops the animation.
*/
public void stop() {
this.isRunning = false;
}
/**
* Moves the label one frame to the left. If it's out of display range, move it back
* to the right, out of display range.
*/
private void tickAnimation() {
this.panelLocation -= MarqueePanel.MOVEMENT_PER_FRAME;
if (this.panelLocation < this.textLabel.getWidth())
this.panelLocaton = this.getWidth();
this.textLabel.setLocation(this.panelLocation, 0);
this.repaint();
if (this.isRunning) {
Timer t = new Timer(1000 / MarqueePanel.FRAMES_PER_SECOND, this.taskPerformer);
t.setRepeats(false);
t.start();
}
}
}
Add a JLabel to your frame or panel.
ScrollText s= new ScrollText("ello Everyone.");
jLabel3.add(s);
public class ScrollText extends JComponent {
private BufferedImage image;
private Dimension imageSize;
private volatile int currOffset;
private Thread internalThread;
private volatile boolean noStopRequested;
public ScrollText(String text) {
currOffset = 0;
buildImage(text);
setMinimumSize(imageSize);
setPreferredSize(imageSize);
setMaximumSize(imageSize);
setSize(imageSize);
noStopRequested = true;
Runnable r = new Runnable() {
public void run() {
try {
runWork();
} catch (Exception x) {
x.printStackTrace();
}
}
};
internalThread = new Thread(r, "ScrollText");
internalThread.start();
}
private void buildImage(String text) {
RenderingHints renderHints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
renderHints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
BufferedImage scratchImage = new BufferedImage(1, 1,
BufferedImage.TYPE_INT_RGB);
Graphics2D scratchG2 = scratchImage.createGraphics();
scratchG2.setRenderingHints(renderHints);
Font font = new Font("Serif", Font.BOLD | Font.ITALIC, 24);
FontRenderContext frc = scratchG2.getFontRenderContext();
TextLayout tl = new TextLayout(text, font, frc);
Rectangle2D textBounds = tl.getBounds();
int textWidth = (int) Math.ceil(textBounds.getWidth());
int textHeight = (int) Math.ceil(textBounds.getHeight());
int horizontalPad = 600;
int verticalPad = 10;
imageSize = new Dimension(textWidth + horizontalPad, textHeight
+ verticalPad);
image = new BufferedImage(imageSize.width, imageSize.height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
g2.setRenderingHints(renderHints);
int baselineOffset = (verticalPad / 2) - ((int) textBounds.getY());
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, imageSize.width, imageSize.height);
g2.setColor(Color.GREEN);
tl.draw(g2, 0, baselineOffset);
// Free-up resources right away, but keep "image" for
// animation.
scratchG2.dispose();
scratchImage.flush();
g2.dispose();
}
public void paint(Graphics g) {
// Make sure to clip the edges, regardless of curr size
g.setClip(0, 0, imageSize.width, imageSize.height);
int localOffset = currOffset; // in case it changes
g.drawImage(image, -localOffset, 0, this);
g.drawImage(image, imageSize.width - localOffset, 0, this);
// draw outline
g.setColor(Color.black);
g.drawRect(0, 0, imageSize.width - 1, imageSize.height - 1);
}
private void runWork() {
while (noStopRequested) {
try {
Thread.sleep(10); // 10 frames per second
// adjust the scroll position
currOffset = (currOffset + 1) % imageSize.width;
// signal the event thread to call paint()
repaint();
} catch (InterruptedException x) {
Thread.currentThread().interrupt();
}
}
}
public void stopRequest() {
noStopRequested = false;
internalThread.interrupt();
}
public boolean isAlive() {
return internalThread.isAlive();
}
}
This is supposed to be an improvement of #camickr MarqueePanel. Please see above.
To map mouse events to the specific components added to MarqueePanel
Override add(Component comp) of MarqueePanel in order to direct all mouse events of the components
An issue here is what do do with the MouseEvents fired from the individual components.
My approach is to remove the mouse listeners form the components added and let the MarqueePanel redirect the event to the correct component.
In my case these components are supposed to be links.
#Override
public Component add(Component comp) {
comp = super.add(comp);
if(comp instanceof MouseListener)
comp.removeMouseListener((MouseListener)comp);
comp.addMouseListener(this);
return comp;
}
Then map the component x to a MarqueePanel x and finally the correct component
#Override
public void mouseClicked(MouseEvent e)
{
Component source = (Component)e.getSource();
int x = source.getX() + e.getX();
int y = source.getY();
MarqueePanel2 marqueePanel = (MarqueePanel2) ((JComponent)e.getSource()).getParent();
double x2 = marqueePanel.getWidth();
double x1 = Math.abs(marqueePanel.scrollOffset);
if(x >= x1 && x <= x2)
{
System.out.println("Bang " + x1);
Component componentAt = getComponentAt(x+marqueePanel.scrollOffset, y);
if(comp instanceof MouseListener)
((MouseListener) componentAt).mouseClicked(e);
System.out.println(componentAt.getName());
}
else
{
return;
}
//System.out.println(x);
}

Actions inside of another action like Netbeans

I need to find the way to show several actions inside of another action like Netbeans does with Run Main Project icon.
You can see there is a default action Run Main Project and if you click in the little arrow next to the green play icon, you can select a specific action like Run .
I was checking the code of Netbeans but I can't find the code to make this in my application.
Ah, (one of) the holy grails of UI components, a split button. Over a number of years I've tried to find one which would perform well under multiple look and feels and failed dismally.
Many used multiple buttons or just resorted to using a JComboBox
Like many things, I stumbled across one which was pretty well done, but which I had to modify to suit my needs, unfortunately, I don't remember the original version or author, sorry. (If you believe this code is based on yours, please leave a comment with a link to the original and I will evaluate and provide appropriate credit)
Basically, if you click the button, it will run the "default" action (Bananas) otherwise you can select one of the sub elements and it will execute it
public class SplitButton extends JButton {
private int separatorSpacing = 4;
private int splitWidth = 22;
private int arrowSize = 8;
private boolean onSplit;
private Rectangle splitRectangle;
private JFrame popupMenu;
private boolean alwaysDropDown;
private Color arrowColor = Color.BLACK;
private Color disabledArrowColor = Color.GRAY;
private Image image;
private MouseHandler mouseHandler;
private boolean toolBarButton;
private PopupWindowEventHandler popupWindowEventHandler;
/**
* Creates a button with initial text and an icon.
*
* #param text the text of the button
* #param icon the Icon image to display on the button
*/
public SplitButton() {
super();
addMouseMotionListener(getMouseHandler());
addMouseListener(getMouseHandler());
// Default for no "default" action...
setAlwaysDropDown(true);
InputMap im = getInputMap(WHEN_FOCUSED);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "PopupMenu.close");
am.put("PopupMenu.close", new ClosePopupAction());
}
public SplitButton(Action defaultAction, Action... actions) {
this();
setAction(defaultAction);
for (Action action : actions) {
addAction(action);
}
}
public SplitButton(String text, Icon icon, Action... actions) {
this((Action) null, actions);
setText(text);
setIcon(icon);
}
public SplitButton(String text, Action... actions) {
this((Action) null, actions);
setText(text);
}
public JSplitButton(Icon icon, Action... actions) {
this((Action) null, actions);
setIcon(icon);
}
#Override
public void setAction(Action a) {
super.setAction(a);
if (a != null) {
setAlwaysDropDown(false);
}
}
/**
* Creates a pre-configured button suitable for being used on a JToolBar
*
* #param defaultAction
* #param actions
* #return
*/
public static SplitButton createToolBarButton(Action defaultAction, Action... actions) {
JSplitButton btn = new JSplitButton(defaultAction, actions);
btn.configureForToolBar();
return btn;
}
/**
* Creates a pre-configured "options only" button suitable for being used on
* a JToolBar
*
* #param text
* #param icon
* #param actions
* #return
*/
public static SplitButton createToolBarButton(String text, Icon icon, Action... actions) {
JSplitButton btn = new JSplitButton(icon, actions);
btn.setToolTipText(text);
btn.configureForToolBar();
return btn;
}
/**
* Used to determine if the button is begin configured for use on a tool bar
*
* #return
*/
public boolean isToolBarButton() {
return toolBarButton;
}
/**
* Configures this button for use on a tool bar...
*/
public void configureForToolBar() {
toolBarButton = true;
if (getIcon() != null) {
setHideActionText(true);
}
setHorizontalTextPosition(JButton.CENTER);
setVerticalTextPosition(JButton.BOTTOM);
setFocusable(false);
}
protected MouseHandler getMouseHandler() {
if (mouseHandler == null) {
mouseHandler = new MouseHandler();
}
return mouseHandler;
}
protected AbstractButton getButtonFor(Action action) {
Container parent = ((JFrame) getPopupWindow()).getContentPane();
AbstractButton btn = null;
for (Component comp : parent.getComponents()) {
if (comp instanceof AbstractButton) {
Action childAction = ((AbstractButton) comp).getAction();
if (action.equals(childAction)) {
btn = (AbstractButton) comp;
break;
}
}
}
return btn;
}
/**
* Returns the index of the specified action within the popup window or -1
* of it does not exist
*
* #param action
* #return
*/
public int indexOfAction(Action action) {
Container parent = ((JFrame) getPopupWindow()).getContentPane();
AbstractButton btn = getButtonFor(action);
return btn == null ? -1 : parent.getComponentZOrder(btn);
}
/**
* Adds the specified action to the popup menu...
*
* This simply calls getPopupWindow().add(action)
*
* #param action Add
*/
public void addAction(Action action) {
addActionAt(action, -1);
}
protected int getOptionsCount() {
return ((JFrame) getPopupWindow()).getContentPane().getComponentCount();
}
protected void addActionAt(Action action, int index) {
if (index < 0 || index >= getOptionsCount()) {
getPopupWindow().add(createMenuItem(action));
} else {
getPopupWindow().add(createMenuItem(action), index);
}
}
protected void removeAction(Action action) {
AbstractButton btn = getButtonFor(action);
if (btn != null) {
getPopupWindow().remove(btn);
}
}
/**
* Creates a new JMenuItem from the supplied Action. This is used to
* provided the ability for subclasses to either change the type of menu
* item used by the button or add additional functionality (like listeners)
* should they be required
*
* #param action
* #return
*/
protected JMenuItem createMenuItem(Action action) {
return new JMenuItem(action);
}
#Override
public Insets getInsets() {
Insets insets = (Insets) super.getInsets().clone();
insets.right += splitWidth;
return insets;
}
#Override
public Insets getInsets(Insets insets) {
Insets insets1 = getInsets();
insets.left = insets1.left;
insets.right = insets1.right;
insets.bottom = insets1.bottom;
insets.top = insets1.top;
return insets1;
}
/**
* Returns the window that acts as the buttons popup window
*
* #return
*/
public Window getPopupWindow() {
if (popupMenu == null) {
popupMenu = new JFrame();
popupMenu.setFocusableWindowState(false);
popupMenu.setUndecorated(true);
popupMenu.setContentPane(createPopupWindowContentPane());
popupMenu.setAlwaysOnTop(true);
DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
if ("focusOwner".equalsIgnoreCase(name)
|| "permanentFocusOwner".equalsIgnoreCase(name)
|| "focusedWindow".equalsIgnoreCase(name)
|| "activeWindow".equalsIgnoreCase(name)) {
Window focusedWindow = DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
if (!popupMenu.equals(focusedWindow)) {
closePopupWinodw();
}
}
}
});
}
return popupMenu;
}
protected Container createPopupWindowContentPane() {
return new DefaultMenuPane();
}
protected void closePopupWinodw() {
getPopupWindow().setVisible(false);
if (popupWindowEventHandler != null) {
Toolkit.getDefaultToolkit().removeAWTEventListener(popupWindowEventHandler);
}
}
protected void showPopupWindow() {
Window popup = getPopupWindow();
popup.pack();
Point pos = getLocationOnScreen();
popup.setLocation(pos.x + (getWidth() - popup.getWidth()), pos.y + getHeight());
popup.setVisible(true);
if (popupWindowEventHandler == null) {
popupWindowEventHandler = new PopupWindowEventHandler();
}
Toolkit.getDefaultToolkit().addAWTEventListener(popupWindowEventHandler, AWTEvent.MOUSE_EVENT_MASK);
}
/**
* Returns the separatorSpacing. Separator spacing is the space above and
* below the separator( the line drawn when you hover your mouse over the
* split part of the button).
*
* #return separatorSpacingimage = null; //to repaint the image with the new
* size
*/
public int getSeparatorSpacing() {
return separatorSpacing;
}
/**
* Sets the separatorSpacing.Separator spacing is the space above and below
* the separator( the line drawn when you hover your mouse over the split
* part of the button).
*
* #param spacing
*/
public void setSeparatorSpacing(int spacing) {
if (spacing != separatorSpacing && spacing >= 0) {
int old = separatorSpacing;
this.separatorSpacing = spacing;
image = null;
firePropertyChange("separatorSpacing", old, separatorSpacing);
revalidate();
repaint();
}
}
/**
* Show the dropdown menu, if attached, even if the button part is clicked.
*
* #return true if alwaysDropdown, false otherwise.
*/
public boolean isAlwaysDropDown() {
return alwaysDropDown;
}
/**
* Show the dropdown menu, if attached, even if the button part is clicked.
*
* If true, this will prevent the button from raising any actionPerformed
* events for itself
*
* #param value true to show the attached dropdown even if the button part
* is clicked, false otherwise
*/
public void setAlwaysDropDown(boolean value) {
if (alwaysDropDown != value) {
this.alwaysDropDown = value;
firePropertyChange("alwaysDropDown", !alwaysDropDown, alwaysDropDown);
}
}
/**
* Gets the color of the arrow.
*
* #return arrowColor
*/
public Color getArrowColor() {
return arrowColor;
}
/**
* Set the arrow color.
*
* #param color
*/
public void setArrowColor(Color color) {
if (arrowColor != color) {
Color old = arrowColor;
this.arrowColor = color;
image = null;
firePropertyChange("arrowColor", old, arrowColor);
repaint();
}
}
/**
* gets the disabled arrow color
*
* #return disabledArrowColor color of the arrow if no popup attached.
*/
public Color getDisabledArrowColor() {
return disabledArrowColor;
}
/**
* sets the disabled arrow color
*
* #param color color of the arrow if no popup attached.
*/
public void setDisabledArrowColor(Color color) {
if (disabledArrowColor != color) {
Color old = disabledArrowColor;
this.disabledArrowColor = color;
image = null; //to repaint the image with the new color
firePropertyChange("disabledArrowColor", old, disabledArrowColor);
}
}
/**
* Splitwidth is the width of the split part of the button.
*
* #return splitWidth
*/
public int getSplitWidth() {
return splitWidth;
}
/**
* Splitwidth is the width of the split part of the button.
*
* #param width
*/
public void setSplitWidth(int width) {
if (splitWidth != width) {
int old = splitWidth;
this.splitWidth = width;
firePropertyChange("splitWidth", old, splitWidth);
revalidate();
repaint();
}
}
/**
* gets the size of the arrow.
*
* #return size of the arrow
*/
public int getArrowSize() {
return arrowSize;
}
/**
* sets the size of the arrow
*
* #param size
*/
public void setArrowSize(int size) {
if (arrowSize != size) {
int old = arrowSize;
this.arrowSize = size;
image = null; //to repaint the image with the new size
firePropertyChange("setArrowSize", old, arrowSize);
revalidate();
repaint();
}
}
/**
* Gets the image to be drawn in the split part. If no is set, a new image
* is created with the triangle.
*
* #return image
*/
public Image getImage() {
if (image == null) {
Graphics2D g = null;
BufferedImage img = new BufferedImage(arrowSize, arrowSize, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) img.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
g.setColor(popupMenu != null ? arrowColor : disabledArrowColor);
//this creates a triangle facing right >
g.fillPolygon(new int[]{0, 0, arrowSize / 2}, new int[]{0, arrowSize, arrowSize / 2}, 3);
g.dispose();
//rotate it to face downwards
img = rotate(img, 90);
BufferedImage dimg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
g = (Graphics2D) dimg.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(img, null, 0, 0);
g.dispose();
for (int i = 0; i < dimg.getHeight(); i++) {
for (int j = 0; j < dimg.getWidth(); j++) {
if (dimg.getRGB(j, i) == Color.WHITE.getRGB()) {
dimg.setRGB(j, i, 0x8F1C1C);
}
}
}
image = Toolkit.getDefaultToolkit().createImage(dimg.getSource());
}
return image;
}
/**
*
* #param g
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//Graphics gClone = g.create();//EDIT: Hervé Guillaume
Color oldColor = g.getColor();
splitRectangle = new Rectangle(getWidth() - splitWidth, 0, splitWidth, getHeight());
g.translate(splitRectangle.x, splitRectangle.y);
int mh = getHeight() / 2;
int mw = splitWidth / 2;
g.drawImage(getImage(), mw - arrowSize / 2, mh + 2 - arrowSize / 2, null);
if (!alwaysDropDown) {
if (getModel().isRollover() || isFocusable()) {
g.setColor(UIManager.getLookAndFeelDefaults().getColor("Button.background"));
g.drawLine(1, separatorSpacing + 2, 1, getHeight() - separatorSpacing - 2);
g.setColor(UIManager.getLookAndFeelDefaults().getColor("Button.shadow"));
g.drawLine(2, separatorSpacing + 2, 2, getHeight() - separatorSpacing - 2);
}
}
g.setColor(oldColor);
g.translate(-splitRectangle.x, -splitRectangle.y);
}
/**
* Rotates the given image with the specified angle.
*
* #param img image to rotate
* #param angle angle of rotation
* #return rotated image
*/
private BufferedImage rotate(BufferedImage img, int angle) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage dimg = dimg = new BufferedImage(w, h, img.getType());
Graphics2D g = dimg.createGraphics();
g.rotate(Math.toRadians(angle), w / 2, h / 2);
g.drawImage(img, null, 0, 0);
return dimg;
}
#Override
protected void fireActionPerformed(ActionEvent event) {
// This is a little bit of a nasty trick. Basically this is where
// we try and decide if the buttons "default" action should
// be fired or not. We don't want it firing if the button
// is in "options only" mode or the user clicked on
// on the "drop down arrow"....
if (onSplit || isAlwaysDropDown()) {
showPopupWindow();
} else {
super.fireActionPerformed(event);
}
}
protected class MouseHandler extends MouseAdapter {
#Override
public void mouseExited(MouseEvent e) {
onSplit = false;
repaint(splitRectangle);
}
#Override
public void mouseMoved(MouseEvent e) {
if (splitRectangle.contains(e.getPoint())) {
onSplit = true;
} else {
onSplit = false;
}
repaint(splitRectangle);
}
}
protected class PopupWindowEventHandler implements AWTEventListener {
#Override
public void eventDispatched(AWTEvent event) {
if (popupMenu.isVisible()) {
switch (event.getID()) {
case MouseEvent.MOUSE_RELEASED:
Object source = event.getSource();
if (source instanceof Component) {
Window win = SwingUtilities.getWindowAncestor((Component) source);
if (!popupMenu.equals(win)) {
closePopupWinodw();
}
}
break;
}
}
}
}
protected class ClosePopupAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
closePopupWinodw();
}
}
protected class DefaultMenuPane extends JPanel {
public DefaultMenuPane() {
setBorder(UIManager.getBorder("PopupMenu.border"));
setBackground(UIManager.getColor("PopupMenu.background"));
setLayout(new GridLayout(0, 1));
}
}
}
It would be configured something like ...
SplitButton btn = new SplitButton();
btn.setAction(new FruitAction("Banana", new BananaIcon(32, 32)));
btn.addAction(new FruitAction("Apple", new AppleIcon(32, 32)));
btn.addAction(new FruitAction("Black Berry", new BlackBerriesIcon(32, 32)));
btn.addAction(new FruitAction("Grapes", new GrapesIcon(32, 32)));
btn.addAction(new FruitAction("Peach", new PeachIcon(32, 32)));
btn.addAction(new FruitAction("Strewberry", new StrewberriesIcon(32, 32)));
And, for reference, the fruit action looks like...
public class FruitAction extends AbstractAction {
public FruitAction(String text, Icon icon) {
putValue(NAME, text);
putValue(SMALL_ICON, icon);
putValue(SHORT_DESCRIPTION, text);
}
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "I am " + getValue(NAME), "Fruit", JOptionPane.INFORMATION_MESSAGE);
}
}
This is use a custom vector based icon library, so obviously, I won't be including that, but it gives you an idea of how to configure it
I too have been looking for a decent JSplitButton for a while, but everything I found as a standalone component was rather disappointing.
MadProgrammer's answer looked very promising, but it did not work all that well for me. I'm not 100% sure what it might be caused by, whether it's by design or because of the L&F I'm using but the provided component had focus issues. Specifically, the items of the popup did not show any hover indicators, indicating they weren't receiving focus. Also upon clicking an item in the popup the popup did not close itself.
Anyway, I rewrote parts of the component and made it use a JPopupMenu instead of a custom JFrame to avoid handling focus myself. The component set's the popup menu as its popup menu using the JComponent.setComponentPopupMenu() and then just invoking the popup menu upon clicking the dropdown arrow. This also makes it that it's possible to right-click the button to show the popup directly.
The popup has focus and behaves like a regular JPopupMenu, supporting adding stuff like separators and whatnot.
Note: The L&F used in the image is flatlaf
The button's default action can be set like a normal JButton using addActionListener() or using actions by calling setAction().
SplitButton btn = new SplitButton("Click me");
btn.addActionListener((e) -> {
System.out.println("Button clicked");
});
SplitButton btn2 = new SplitButton(new AbstractAction("Click me") {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked");
}
});
The popup menu can be created separately and then assigned to the button using setPopupMenu() or you can add individual items as actions to the menu without the need of creating it yourself using addAction and addActionAt.
JPopupMenu popup = new JPopupMenu();
popup.add(new JMenuItem("A popup option"));
popup.add(new JMenuItem("JMenuItem with icon", Icons.deleteBin));
popup.addSeparator();
btn.setPopupMenu(popup);
btn.addAction(new AbstractAction("Or don't", Icons.alert) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Or don't clicked");
}
});
btn.addAction(new AbstractAction("Click me in a different way") {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Click me in a different way clicked");
}
});
The popup menu can be retrieved simply by using getPopupMenu().
The full code
Again, original by MadProgrammer and whoever he got it from :D
/**
* A swing split button implementation.
* A JButton that has an additional section with an arrow icon on the right that when clicked
* shows a JPopupMenu that is positioned flush with the button.
*
* The implementation sets the buttons pop-up menu using setComponentPopupMenu()
* meaning that in addition to clicking the drop-down arrow, user can also right click
* the button to open the pop-up menu.
*
* Author: DUDSS - 21.02.2020
* I modified the button to use a JPopupMenu instead of a custom JFrame to avoid hacky
* focus workarounds and fix focus issues.
*
* Credit:
* Modified version of a split button by MadProgrammer.
* https://stackoverflow.com/questions/36352707/actions-inside-of-another-action-like-netbeans
* It's original author seems to be unknown.
*
*/
public class SplitButton extends JButton {
private int separatorSpacing = 4;
private int splitWidth = 22;
private int arrowSize = 8;
private boolean onSplit;
private Rectangle splitRectangle;
private boolean alwaysDropDown;
private Color arrowColor = Color.BLACK;
private Color disabledArrowColor = Color.GRAY;
private Image image;
private MouseHandler mouseHandler;
private boolean toolBarButton;
private JPopupMenu jpopupMenu;
/**
* Creates a button with initial text and an icon.
*
* #param text the text of the button
* #param icon the Icon image to display on the button
*/
public SplitButton() {
super();
addMouseMotionListener(getMouseHandler());
addMouseListener(getMouseHandler());
// Default for no "default" action...
setAlwaysDropDown(true);
InputMap im = getInputMap(WHEN_FOCUSED);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "PopupMenu.close");
am.put("PopupMenu.close", new ClosePopupAction());
}
public SplitButton(Action defaultAction) {
this();
setAction(defaultAction);
}
public SplitButton(Action defaultAction, JPopupMenu popup) {
this();
setAction(defaultAction);
setPopupMenu(popup);
}
public SplitButton(Action defaultAction, Action... actions) {
this();
setAction(defaultAction);
for (Action a : actions) {
addAction(a);
}
}
public SplitButton(String text) {
this();
setText(text);
}
public SplitButton(String text, Icon icon) {
this();
setText(text);
setIcon(icon);
}
public SplitButton(String text, JPopupMenu popup) {
this();
setText(text);
setPopupMenu(popup);
}
public SplitButton(String text, Icon icon, JPopupMenu popup) {
this();
setText(text);
setIcon(icon);
setPopupMenu(popup);
}
/**
* Creates a pre-configured button suitable for being used on a JToolBar
*
* #param defaultAction
* #param actions
* #return
*/
public static SplitButton createToolBarButton(Action defaultAction, Action... actions) {
SplitButton btn = new SplitButton(defaultAction, actions);
btn.configureForToolBar();
return btn;
}
/**
* Creates a pre-configured "options only" button suitable for being used on
* a JToolBar
*
* #param text
* #param icon
* #param actions
* #return
*/
public static SplitButton createToolBarButton(String text, Icon icon, JPopupMenu popupMenu) {
SplitButton btn = new SplitButton(text, icon);
btn.setPopupMenu(popupMenu);
btn.setToolTipText(text);
btn.configureForToolBar();
return btn;
}
#Override
public void addActionListener(ActionListener l) {
if (l != null) {
setAlwaysDropDown(false);
}
super.addActionListener(l);
}
#Override
public void setAction(Action a) {
super.setAction(a);
if (a != null) {
setAlwaysDropDown(false);
}
}
public void addActionAt(Action a, int index) {
getPopupMenu().insert(a, index);
}
public void addAction(Action a) {
getPopupMenu().add(a);
}
public void setPopupMenu(JPopupMenu popup) {
jpopupMenu = popup;
this.setComponentPopupMenu(popup);
}
/**
* Returns the buttons popup menu.
*
* #return
*/
public JPopupMenu getPopupMenu() {
if (jpopupMenu == null) {
jpopupMenu = new JPopupMenu();
}
return jpopupMenu;
}
/**
* Used to determine if the button is begin configured for use on a tool bar
*
* #return
*/
public boolean isToolBarButton() {
return toolBarButton;
}
/**
* Configures this button for use on a tool bar...
*/
public void configureForToolBar() {
toolBarButton = true;
if (getIcon() != null) {
setHideActionText(true);
}
setHorizontalTextPosition(JButton.CENTER);
setVerticalTextPosition(JButton.BOTTOM);
setFocusable(false);
}
protected MouseHandler getMouseHandler() {
if (mouseHandler == null) {
mouseHandler = new MouseHandler();
}
return mouseHandler;
}
protected int getOptionsCount() {
return getPopupMenu().getComponentCount();
}
/*protected void addActionAt(Action action, int index) {
if (index < 0 || index >= getOptionsCount()) {
getPopupWindow().add(createMenuItem(action));
} else {
getPopupWindow().add(createMenuItem(action), index);
}
}*/
/*protected void removeAction(Action action) {
AbstractButton btn = getButtonFor(action);
if (btn != null) {
getPopupWindow().remove(btn);
}
}*/
#Override
public Insets getInsets() {
Insets insets = (Insets) super.getInsets().clone();
insets.right += splitWidth;
return insets;
}
#Override
public Insets getInsets(Insets insets) {
Insets insets1 = getInsets();
insets.left = insets1.left;
insets.right = insets1.right;
insets.bottom = insets1.bottom;
insets.top = insets1.top;
return insets1;
}
protected void closePopupMenu() {
getPopupMenu().setVisible(false);
}
protected void showPopupMenu() {
if (getOptionsCount() > 0) {
JPopupMenu menu = getPopupMenu();
menu.setVisible(true); //Necessary to calculate pop-up menu width the first time it's displayed.
menu.show(this, (getWidth() - menu.getWidth()), getHeight());
}
}
/**
* Returns the separatorSpacing. Separator spacing is the space above and
* below the separator( the line drawn when you hover your mouse over the
* split part of the button).
*
* #return separatorSpacingimage = null; //to repaint the image with the new
* size
*/
public int getSeparatorSpacing() {
return separatorSpacing;
}
/**
* Sets the separatorSpacing.Separator spacing is the space above and below
* the separator( the line drawn when you hover your mouse over the split
* part of the button).
*
* #param spacing
*/
public void setSeparatorSpacing(int spacing) {
if (spacing != separatorSpacing && spacing >= 0) {
int old = separatorSpacing;
this.separatorSpacing = spacing;
image = null;
firePropertyChange("separatorSpacing", old, separatorSpacing);
revalidate();
repaint();
}
}
/**
* Show the dropdown menu, if attached, even if the button part is clicked.
*
* #return true if alwaysDropdown, false otherwise.
*/
public boolean isAlwaysDropDown() {
return alwaysDropDown;
}
/**
* Show the dropdown menu, if attached, even if the button part is clicked.
*
* If true, this will prevent the button from raising any actionPerformed
* events for itself
*
* #param value true to show the attached dropdown even if the button part
* is clicked, false otherwise
*/
public void setAlwaysDropDown(boolean value) {
if (alwaysDropDown != value) {
this.alwaysDropDown = value;
firePropertyChange("alwaysDropDown", !alwaysDropDown, alwaysDropDown);
}
}
/**
* Gets the color of the arrow.
*
* #return arrowColor
*/
public Color getArrowColor() {
return arrowColor;
}
/**
* Set the arrow color.
*
* #param color
*/
public void setArrowColor(Color color) {
if (arrowColor != color) {
Color old = arrowColor;
this.arrowColor = color;
image = null;
firePropertyChange("arrowColor", old, arrowColor);
repaint();
}
}
/**
* gets the disabled arrow color
*
* #return disabledArrowColor color of the arrow if no popup attached.
*/
public Color getDisabledArrowColor() {
return disabledArrowColor;
}
/**
* sets the disabled arrow color
*
* #param color color of the arrow if no popup attached.
*/
public void setDisabledArrowColor(Color color) {
if (disabledArrowColor != color) {
Color old = disabledArrowColor;
this.disabledArrowColor = color;
image = null; //to repaint the image with the new color
firePropertyChange("disabledArrowColor", old, disabledArrowColor);
}
}
/**
* Splitwidth is the width of the split part of the button.
*
* #return splitWidth
*/
public int getSplitWidth() {
return splitWidth;
}
/**
* Splitwidth is the width of the split part of the button.
*
* #param width
*/
public void setSplitWidth(int width) {
if (splitWidth != width) {
int old = splitWidth;
this.splitWidth = width;
firePropertyChange("splitWidth", old, splitWidth);
revalidate();
repaint();
}
}
/**
* gets the size of the arrow.
*
* #return size of the arrow
*/
public int getArrowSize() {
return arrowSize;
}
/**
* sets the size of the arrow
*
* #param size
*/
public void setArrowSize(int size) {
if (arrowSize != size) {
int old = arrowSize;
this.arrowSize = size;
image = null; //to repaint the image with the new size
firePropertyChange("setArrowSize", old, arrowSize);
revalidate();
repaint();
}
}
/**
* Gets the image to be drawn in the split part. If no is set, a new image
* is created with the triangle.
*
* #return image
*/
public Image getImage() {
if (image == null) {
Graphics2D g = null;
BufferedImage img = new BufferedImage(arrowSize, arrowSize, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) img.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
g.setColor(jpopupMenu != null ? arrowColor : disabledArrowColor);
//this creates a triangle facing right >
g.fillPolygon(new int[]{0, 0, arrowSize / 2}, new int[]{0, arrowSize, arrowSize / 2}, 3);
g.dispose();
//rotate it to face downwards
img = rotate(img, 90);
BufferedImage dimg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
g = (Graphics2D) dimg.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(img, null, 0, 0);
g.dispose();
for (int i = 0; i < dimg.getHeight(); i++) {
for (int j = 0; j < dimg.getWidth(); j++) {
if (dimg.getRGB(j, i) == Color.WHITE.getRGB()) {
dimg.setRGB(j, i, 0x8F1C1C);
}
}
}
image = Toolkit.getDefaultToolkit().createImage(dimg.getSource());
}
return image;
}
/**
*
* #param g
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//Graphics gClone = g.create();//EDIT: Hervé Guillaume
Color oldColor = g.getColor();
splitRectangle = new Rectangle(getWidth() - splitWidth, 0, splitWidth, getHeight());
g.translate(splitRectangle.x, splitRectangle.y);
int mh = getHeight() / 2;
int mw = splitWidth / 2;
g.drawImage(getImage(), mw - arrowSize / 2, mh + 2 - arrowSize / 2, null);
if (!alwaysDropDown) {
if (getModel().isRollover() || isFocusable()) {
g.setColor(UIManager.getLookAndFeelDefaults().getColor("Button.background"));
g.drawLine(1, separatorSpacing + 2, 1, getHeight() - separatorSpacing - 2);
g.setColor(UIManager.getLookAndFeelDefaults().getColor("Button.shadow"));
g.drawLine(2, separatorSpacing + 2, 2, getHeight() - separatorSpacing - 2);
}
}
g.setColor(oldColor);
g.translate(-splitRectangle.x, -splitRectangle.y);
}
/**
* Rotates the given image with the specified angle.
*
* #param img image to rotate
* #param angle angle of rotation
* #return rotated image
*/
private BufferedImage rotate(BufferedImage img, int angle) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage dimg = dimg = new BufferedImage(w, h, img.getType());
Graphics2D g = dimg.createGraphics();
g.rotate(Math.toRadians(angle), w / 2, h / 2);
g.drawImage(img, null, 0, 0);
return dimg;
}
#Override
protected void fireActionPerformed(ActionEvent event) {
// This is a little bit of a nasty trick. Basically this is where
// we try and decide if the buttons "default" action should
// be fired or not. We don't want it firing if the button
// is in "options only" mode or the user clicked on
// on the "drop down arrow"....
if (onSplit || isAlwaysDropDown()) {
showPopupMenu();
} else {
super.fireActionPerformed(event);
}
}
protected class MouseHandler extends MouseAdapter {
#Override
public void mouseExited(MouseEvent e) {
onSplit = false;
repaint(splitRectangle);
}
#Override
public void mouseMoved(MouseEvent e) {
if (splitRectangle.contains(e.getPoint())) {
onSplit = true;
} else {
onSplit = false;
}
repaint(splitRectangle);
}
}
protected class ClosePopupAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
closePopupMenu();
}
}
}

Custom painting in Java - drawing UI elements onto the JPanel

I am creating a drawing board program, basically a simplified version of MS Paint. It is working for the most part but when I draw on the board, the currently selected UI element shows up on the drawing area.
I tried paintComponent, which worked when I called super.paintComponent(g) (as one does) but it cleared the drawing area before I drew the next object. I overrode update and put a println statement in there and it is never being called.
The buttons at the top are red because I set the background to the bottom JPanel to red to see what the background of these buttons would be. So clearly they are part of the bottom JPanel. I am using a BorderLayout for the layout.
Here is my code (with some irrelevant bits removed):
public class JSPaint extends JFrame implements Serializable
{
private static final long serialVersionUID = -8787645153679803322L;
private JFrame mainFrame;
private JPanel bp;
private JButton ...
private DrawingArea da;
public JSPaint()
{
setTitle("JS Paint");
setSize(1024, 768);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Drawing area
da = new DrawingArea();
setLayout(new BorderLayout());
// add the buttons to the panel
buttonPanel();
// Add the drawing area
add(bp, BorderLayout.SOUTH);
bp.setBackground(Color.RED);
add(da, BorderLayout.CENTER);
da.setBackground(Color.BLUE);
setVisible(true);
}
// I put it here too just in case
#Override
public void update(Graphics g)
{
System.out.println("update in JSPaint called.");
paint(g);
}
/*
* Creates the panel for the buttons, creates the buttons and places them on
* the panel
*/
public void buttonPanel()
{
// Create the panel for the buttons to be placed in
bp = new JPanel();
saveButton = new JButton("Save");
loadButton = new JButton("Load");
//more buttons
bp.add(saveButton);
bp.add(loadButton);
//more buttons
// ActionListeners
colorButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
System.out.println("color");
da.color();
}
});
}
public class DrawingArea extends JPanel
{
private static final long serialVersionUID = -8299084743195098560L;
boolean dragged = false;
#Override
public void update(Graphics g)
{
System.out.println("Update in DrawingArea called");
paint(g);
}
/*
* Draws the selected shape onto the screen and saves it into a Stack.
*
*/
public void draw()
{
this.addMouseMotionListener(new MouseMotionListener()
{
public void mouseDragged(MouseEvent me)
{
dragged = true;
}
public void mouseMoved(MouseEvent me) {}
});
//more listeners...
});
}
/*
* Draws the selected String onto the screen when the mouse is held down.
*
*/
public void brush()
{
this.addMouseMotionListener(new MouseMotionListener()
{
public void mouseDragged(MouseEvent me)
{
// If we are in drawing mode, draw the String. Create a new
// Figure Object and push it onto the Stack
if(activeButton == "brush")
{
startPoint = me.getPoint();
Figure fig = new Figure("String", startPoint, null, currentColor);
// figures.push(calculate(fig));
toPaint.push(calculate(fig));
repaint();
}
}
public void mouseMoved(MouseEvent me) {}
});
}
// more of the same...
public void paint(Graphics g)
{
toSave.addAll(toPaint);
while(!toPaint.isEmpty())
{
Figure f = toPaint.pop();
String t = f.type;
if(f.color != null)
{
g.setColor(f.color);
}
switch(t)
{
case "Rectangle": g.drawRect(f.x1, f.y1, f.width, f.height);
break;
case "Oval": g.drawOval(f.x1, f.y1, f.width, f.height);
break;
case "Line": g.drawLine(f.x1, f.y1, f.x2, f.y2);
break;
case "Clear":
g.fillRect(0, 0, da.getWidth(), da.getHeight());
clearStack(toSave);
break;
case "String": g.drawString(f.toPrint, f.x1, f.y1);
break;
}
}
}
}
private class Figure implements Serializable
{
private static final long serialVersionUID = 4690475365105752994L;
String type, toPrint;
Color color;
Point start;
Point end;
int x1, y1, x2, y2, width, height;
public Figure(String figureType,
Point startPoint, Point endPoint, Color figureColor)
{
type = figureType;
color = figureColor;
start = startPoint;
end = endPoint;
}
// Rect, Oval
public Figure(String figureType, int figureX, int figureY,
int figureWidth, int figureHeight, Color figureColor)
{
type = figureType;
x1 = figureX;
y1 = figureY;
width = figureWidth;
height = figureHeight;
color = figureColor;
}
// more shapes
}
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new JSPaint();
}
});
}
}
If the currently selected UI element shows up on the drawing area, this can have only one reason: you are doing something wrong with the Graphics objects, possibly calling a translate on them, and not resetting the tranlation when you are done. (See this link for an example of translate used correctly).

Button with round edges

I am trying to get a button with round edges. My button has a background color of yellow. I am not able to get the round edge for my button. Here is the code i am trying
class RoundedBorder implements Border {
int radius;
RoundedBorder(int radius) {
this.radius = radius;
}
public Insets getBorderInsets(Component c) {
return new Insets(this.radius+1, this.radius+1, this.radius+2, this.radius);
}
public boolean isBorderOpaque() {
return true;
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
g.drawRoundRect(x,y,width-1,height-1,radius,radius);
}
}
jButton1.setText(aContinue);
jButton1.setBackground(new java.awt.Color(255, 255, 0));
jButton1.setBorder(new RoundedBorder(20));
I am not able to get round edges using this piece of code.. Here is how my button looks .
I want to have round edges with no overflowing background color.
Option1- Use Images
Option2- Use the following code (Extracted from Make a button round)
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class RoundButton extends JButton {
public RoundButton(String label) {
super(label);
// These statements enlarge the button so that it
// becomes a circle rather than an oval.
Dimension size = getPreferredSize();
size.width = size.height = Math.max(size.width,
size.height);
setPreferredSize(size);
// This call causes the JButton not to paint
// the background.
// This allows us to paint a round background.
setContentAreaFilled(false);
}
// Paint the round background and label.
protected void paintComponent(Graphics g) {
if (getModel().isArmed()) {
// You might want to make the highlight color
// a property of the RoundButton class.
g.setColor(Color.lightGray);
} else {
g.setColor(getBackground());
}
g.fillOval(0, 0, getSize().width-1,
getSize().height-1);
// This call will paint the label and the
// focus rectangle.
super.paintComponent(g);
}
// Paint the border of the button using a simple stroke.
protected void paintBorder(Graphics g) {
g.setColor(getForeground());
g.drawOval(0, 0, getSize().width-1,
getSize().height-1);
}
// Hit detection.
Shape shape;
public boolean contains(int x, int y) {
// If the button has changed size,
// make a new shape object.
if (shape == null ||
!shape.getBounds().equals(getBounds())) {
shape = new Ellipse2D.Float(0, 0,
getWidth(), getHeight());
}
return shape.contains(x, y);
}
// Test routine.
public static void main(String[] args) {
// Create a button with the label "Jackpot".
JButton button = new RoundButton("Jackpot");
button.setBackground(Color.green);
// Create a frame in which to show the button.
JFrame frame = new JFrame();
frame.getContentPane().setBackground(Color.yellow);
frame.getContentPane().add(button);
frame.getContentPane().setLayout(new FlowLayout());
frame.setSize(150, 150);
frame.setVisible(true);
}
}
Option3- Use Look and Feel that supports Round Buttons http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/NimbusLookandFeel_OBE2012/CustomizingLandF.html
Option4- Go with JavaFX and use CSS. There are free CSS scripts supporting this
Found this great example by oracle which supplies a class which creates RoundButton.
Here is an example using an edited RoundButton class to create RoundedButton class:
import java.awt.AWTEvent;
import java.awt.AWTEventMulticaster;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
initComponents();
}
private void initComponents() {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField tf = new JTextField("");
RoundedButton rb = new RoundedButton("Go");
rb.setBackground(Color.yellow);
rb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(frame, "You said: " + tf.getText());
}
});
frame.add(tf, BorderLayout.NORTH);
frame.add(rb);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
}
class RoundedButton extends Component {
ActionListener actionListener; // Post action events to listeners
String label; // The Button's text
protected boolean pressed = false; // true if the button is detented.
/**
* Constructs a RoundedButton with no label.
*/
public RoundedButton() {
this("");
}
/**
* Constructs a RoundedButton with the specified label.
*
* #param label the label of the button
*/
public RoundedButton(String label) {
this.label = label;
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
/**
* gets the label
*
* #see setLabel
*/
public String getLabel() {
return label;
}
/**
* sets the label
*
* #see getLabel
*/
public void setLabel(String label) {
this.label = label;
invalidate();
repaint();
}
/**
* paints the RoundedButton
*/
#Override
public void paint(Graphics g) {
// paint the interior of the button
if (pressed) {
g.setColor(getBackground().darker().darker());
} else {
g.setColor(getBackground());
}
g.fillRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 20, 20);
// draw the perimeter of the button
g.setColor(getBackground().darker().darker().darker());
g.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 20, 20);
// draw the label centered in the button
Font f = getFont();
if (f != null) {
FontMetrics fm = getFontMetrics(getFont());
g.setColor(getForeground());
g.drawString(label, getWidth() / 2 - fm.stringWidth(label) / 2, getHeight() / 2 + fm.getMaxDescent());
}
}
/**
* The preferred size of the button.
*/
#Override
public Dimension getPreferredSize() {
Font f = getFont();
if (f != null) {
FontMetrics fm = getFontMetrics(getFont());
int max = Math.max(fm.stringWidth(label) + 40, fm.getHeight() + 40);
return new Dimension(max, max);
} else {
return new Dimension(100, 100);
}
}
/**
* The minimum size of the button.
*/
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
/**
* Adds the specified action listener to receive action events from this
* button.
*
* #param listener the action listener
*/
public void addActionListener(ActionListener listener) {
actionListener = AWTEventMulticaster.add(actionListener, listener);
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
/**
* Removes the specified action listener so it no longer receives action
* events from this button.
*
* #param listener the action listener
*/
public void removeActionListener(ActionListener listener) {
actionListener = AWTEventMulticaster.remove(actionListener, listener);
}
/**
* Determine if click was inside round button.
*/
#Override
public boolean contains(int x, int y) {
int mx = getSize().width / 2;
int my = getSize().height / 2;
return (((mx - x) * (mx - x) + (my - y) * (my - y)) <= mx * mx);
}
/**
* Paints the button and distribute an action event to all listeners.
*/
#Override
public void processMouseEvent(MouseEvent e) {
Graphics g;
switch (e.getID()) {
case MouseEvent.MOUSE_PRESSED:
// render myself inverted....
pressed = true;
// Repaint might flicker a bit. To avoid this, you can use
// double buffering (see the Gauge example).
repaint();
break;
case MouseEvent.MOUSE_RELEASED:
if (actionListener != null) {
actionListener.actionPerformed(new ActionEvent(
this, ActionEvent.ACTION_PERFORMED, label));
}
// render myself normal again
if (pressed == true) {
pressed = false;
// Repaint might flicker a bit. To avoid this, you can use
// double buffering (see the Gauge example).
repaint();
}
break;
case MouseEvent.MOUSE_ENTERED:
break;
case MouseEvent.MOUSE_EXITED:
if (pressed == true) {
// Cancel! Don't send action event.
pressed = false;
// Repaint might flicker a bit. To avoid this, you can use
// double buffering (see the Gauge example).
repaint();
// Note: for a more complete button implementation,
// you wouldn't want to cancel at this point, but
// rather detect when the mouse re-entered, and
// re-highlight the button. There are a few state
// issues that that you need to handle, which we leave
// this an an excercise for the reader (I always
// wanted to say that!)
}
break;
}
super.processMouseEvent(e);
}
}
Another clean way to make this happen is to define a custom ButtonUI that would draw a rounded corner button.

Keeping draw graphics - removing super.paintComponent

I have a class named Foo that extends a class named Bar that extends JPanel and implements ActionListener. When I select Circle and click the draw button, I draw a circle, and when I press rectangle and click draw, it erases the previous shape and draws a rectangle.
However, I want to keep all the shapes on the JPanel until I choose to click the erase button. So I removed the super.paintComponent(g) and it works, but it also causes buttons of class Bar to reappear in a glitchy manner. How can I stop the buttons from painting again?
I was thinking not to extend Bar and make Foo extend JPanel.
public class Bar extends JPanel implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if (e.getActionCommand() == "Draw")
{
this.requestDraw = true;
repaint();
}
if (e.getActionCommand() == "Circle")
{
requestRectangle = false;
requestTriangle = false;
requestCircle = true;
}
if (e.getActionCommand() == "Rectangle")
{
requestCircle = false;
requestTriangle = false;
requestRectangle = true;
}
if (e.getActionCommand() == "Right Triangle")
{
requestCircle = false;
requestRectangle = false;
requestTriangle = true;
}
}
public class Foo extends Bar
{
#Override
public void paintComponent(Graphics g)
{
//super.paintComponent(g);
if(RequestDraw())
{
if(RequestCircle())
circle.draw(g);
if(RequestRectangle())
rectangle.draw(g);
if(RequestTriangle())
rightTriangle.draw(g);
}
if(!RequestDraw())
{
g.setColor(getBackground());
g.fillRect(0,0,getWidth(), getHeight());
}
}
}
}
Along side all of Hovercraft's comments
The Graphics context is shared between components. One of the tasks of the super.paintComponent is to "clean" the graphics context before painting on it.
This is why you're seeing two version of your buttons...
I would also do a number of things differently. This should help with extendability and reuse over time, as well also reduce the logic a little.
I would...
Abstract the shapes to a basic "shape" class that has the minimum requirements, such as fill and stroke color, location, size, stroke, etc and know how to paint itself.
I would create a model of some kind that would allow you to separate and define the boundaries of responsibility. It's not the responsibility of the component to "manage" the shapes, it only cares about painting them on it's surface. Equally, the component doesn't care about what the "shape" is, it only wants to know about how they are to be painted...
I would use Actions to simply the creation of those shapes and adding them to the model...
I've only created a triangle shape (and it has no attributes beyond location and size), but I'm sure you'll get the general idea...(ps you'll need to supply your own triangle icon for the action ;))
public class DrawMe {
public static void main(String[] args) {
new DrawMe();
}
public DrawMe() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
DrawModel model = new DefaultDrawModel();
model.addElement(new Triangle(new Rectangle(10, 10, 100, 100)));
DrawPane drawPane = new DrawPane(model);
JToolBar toolBar = new JToolBar();
toolBar.add(new AddTriangleAction(model));
frame.add(toolBar, BorderLayout.NORTH);
frame.add(drawPane);
frame.setSize(400, 400);
frame.setVisible(true);
}
});
}
/**
* Simple action used to add triangles to the model...the model acts
* as a bridge between the action and the UI.
*/
protected class AddTriangleAction extends AbstractAction {
private DrawModel model;
public AddTriangleAction(DrawModel model) {
// Supply your own icon
putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/shape_triangle.png")));
this.model = model;
}
public DrawModel getModel() {
return model;
}
#Override
public void actionPerformed(ActionEvent e) {
// Randomly add the triangles...
int x = (int)(Math.random() * 400);
int y = (int)(Math.random() * 400);
model.addElement(new Triangle(new Rectangle(x, y, 100, 100)));
}
}
/**
* This is the background pane, from which the draw pane extends...
*/
protected class BackgroundPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = getWidth() / 2;
int y = getHeight() / 2;
Graphics2D g2d = (Graphics2D) g.create();
RadialGradientPaint rgp = new RadialGradientPaint(
new Point(x, y),
Math.max(getWidth(), getHeight()),
new float[]{0f, 1f},
new Color[]{Color.GRAY, Color.WHITE}
);
g2d.setPaint(rgp);
g2d.fill(new Rectangle(0, 0, getWidth(), getHeight()));
g2d.setBackground(Color.BLACK);
g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
g2d.dispose();
}
}
/**
* This is a simple model, I stole the list model because it was quicker
* and easier to demonstrate (don't need to write all the listeners)
*/
public interface DrawModel extends ListModel<DrawMeShape> {
public void addElement(DrawMeShape shape);
public void removeElement(DrawMeShape shape);
}
/**
* A default implementation of the DrawModel...
*/
public class DefaultDrawModel extends DefaultListModel<DrawMeShape> implements DrawModel {
#Override
public void removeElement(DrawMeShape shape) {
removeElement((Object)shape);
}
}
/**
* The actually "canvas" that shapes are rendered to
*/
protected class DrawPane extends BackgroundPane {
// Should provide ability to setModel...
private DrawModel model;
public DrawPane(DrawModel model) {
this.model = model;
model.addListDataListener(new ListDataListener() {
#Override
public void intervalAdded(ListDataEvent e) {
repaint();
}
#Override
public void intervalRemoved(ListDataEvent e) {
repaint();
}
#Override
public void contentsChanged(ListDataEvent e) {
repaint();
}
});
}
public DrawModel getModel() {
return model;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Draw the shapes from the model...
Graphics2D g2d = (Graphics2D) g.create();
DrawModel model = getModel();
for (int index = 0; index < model.getSize(); index++) {
DrawMeShape shape = model.getElementAt(index);
shape.paint(g2d, this);
}
g2d.dispose();
}
}
/**
* A abstract concept of a shape. Personally, if I was doing it, I would
* generate an interface first, but this is just a proof of concept...
*/
public abstract class DrawMeShape {
private Rectangle bounds;
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Rectangle getBounds() {
return bounds;
}
protected abstract Shape getShape();
/**
* The shape knows how to paint, but it needs to know what to paint...
* #param g2d
* #param parent
*/
public void paint(Graphics2D g2d, JComponent parent) {
g2d = (Graphics2D) g2d.create();
Rectangle bounds = getBounds();
Shape shape = getShape();
g2d.translate(bounds.x, bounds.y);
g2d.setColor(Color.DARK_GRAY);
g2d.fill(shape);
g2d.setColor(Color.BLACK);
g2d.draw(shape);
g2d.dispose();
}
}
/**
* An implementation of a Triangle shape...
*/
public class Triangle extends DrawMeShape {
public Triangle(Rectangle bounds) {
setBounds(bounds);
}
#Override
protected Shape getShape() {
// This should be cached ;)
Path2D path = new Path2D.Float();
Rectangle bounds = getBounds();
path.moveTo(bounds.width / 2, 0);
path.lineTo(bounds.width, bounds.height);
path.lineTo(0, bounds.height);
path.lineTo(bounds.width / 2, 0);
path.closePath();
return path;
}
}
}
Happy drawing...
Suggestions:
Don't remove super.paintComponent(g) as it has a necessary important role to play.
Instead why not draw to a BufferedImage and then draw that BufferedImage in the paintComponent(...) method override.
Then if you want to erase drawn images, simply create a new BufferedImage, or draw over it.
As an aside, don't compare Strings using ==. Use the equals(...) or the equalsIgnoreCase(...) method instead. Understand that == checks if the two objects are the same which is not what you're interested in. The methods on the other hand check if the two Strings have the same characters in the same order, and that's what matters here. So instead of
if (fu == "bar") {
// do something
}
do,
if ("bar".equals(fu)) {
// do something
}
or,
if ("bar".equalsIgnoreCase(fu)) {
// do something
}

Categories

Resources