I have a panel of buttons that represent an inventory, and in order to give emphasis to one button in particular I increased its border size. The border increases inward, however, which would be totally fine it didn't cover up the highlight effect buttons have when the mouse hovers over them. Is there any way to make the button highlight effect still show up in some way if it's covered up by the border? Maybe by increasing the highlight effect's border size?
This picture is an example of what I'm talking about. The top button is the emphasized button with the increased border size. The second button is a normal button being highlighted by the cursor(the mouse cursor doesn't show up in snip snapshots). As the picture shows, the increased border size is bigger than the highlight effect so when you try to highlight the first button seemingly nothing happens. The third/fourth are just normal buttons with no effect.
Sorry if the wording here is a little confusing but I'm not sure what the best way to phrase my probelm is.
Here is a simple example. The button with the red border no longer highlights when your mouse cursor hovers over it.
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
public class Test {
JFrame window;
JPanel screen1;
Border borderThick = BorderFactory.createLineBorder(Color.red, 3);
public Test(){
//Frame Window
window = new JFrame();
window.setSize(800,600);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().setBackground(Color.blue);
//Screen 1
screen1 = new JPanel();
screen1.setBounds(100, 100, 600, 205);
screen1.setBackground(Color.blue);
JButton buttonThickBorder = new JButton("Thick Button");
buttonThickBorder.setBorder(borderThick);
screen1.add(buttonThickBorder);
JButton buttonNormalButton = new JButton("NormalButton");
screen1.add(buttonNormalButton);
window.add(screen1);
window.setVisible(true);
}
public static void main(String[] args){
new Test();
}
}
JButton
s come with their own default Border
which is the one you are seeing by default (which comes with hover effects, press effects, etc). If you change the border to provide your own, then the default border is gone.
To fix this, one way is to implement your own border which will listen for hover events, which is easier done by listening to the JButton
's model changes, like so:
import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
public class Custom {
private static void createAndShowGUI() {
JFrame window;
JPanel screen1;
Border borderThick = BorderFactory.createLineBorder(Color.red, 3);
//Frame Window
window = new JFrame();
window.setSize(400,300);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().setBackground(Color.blue);
//Screen 1
screen1 = new JPanel();
screen1.setBounds(100, 100, 600, 205);
screen1.setBackground(Color.blue);
JButton buttonThickBorder = new JButton("Thick Button");
buttonThickBorder.setBorder(borderThick);
screen1.add(buttonThickBorder);
//Changes:
Border borderThickRollover = BorderFactory.createLineBorder(Color.CYAN, 3);
buttonThickBorder.getModel().addChangeListener(e -> {
final ButtonModel model = (ButtonModel) e.getSource();
if (model.isRollover())
buttonThickBorder.setBorder(borderThickRollover);
else
buttonThickBorder.setBorder(borderThick);
});
JButton buttonNormalButton = new JButton("NormalButton");
screen1.add(buttonNormalButton);
window.add(screen1);
window.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Custom::createAndShowGUI);
}
}
Similarly you can listen for other events, like the button being pressed, armed, selected or enabled (all those via the button's model) and change the border accordingly. For even more events, you can use listeners on the button itself (like FocusListener
for example).
One other way to fix the issue you are seeing is to use the default border itself, along with your custom border, by constructing a CompoundBorder
, for example like so:
import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
public class Default {
private static void createAndShowGUI() {
JFrame window;
JPanel screen1;
//Border borderThick = BorderFactory.createLineBorder(Color.red, 3);
//Frame Window
window = new JFrame();
window.setSize(400,300);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().setBackground(Color.blue);
//Screen 1
screen1 = new JPanel();
screen1.setBounds(100, 100, 600, 205);
screen1.setBackground(Color.blue);
JButton buttonThickBorder = new JButton("Thick Button");
//buttonThickBorder.setBorder(borderThick);
screen1.add(buttonThickBorder);
//Changes:
Border borderThickRollover = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.CYAN, 5), buttonThickBorder.getBorder());
buttonThickBorder.setBorder(borderThickRollover);
JButton buttonNormalButton = new JButton("NormalButton");
screen1.add(buttonNormalButton);
window.add(screen1);
window.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Default::createAndShowGUI);
}
}
This way you are getting the default border plus your extra border. This way you don't necessarily need to listen for model changes.
Notice, in the second example, that when you hover over the outside border of the compound one (ie the cyan sub-border) the button model changes to rolled over which makes the inside (default) border to be notified. This happens because the compound border is maintained by the button, so hovering the compound border anywhere will make the button's model rolled over. If this is not desired, then the simplest workaround I can think of right now is that you can just add the button inside a JPanel
which will have a cyan LineBorder
and install the button on that panel.
Generally speaking, you can obtain a JButton
's default Border
, without the need to actually construct a JButton
, through the UIManager
like so:
Border buttonDefaultBorder = javax.swing.UIManager.getBorder("Button.border");
...unless of course you install your own Look And Feel (L&F) which behaves differently, in which case you can obtain the default border after constructing the button, but that's a different story (and probably an erroneous L&F implementation).