Try to use some "dark side" features of Swing and got problem when I try to populate the popup of a combo-box with some components. Here is my SSCCE:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormatSymbols;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.plaf.basic.ComboPopup;
public class ComboBoxTryout extends JComboBox {
private JPanel panel;
public ComboBoxTryout() {
initPanel();
}
private void initPanel() {
panel = new JPanel(new GridLayout(7, 1, 5, 5));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
DateFormatSymbols symbols = new DateFormatSymbols();
for (String s : symbols.getWeekdays()) {
if (s != null && !s.trim().isEmpty()) {
panel.add(createCheckBox(s));
}
}
setPopupComponent(this, panel);
}
private JCheckBox createCheckBox(String text) {
final JCheckBox cb = new JCheckBox(text);
cb.setHorizontalAlignment(SwingConstants.LEADING);
cb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
StringBuilder b = new StringBuilder();
for (Component c : panel.getComponents()) {
if (c instanceof JCheckBox && ((JCheckBox) c).isSelected()) {
if (b.length() > 0) {
b.append(", ");
}
b.append(((JCheckBox) c).getText().substring(0, 3));
}
}
getModel().setSelectedItem(b.toString());
}
});
return cb;
}
/**
* Sets the custom component as popup component for the combo-box.
*
* #param combo combo-box to get new popup component.
* #param comp new popup component.
*/
public static void setPopupComponent(JComboBox<?> combo, Component comp) {
final ComboPopup popup = (ComboPopup) combo.getUI().getAccessibleChild(combo, 0);
if (popup instanceof Container) {
Container c = (Container) popup;
c.removeAll();
c.setLayout(new GridLayout(1, 1));
c.add(comp);
c.setPreferredSize(comp.getPreferredSize());
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frm = new JFrame("Test");
frm.add(new ComboBoxTryout(), BorderLayout.NORTH);
frm.setSize(250, 600);
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
});
}
}
I can open the combo-box but when I click a check-box in the popup the combo-box goes closed and check-box is still not selected. But, when I change the line
frm.add(new ComboBoxTryout(), BorderLayout.NORTH);
to
frm.add(new ComboBoxTryout(), BorderLayout.SOUTH);
all works fine: I can change state of check-boxes and popup is still visible! Any suggestions, how to get it working for BorderLayout.NORTH?
The following doesn't use a regular JComboBox, but rather mimics one to give you more control.
This example is inspired from Creating Pop-Up Components and from your code.
It uses a Popup component and listeners on the button and on the textfield (textfield + button to look like a combobox, this part is not visually optimized in this example) to manage the popup hiding/showing.
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DateFormatSymbols;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SwingConstants;
public class ButtonPopupSample {
private final JTextField tField;
private final JButton start;
private final JPanel panel;
private boolean popping = false;
private Popup popup;
ButtonPopupSample() {
panel = new JPanel(new GridLayout(7, 1, 5, 5));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
DateFormatSymbols symbols = new DateFormatSymbols();
for (String s : symbols.getWeekdays()) {
if (s != null && !s.trim().isEmpty()) {
panel.add(createCheckBox(s));
}
}
JFrame frame = new JFrame("Button Popup Sample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tField = new JTextField(20);
tField.setEditable(false);
start = new JButton("Pop");
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
popOrNot();
}
};
start.addActionListener(actionListener);
tField.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent me) {
popOrNot();
}
});
JPanel contPan = new JPanel();
contPan.add(tField);
contPan.add(start);
frame.setContentPane(contPan);
frame.setSize(350, 250);
frame.setVisible(true);
}
private JCheckBox createCheckBox(final String text) {
final JCheckBox cb = new JCheckBox(text);
cb.setHorizontalAlignment(SwingConstants.LEADING);
cb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
StringBuilder b = new StringBuilder();
for (Component c : panel.getComponents()) {
if (c instanceof JCheckBox && ((JCheckBox) c).isSelected()) {
if (b.length() > 0) {
b.append(", ");
}
b.append(((JCheckBox) c).getText().substring(0, 3));
}
}
tField.setText(b.toString());
}
});
return cb;
}
private void popOrNot() {
if (popping) {
popup.hide();
} else {
PopupFactory factory = PopupFactory.getSharedInstance();
Point location = tField.getLocationOnScreen();
popup = factory.getPopup(tField, panel, location.x, location.y + tField.getHeight());
popup.show();
}
popping = !popping;
}
public static void main(final String args[]) {
new ButtonPopupSample();
}
}
Solution found. A strange focus change event provides the above described problem. So processing of this event must be prevented.
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DateFormatSymbols;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.plaf.basic.ComboPopup;
#SuppressWarnings("serial")
public class ComboBoxTryout extends JComboBox {
private JPanel panel;
private boolean avoidFocusChange;
/**
*
*/
public ComboBoxTryout() {
initPanel();
}
private void initPanel() {
panel = new JPanel(new GridLayout(7, 1, 5, 5));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
DateFormatSymbols symbols = new DateFormatSymbols();
for (String s : symbols.getWeekdays()) {
if (s != null && !s.trim().isEmpty()) {
panel.add(createCheckBox(s));
}
}
setPopupComponent(this, panel);
}
#Override
protected void processFocusEvent(FocusEvent e) {
if (avoidFocusChange && FocusEvent.FOCUS_LOST == e.getID() && panel.isAncestorOf(e.getOppositeComponent())) {
System.out.println(e); // skip it
} else {
super.processFocusEvent(e);
}
avoidFocusChange = false;
}
private JCheckBox createCheckBox(String text) {
final JCheckBox cb = new JCheckBox(text);
cb.setHorizontalAlignment(SwingConstants.LEADING);
cb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
StringBuilder b = new StringBuilder();
for (Component c : panel.getComponents()) {
if (c instanceof JCheckBox && ((JCheckBox) c).isSelected()) {
if (b.length() > 0) {
b.append(", ");
}
b.append(((JCheckBox) c).getText().substring(0, 3));
}
}
getModel().setSelectedItem(b.toString());
}
});
cb.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
avoidFocusChange = SwingUtilities.isLeftMouseButton(e);
}
});
return cb;
}
/**
* Sets the custom component as popup component for the combo-box.
*
* #param combo combo-box to get new popup component.
* #param comp new popup component.
*/
public static void setPopupComponent(JComboBox<?> combo, Component comp) {
final ComboPopup popup = (ComboPopup) combo.getUI().getAccessibleChild(combo, 0);
if (popup instanceof Container) {
Container c = (Container) popup;
c.removeAll();
c.setLayout(new GridLayout(1, 1));
c.add(comp);
Dimension dim = comp.getPreferredSize();
dim.width += 10; // need 10 px more width
c.setPreferredSize(dim);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frm = new JFrame("Test");
frm.add(new ComboBoxTryout(), BorderLayout.NORTH);
frm.setSize(250, 600);
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
});
}
}
If somebody finds a better solution, please post it!!!
Related
I'm writing a GUI using Swing. I have a custom written JComboBox using a ListCellRenderer and a BasicComboBoxEditor.
In my getListCellRendererComponent() method I change the color of the the list based on whether the item is "selected" (mouse is hovering above), which is nice and all, but I don't want the selection to change background color once a choice is made, which it currently does.
The first picture shows how the interface looks before a selection is made, and the second one shows how it looks after.
QUESTION
How do I change the background of the "selection" to the "stockColor"?
MCVE
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.util.Vector;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.plaf.basic.BasicComboBoxEditor;
public class TFComboBox extends JComboBox{
public static void main(String[] args){
createAndShowGUI();
}
public static void createAndShowGUI(){
JFrame frame = new JFrame("MCVE");
JPanel pane = new JPanel(new BorderLayout());
TFComboBox cb = new TFComboBox();
boolean[] tf = {true, false};
cb.addItems(tf);
JButton b = new JButton("Click me!");
pane.add(cb, BorderLayout.CENTER);
pane.add(b, BorderLayout.LINE_END);
frame.add(pane);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private DefaultComboBoxModel model;
private Vector<Boolean> comboBoxItems;
private JComboBox comboBox;
public TFComboBox(){
comboBoxItems = new Vector<Boolean>();
comboBoxItems.add(Boolean.TRUE);
comboBoxItems.add(Boolean.FALSE);
comboBox = new JComboBox(comboBoxItems);
model = new DefaultComboBoxModel();
setModel(model);
setRenderer(new TrueFalseComboRenderer());
setEditor(new TrueFalseComboEditor());
}
public void addItems(boolean[] items){
for(boolean anItem : items){
model.addElement(anItem);
}
}
}
class TrueFalseComboRenderer extends JPanel implements ListCellRenderer {
private JLabel labelItem = new JLabel();
private Color stockColor = labelItem.getBackground();
public TrueFalseComboRenderer(){
setLayout(new BorderLayout());
labelItem.setOpaque(true);
labelItem.setHorizontalAlignment(JLabel.CENTER);
add(labelItem);
setBackground(Color.LIGHT_GRAY);
}
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
boolean tempValue = (boolean) value;
labelItem.setText(Boolean.toString(tempValue));
if(isSelected){
labelItem.setBackground(stockColor.darker());
labelItem.setForeground(Color.WHITE);
} else {
labelItem.setBackground(stockColor);
labelItem.setForeground(Color.BLACK);
}
return this;
}
}
class TrueFalseComboEditor extends BasicComboBoxEditor {
private JLabel labelItem = new JLabel();
private JPanel panel = new JPanel();
private Object selectedItem;
public TrueFalseComboEditor() {
labelItem.setOpaque(false);
labelItem.setHorizontalAlignment(JLabel.CENTER);
labelItem.setForeground(Color.WHITE);
panel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 2));
panel.setBackground(Color.BLUE);
panel.add(labelItem);
}
public Component getEditorComponent(){
return this.panel;
}
public Object getItem(){
return this.selectedItem;
}
public void setItem(Object item){
if(item == null){
return;
}
this.selectedItem = item;
labelItem.setText(item.toString());
}
}
EDIT
I've added a MCVE and as you can see it is the "problem" that the JComboBox is focused that has to do with my issue. I've placed a button next to the ComboBox to help with removing the focus from the ComboBox.
Simply doing a setFocusable(false) would fix it, but also take away some of the functionality of the rest of the program, so this is not desired.
for better help sooner post an SSCCE / MCVE, short, runnable, compilable, with hardcoded value for JComboBox / XxxComboBoxModel in local variable
JList has Boolean array implemented as default in API (no idea whats hidden in
String trueFalseItem = Boolean.toString(tempValue); and with value stored JComboBox model)
this is just code minimum to test isSelected and to change JList.setSelectionXxx inside DefaultListCellRenderer
for example (code in SSCCE / MCVE form)
.
.
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.SwingUtilities;
public class ComboBoxBooleanModel {
private javax.swing.Timer timer = null;
private Vector<Boolean> comboBoxItems;
private JComboBox box;
public ComboBoxBooleanModel() {
comboBoxItems = new Vector<Boolean>();
comboBoxItems.add(Boolean.TRUE);
comboBoxItems.add(Boolean.FALSE);
box = new JComboBox(comboBoxItems);
box.setRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
Component c = super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
if (c instanceof JLabel) {
JLabel l = (JLabel) c;
if (Boolean.TRUE.equals(value)) {
l.setBackground(Color.RED);
if (isSelected) {
list.setSelectionForeground(Color.RED);
list.setSelectionBackground(Color.BLUE);
}
} else if (Boolean.FALSE.equals(value)) {
l.setBackground(Color.BLUE);
if (isSelected) {
list.setSelectionForeground(Color.BLUE);
list.setSelectionBackground(Color.RED);
}
}
return l;
}
return c;
}
});
JFrame frame = new JFrame("");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(box);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
box.setSelectedIndex(1);
}
});
start();
}
private void start() {
timer = new javax.swing.Timer(2250, updateCol());
timer.start();
}
public Action updateCol() {
return new AbstractAction("text load action") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
if (box.getSelectedItem() == (Boolean) false) {
box.setSelectedItem((Boolean) true);
} else {
box.setSelectedItem((Boolean) false);
}
}
};
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ComboBoxBooleanModel comboBoxModel = new ComboBoxBooleanModel();
}
});
}
}
Here is a short demo of 2 JCombos, one of which will not change its background color when selected :
public static void main(String[] args){
JFrame frame = new JFrame("Combos BG Color test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.getContentPane().setPreferredSize(new Dimension(400, 40));
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout(1,2));
frame.add(mainPanel);
JComboBox<String> aCombo = new JComboBox<>(new String[]{"A","B","C"});
mainPanel.add(aCombo);
JComboBox<String> bCombo = new JComboBox<>(new String[]{"1","2","3"});
Color bgColor = bCombo.getBackground();
bCombo.setRenderer(new DefaultListCellRenderer() {
#Override
public void paint(Graphics g) {
setBackground(bgColor);
super.paint(g);
}
});
mainPanel.add(bCombo);
frame.pack();
frame.setVisible(true);
}
(Most of the credit goes to this answer)
I have a problem with comboboxes which display list of buttons. What I have now is
http://hizliresim.com/djQzr7
However I want something more like this
http://hizliresim.com/QXDE3G
First button is combobox and the second one is that combobox when it is clicked.
Here is the code
package asd;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.JComboBox;
public class asd extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
asd frame = new asd();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public asd() {
try {
// Set System L&F
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
}
catch (UnsupportedLookAndFeelException e) {
// handle exception
}
catch (ClassNotFoundException e) {
// handle exception
}
catch (InstantiationException e) {
// handle exception
}
catch (IllegalAccessException e) {
// handle exception
}
ComboBoxRender renderer;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JComboBox comboBox = new JComboBox();
comboBox.setBounds(92, 85, 60, 40);
renderer = new ComboBoxRender();
comboBox.setRenderer(renderer);
comboBox.addItem("1");
comboBox.addItem("2");
comboBox.addItem("3");
comboBox.setPreferredSize(new Dimension(60, 40));
comboBox.setMinimumSize(new Dimension(70, 30));
comboBox.setMaximumSize(new Dimension(500, 500));
comboBox.setBackground(Color.GRAY);
comboBox.setUI(new javax.swing.plaf.metal.MetalComboBoxUI(){
public void layoutComboBox(Container parent, MetalComboBoxLayoutManager manager) {
super.layoutComboBox(parent, manager);
arrowButton.setBounds(0,0,0,0);
}
});
contentPane.add(comboBox);
}
}
class ComboBoxRender implements ListCellRenderer<Object> {
#Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index,boolean isSelected, boolean cellHasFocus) {
JButton btn = new JButton(value.toString());
btn.setSelected(false);
btn.setBorderPainted(true);
btn.setBackground(Color.gray);
btn.setSize(new Dimension(100, 100));
btn.setMargin(new Insets(1, 1, 1, 1));
btn.setMinimumSize(new Dimension(200,200));
return btn;
}
}
For that you need to use ListCellRenderer. Read about custom renderers.
For example :
import java.awt.Component;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
public class TestFrame extends JFrame {
public TestFrame() {
init();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void init() {
JComboBox<String> box = new JComboBox<>(new String[]{"1","2"});
box.setRenderer(getRenderer());
add(box);
}
private ListCellRenderer<? super String> getRenderer() {
return new ListCellRenderer<String>() {
private JButton btn = new JButton();
public Component getListCellRendererComponent(JList<? extends String> list,String value, int index,
boolean isSelected, boolean cellHasFocus) {
btn.setText(value);
return btn;
};
};
}
public static void main(String args[]) {
new TestFrame();
}
}
I have 4 JPanels. In one of the panel I have a combo Box.Upon selecting "Value A" in combo box Panel2 should be displayed.Similarly if I select "Value B" Panel3 should be selected....
Though action Listener should be used in this context.How to make a call to another tab with in that action listener.
public class SearchComponent
{
....
.
public SearchAddComponent(....)
{
panel = addDropDown(panelList(), "panel", gridbag, h6Box);
panel.addComponentListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ItemSelectable is = (ItemSelectable)actionEvent.getSource();
Object name=selectedString(is);
}
});
}
public static final Vector<String> panelList(){
List<String> panelList = new ArrayList<String>();
panelList.add("A");
panelList.add("B");
panelList.add("C");
panelList.add("D");
panelList.add("E");
panelList.add("F);
Vector<String> panelVector = null;
Collections.copy(panelVector, panelList);
return panelVector;
}
public Object selectedString(ItemSelectable is) {
Object selected[] = is.getSelectedObjects();
return ((selected.length == 0) ? "null" : (ComboItem)selected[0]);
}
}
Use a Card Layout. See the Swing tutorial on How to Use a Card Layout for a working example.
Try This code:
import java.awt.EventQueue;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.border.Border;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JComboBox;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import java.awt.Container;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class CardLayoutExample {
JFrame guiFrame;
CardLayout cards;
JPanel cardPanel;
public static void main(String[] args) {
//Use the event dispatch thread for Swing components
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
new CardLayoutExample();
}
});
}
public CardLayoutExample()
{
guiFrame = new JFrame();
//make sure the program exits when the frame closes
guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
guiFrame.setTitle("CardLayout Example");
guiFrame.setSize(400,300);
//This will center the JFrame in the middle of the screen
guiFrame.setLocationRelativeTo(null);
guiFrame.setLayout(new BorderLayout());
//creating a border to highlight the JPanel areas
Border outline = BorderFactory.createLineBorder(Color.black);
JPanel tabsPanel = new JPanel();
tabsPanel.setBorder(outline);
JButton switchCards = new JButton("Switch Card");
switchCards.setActionCommand("Switch Card");
switchCards.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent event)
{
cards.next(cardPanel);
}
});
tabsPanel.add(switchCards);
guiFrame.add(tabsPanel,BorderLayout.NORTH);
cards = new CardLayout();
cardPanel = new JPanel();
cardPanel.setLayout(cards);
cards.show(cardPanel, "Fruits");
JPanel firstCard = new JPanel();
firstCard.setBackground(Color.GREEN);
addButton(firstCard, "APPLES");
addButton(firstCard, "ORANGES");
addButton(firstCard, "BANANAS");
JPanel secondCard = new JPanel();
secondCard.setBackground(Color.BLUE);
addButton(secondCard, "LEEKS");
addButton(secondCard, "TOMATOES");
addButton(secondCard, "PEAS");
cardPanel.add(firstCard, "Fruits");
cardPanel.add(secondCard, "Veggies");
guiFrame.add(tabsPanel,BorderLayout.NORTH);
guiFrame.add(cardPanel,BorderLayout.CENTER);
guiFrame.setVisible(true);
}
//All the buttons are following the same pattern
//so create them all in one place.
private void addButton(Container parent, String name)
{
JButton but = new JButton(name);
but.setActionCommand(name);
parent.add(but);
}
}
I am trying to update the scrollbar on the scrollpane automatically when going up and down in my custom made popupmenu. But it doesn´t work. Have a look at my fully runnable example below where I have added my custom JPopupMenu based component and a JComboBox to compare with. As you can see, in the JComboBox the scrollpane updates its position so the currently selected item always is visible. But that does not work with my component and I don´t know how to set that.
My SSCCE:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.ScrollPane;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class ScrollFocus extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ScrollFocus();
}
});
}
public ScrollFocus() {
this.setLayout(new BorderLayout());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Vector<String> values = new Vector<>();
values.add("a");
values.add("b");
values.add("c");
values.add("d");
values.add("e");
values.add("f");
values.add("g");
values.add("h");
values.add("i");
values.add("j");
JComboBox<String> comboBox = new JComboBox<>(values);
JScrollPane scrollPane = new JScrollPane(comboBox);
this.add(scrollPane, BorderLayout.NORTH);
CustomTextField customTextField = new CustomTextField();
this.add(customTextField, BorderLayout.CENTER);
pack();
setVisible(true);
}
class CustomTextField extends JTextField implements FocusListener {
private CustomPopup customPopup = new CustomPopup();
public CustomTextField() {
this.addFocusListener(this);
this.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
System.out.println("printing...");
setPopupSize();
setPosition();
customPopup.setVisible(true);
if(e.getKeyCode() == KeyEvent.VK_UP) {
if(customPopup.index < customPopup.getListSize())
customPopup.setSelectedIndex(customPopup.index--);
}
else if(e.getKeyCode() == KeyEvent.VK_DOWN){
if(customPopup.index >= 0)
customPopup.setSelectedIndex(customPopup.index++);
}
}
});
}
public void setPopupSize() {
customPopup.setPopupSize(new Dimension(this.getWidth(), 110));
}
public void setPosition() {
customPopup.setLocation(this.getLocation().x, this.getLocation().y);
}
#Override
public void focusGained(FocusEvent e) {
}
#Override
public void focusLost(FocusEvent e) {
customPopup.setVisible(false);
}
class CustomPopup extends JPopupMenu {
String[] values = new String[]{"Value1", "Value2", "Value3", "Value4", "Value5", "Value6", "Value7",
"Value8","Value9", "Value10", "Value11", "Value12", "Value13", "Value14", "Value15", "Value16",};
JList<String> list = new JList<>(values);
JScrollPane scrollPane = new JScrollPane(list);
public int index = 0;
public CustomPopup() {
this.setLayout(new GridLayout(0,1));
this.add(scrollPane);
pack();
}
public void setSelectedIndex(int index) {
list.setSelectedIndex(index);
}
public int getListSize() {
return values.length;
}
}
}
}
in the JComboBox the scrollpane updates its position so the currently selected item always is visible. But that does not work with my component and I don´t know how to set that.
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index); // added
Also, do not use a KeyListener. Swing was designed to be used with Key Bindings. Read thw section from the Swing tutorial on How to Use Key Bindings for more information.
I have created a JButton class that recieving Action, the JButton class includes keystrokes & mouse listener so i can use the same class in multiple frames as needed.
My problems is that:
JButton not getting the focus when pressing the key, but it doing the action.
i need to make a new background or something that tell the user that the button did the action.
Any ideas??
Here is my code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.border.LineBorder;
import swtdesigner.SwingResourceManager;
public class IButtonSave extends JButton{
private static final long serialVersionUID = 1L;
private Action action = null;
public IButtonSave() {
super();
setFocusPainted(true);
setFocusable(true);
try {
jbInit();
} catch (Throwable e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
setMargin(new Insets(0, 0, 0, 0));
setBorder(new LineBorder(Color.black, 1, true));
setIconTextGap(0);
setHorizontalTextPosition(SwingConstants.CENTER);
setVerticalTextPosition(SwingConstants.TOP);
setPreferredSize(new Dimension(50, 43));
setMinimumSize(new Dimension(50, 43));
setMaximumSize(new Dimension(50, 43));
addMouseListener(new ThisMouseListener());
setVerifyInputWhenFocusTarget(true);
}
public void setAction(Action a){
action = a;
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F1,0,true);
KeyStroke ks2 = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0);
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, "Save");
getInputMap(JComponent.WHEN_FOCUSED).put(ks2, "Save");
getActionMap().put("Save", a);
setText("Save [F1]");
setIcon(SwingResourceManager.getIcon(SwingResourceManager.class, "/images/small/save.png"));
setToolTipText("[F1]");
}
private class ThisMouseListener extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
this_mouseClicked(e);
}
}
protected void this_mouseClicked(MouseEvent e) {
if(e.getClickCount() >= 1){
action.actionPerformed(null);
}
}
}
Why extend JButton class when you can simply add KeyBindings to its instance?
Not to sure what you want but this works fine for me:
Basically a JButton which can be activated by mouse click, or pressing F1 (as long as focus is in window and if pressed will shift focus to JButton) or ENTER (only when in focus of JButton).
When the AbstractAction is called it will call requestFocusInWindow() on JButton (thus pressing F1 will make button gain focus which is what I think you wanted):
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JButton btn = new JButton("Button");
AbstractAction aa = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
System.out.println("Here");
btn.requestFocusInWindow();//request that the button has focus
}
};
//so button can be pressed using F1 and ENTER
btn.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter");
btn.getActionMap().put("Enter", aa);
btn.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), "F1");
btn.getActionMap().put("F1", aa);
btn.addActionListener(aa);//so button can be clicked
JTextField tf = new JTextField("added to show ENTER wont work unless button in focus");
frame.add(tf);
frame.add(btn, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
});
}
}
UPADTE:
alternatively as #GuillaumePolet suggested (+1 to him) override processKeyBinding of JButton and check for appropriate key and than call the method:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JButton btn = new JButton("Button") {
#Override
protected boolean processKeyBinding(KeyStroke ks, KeyEvent ke, int i, boolean bln) {
boolean b = super.processKeyBinding(ks, ke, i, bln);
if (b && ks.getKeyCode() == KeyEvent.VK_F1) {
requestFocusInWindow();
}
return b;
}
};
AbstractAction aa = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
System.out.println("Here");
}
};
//so button can be pressed using F1 and ENTER
btn.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter");
btn.getActionMap().put("Enter", aa);
btn.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), "F1");
btn.getActionMap().put("F1", aa);
btn.addActionListener(aa);//so button can be clicked
JTextField tf = new JTextField("added to show ENTER wont work unless button in focus");
frame.add(tf);
frame.add(btn, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
});
}
}
Not sure this is the best way to do it, but it works decently enough and if it is not the best way, you get a free SSCCE out of this answer.
I override processKeyBindings() and if it returns true then I grab the focus to indicate that the action has been performed. If you want to do something else you just need to modify the code there.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class IButtonSave extends JButton {
private static final long serialVersionUID = 1L;
public IButtonSave() {
super();
setFocusPainted(true);
setFocusable(true);
}
#Override
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
boolean processKeyBinding = super.processKeyBinding(ks, e, condition, pressed);
if (processKeyBinding) {
requestFocusInWindow();
}
return processKeyBinding;
}
#Override
public void setAction(Action a) {
super.setAction(a);
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0, true);
KeyStroke ks2 = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, "Save");
getInputMap(JComponent.WHEN_FOCUSED).put(ks2, "Save");
getActionMap().put("Save", a);
setText("Save [F1]");
setToolTipText("[F1]");
}
protected void initUI() {
JFrame frame = new JFrame(IButtonSave.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextArea textarea = new JTextArea(5, 30);
JPanel buttonPanel = new JPanel();
AbstractAction someAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.err.println("Action performed");
}
};
IButtonSave button = new IButtonSave();
button.setAction(someAction);
buttonPanel.add(button);
frame.add(new JScrollPane(textarea));
frame.add(buttonPanel, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new IButtonSave().initUI();
}
});
}
}