Sliding Drawer animation on a JToggle selected state in Java? - java

I am trying to make a program in which, On Selecting JToggleButton shows a Sliding Drawer animated Panel and on deselecting it Hides it.
I want something like this on Clicking Toggle Button,
| Toggle button|---->|Panel|
Slides the Panel like a drawer
and on Deselecting Toggle Button
|Toggle Button|<-----|Hides panel|
I am able to create a new panel on Selecting a JToggle Button but i am confused with creating the animation.
myToggleButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent ev) {
if(ev.getStateChange()==ItemEvent.SELECTED){
myPanel.add(slidingPanel,BorderLayout.CENTER);
myPanel.revalidate();
myPanel.repaint();
} else if(ev.getStateChange()==ItemEvent.DESELECTED){
myPanel.remove(slidingPanel);
myPanel.revalidate();
myPanel.repaint();
}
}
});
How can i achieve sliding Drawer animation for a panel on a ToggleButton click and then hiding the panel .

I was able to figure out how to achieve it.I am writing the code for help.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SlidePanelDemo extends JPanel {
private JPanel pnlMain;
private JFrame frame;
private JPanel contentPane;
private static int drawWidth = 200;
private static int drawHeight = 100;
private Timer timer;
private int graphWidth = 5;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SlidePanelDemo().createAndShowGUI();
}
});
}
public void createAndShowGUI() {
JToggleButton button = new JToggleButton("Tools");
button.setSize(30, 30);
button.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent ev) {
if (ev.getStateChange() == ItemEvent.SELECTED) {
System.out.println("button is selected");
timer = new Timer(40, (ActionEvent e1) -> {
graphWidth = graphWidth + 5;
System.out.println("graphWidth is" + graphWidth);
pnlMain.setSize(graphWidth, drawHeight);
pnlMain.setVisible(true);
if (graphWidth >= 0 && graphWidth == drawWidth) {
timer.stop();
}
});
timer.setRepeats(true);
timer.start();
} else if (ev.getStateChange() == ItemEvent.DESELECTED) {
System.out.println("button is not selected");
timer = new Timer(40, (ActionEvent e1) -> {
graphWidth = graphWidth - 5;
System.out.println("graphWidth is" + graphWidth);
if (graphWidth > 0 && graphWidth <= drawWidth) {
pnlMain.setSize(graphWidth, drawHeight);
pnlMain.setVisible(true);
}
if (graphWidth == 5) {
timer.stop();
}
});
timer.setRepeats(true);
timer.start();
}
}
});
pnlMain = createMainPanel();
JButton label = new JButton("Click");
JButton label2 = new JButton("Click me");
pnlMain.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
pnlMain.add(label);
pnlMain.add(label2);
contentPane = new JPanel();
contentPane.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
contentPane.setOpaque(true);
contentPane.add(button);
contentPane.add(pnlMain);
pnlMain.setVisible(true);
JFrame.setDefaultLookAndFeelDecorated(true);
frame = new JFrame("Slide Out Panel Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(contentPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setSize(500, 300);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(graphWidth, drawHeight);
}
};
panel.setBorder(BorderFactory.createTitledBorder("Main"));
return panel;
}
}

Related

Why JPanel.focusGaind and Lost don't work?

Please take a look at the following code (I've missed the imports purposely)
public class MainFrame extends JFrame {
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainFrame frame = new MainFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public MainFrame() {
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);
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
tabbedPane.setBounds(10, 11, 414, 240);
contentPane.add(tabbedPane);
JPanel panel = new JPanel();
panel.addFocusListener(new FocusListener() {
#Override
public void focusLost(FocusEvent arg0) {
System.out.println("lost");
// I want to do something here, if I reach here!
}
#Override
public void focusGained(FocusEvent arg0) {
System.out.println("gained");
// I want to do something here, if I reach here!
}
});
tabbedPane.addTab("New tab", null, panel, null);
JButton button = new JButton("New button");
panel.add(button);
JPanel panel_1 = new JPanel();
tabbedPane.addTab("New tab", null, panel_1, null);
JPanel panel_2 = new JPanel();
tabbedPane.addTab("New tab", null, panel_2, null);
}
}
I've created this class to test it and then add the onFocusListener in my main code, but it's not working the way I expect. Please tell what's wrong or is this the right EvenetListener at all?
JPanels are not focusable by default. If you ever wanted to use a FocusListener on them, you'd first have to change this property via setFocusable(true).
But even if you do this, a FocusListener is not what you want.
Instead I'd look to listen to the JTabbedPane's model for changes. It uses a SingleSelectionModel, and you can add a ChangeListener to this model, listen for changes, check the component that is currently being displayed and if your component, react.
You are using setBounds and null layouts, something that you will want to avoid doing if you are planning on creating and maintaining anything more than a toy Swing program.
Edit
For example:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("serial")
public class MainPanel extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = 300;
private static final int GAP = 5;
private static final int TAB_COUNT = 5;
private JTabbedPane tabbedPane = new JTabbedPane();
public MainPanel() {
for (int i = 0; i < TAB_COUNT; i++) {
JPanel panel = new JPanel();
panel.add(new JButton("Button " + (i + 1)));
panel.setName("Panel " + (i + 1));
tabbedPane.add(panel.getName(), panel);
}
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout());
add(tabbedPane, BorderLayout.CENTER);
tabbedPane.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent evt) {
Component component = tabbedPane.getSelectedComponent();
System.out.println("Component Selected: " + component.getName());
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
MainPanel mainPanel = new MainPanel();
JFrame frame = new JFrame("MainPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
JPanel is a lightweight container and it is not a Actionable component so it does not get focus events. It lets you add focus listener because of swing component hierarchy. In Order to get tab selected events you need to use JTabbedPane#addChangeListener.
Hope this helps.

JPopupMenu change size dynamically if there is a JPanel inside

I have a JPopupMenu and I want to change it's inner size dynamically depending on it's inner components' size. In my SSCCE I described the problem.
SSCCE:
public class PopupTest2 {
public static void main(String[] a) {
final JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.RED));
final JPopupMenu menu = new JPopupMenu();
// critical step
JPanel itemPanel = new JPanel();
itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.Y_AXIS));
final JMenuItem[] items = new JMenuItem[10];
for (int i = 0; i < 10; i++) {
JMenuItem item = new JMenuItem("Item #"+String.valueOf(i));
itemPanel.add(item);
items[i] = item;
}
menu.add(itemPanel);
JToggleButton button = new JToggleButton("Press me");
button.addActionListener(new ActionListener() {
boolean pressed = false;
#Override
public void actionPerformed(ActionEvent e) {
pressed = !pressed;
if (pressed) {
for (JMenuItem item : items) {
item.setText(item.getText()+" changed");
}
} else {
for (JMenuItem item : items) {
item.setText(item.getText().substring(0, item.getText().length() - 8));
}
}
}
});
panel.add(button, BorderLayout.NORTH);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
menu.show(panel, (int) (e.getX() - menu.getPreferredSize().getWidth()), e.getY());
}
}
});
frame.setContentPane(panel);
frame.setUndecorated(true);
frame.setBackground(new Color(50, 50, 50, 200));
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.setVisible(true);
}
});
}
}
Steps 2 reproduce:
Right-click to show popup menu.
Click the Press me button (on the top of window).
Right-click to show popup menu again (bug #1 - popup is still small-size)
Right-click to show popup menu again (popup menu size is OK)
Click the Press me button again.
Right-click to show popup menu. (bug #2 - popup is still large-size)
How can I force JPopupMenu to change its size before showing? And why does it work if I add items directly to popupMenu?
Here is one way to do this:
public static void main(String[] a) {
final JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.RED));
final JPopupMenu menu = new JPopupMenu();
// critical step
final JPanel itemPanel = new JPanel();
itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.Y_AXIS));
final JMenuItem[] items = new JMenuItem[10];
for (int i = 0; i < 10; i++) {
JMenuItem item = new JMenuItem("Item #"+String.valueOf(i));
itemPanel.add(item);
items[i] = item;
}
menu.add(itemPanel);
JToggleButton button = new JToggleButton("Press me");
button.addActionListener(new ActionListener() {
boolean pressed = false;
#Override
public void actionPerformed(ActionEvent e) {
pressed = !pressed;
if (pressed) {
for (JMenuItem item : items) {
item.setText(item.getText()+" changed");
item.setMaximumSize(new Dimension(70, 50));
item.setPreferredSize(new Dimension(70, 50));
item.setMinimumSize(new Dimension(70, 50));
itemPanel.invalidate();
}
} else {
for (JMenuItem item : items) {
item.setText(item.getText().substring(0, item.getText().length() - 8));
item.setMaximumSize(new Dimension(130, 50));
item.setPreferredSize(new Dimension(130, 50));
item.setMinimumSize(new Dimension(130, 50));
itemPanel.invalidate();
}
}
}
});
panel.add(button, BorderLayout.NORTH);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
menu.show(panel, (int) (e.getX() - menu.getPreferredSize().getWidth()), e.getY());
}
}
});
frame.setContentPane(panel);
frame.setUndecorated(true);
frame.setBackground(new Color(50, 50, 50, 200));
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.setVisible(true);
}
});
}
Thanks all, but I've found the solution after continuous debugging. The problem was in layout used in JPanel (between Popup and items). It called the getPreferredSize() of JPanel which called directly menuItem.getPrefferedSize(), which called BasicMenuItemUI.getPrefferedSize(). The last method uses the MainMenuLayoutHelper class to get the preferred size. This class stores the data about size in Properties static object.
The default JPopupMenu layout - DefaultMenuLayout - clears this static data each time its preferredLayoutSize() method called, with call MenuItemLayoutHelper.clearUsedClientProperties(popupMenu). We will do the same - call this method with our JPanel parameter with further revalidate() call:
public class PopupTest2 {
public static void main(String[] a) {
final JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel(new BorderLayout());
final JPopupMenu menu = new JPopupMenu();
final JPanel itemPanel = new JPanel();
itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.Y_AXIS));
final JMenuItem[] items = new JMenuItem[1];
for (int i = 0; i < 1; i++) {
JMenuItem item = new JMenuItem("Item #"+String.valueOf(i));
itemPanel.add(item);
items[i] = item;
}
menu.updateUI();
menu.add(itemPanel);
JToggleButton button = new JToggleButton("Press me");
button.addActionListener(new ActionListener() {
boolean pressed = false;
#Override
public void actionPerformed(ActionEvent e) {
pressed = !pressed;
if (pressed) {
for (JMenuItem item : items) {
item.setText(item.getText()+" changed");
}
} else {
for (JMenuItem item : items) {
item.setText(item.getText().substring(0, item.getText().length() - 8));
}
}
}
});
panel.add(button, BorderLayout.NORTH);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
MenuItemLayoutHelper.clearUsedClientProperties(itemPanel);
itemPanel.revalidate();
menu.show(panel, (int) (e.getX() - menu.getPreferredSize().getWidth()), e.getY());
}
}
});
frame.setContentPane(panel);
frame.setUndecorated(true);
frame.setBackground(new Color(50, 50, 50, 200));
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.setVisible(true);
}
});
}
}
PS: Dear reader, it is not the best way to write your code. I do that because I really need that (I have such requirements). If you can build your JPopupMenu without JPanel inside, do not use code in this answer, please.
You have to add
menu.updateUI();
after add menu items.
final JMenuItem[] items = new JMenuItem[10];
for (int i = 0; i < 10; i++) {
JMenuItem item = new JMenuItem("Item #"+String.valueOf(i));
itemPanel.add(item);
item.setUI(new MyUI());
items[i] = item;
}
menu.updateUI(); <<<<<<--------
menu.add(itemPanel);

Wizard not redrawing/revalidating jframe

I'd like to create a wizard like app in java. I came to a point where I draw a frame with 3 different layouts, each containing a label and a 'next' button, but when I click the button on my first frame I see an empty frame instead.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class MainFrame extends JFrame implements ActionListener{
private BorderLayout layout;
public MainFrame() {
super("MyWizz");
setSize(500, 500);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setLocationRelativeTo(null);
}
protected ImageIcon createImageIcon(String path,
String description) {
java.net.URL imgURL = getClass().getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL, description);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
public void DrawFirstPage(){
layout = new BorderLayout();
setLayout(layout);
ImageIcon icon = createImageIcon("/images/icon.png","desc");
add(new JLabel("text",icon,JLabel.CENTER),BorderLayout.CENTER);
JButton bn = new JButton("next");
add(bn,BorderLayout.PAGE_END);
setVisible(true);
bn.setActionCommand("goto2");
bn.addActionListener(this);
revalidate();
}
public void DrawBrowsePage(){
System.out.println(getContentPane().getComponentCount());
getContentPane().removeAll();
System.out.println(getContentPane().getComponentCount());
repaint();
revalidate();
layout = new BorderLayout();
JButton bn = new JButton("next");
add(new JLabel("text",JLabel.CENTER),BorderLayout.CENTER);
add(bn,BorderLayout.PAGE_END);
bn.setActionCommand("goto3");
bn.addActionListener(this);
setLayout(layout);
setVisible(true);
System.out.println(getContentPane().getComponentCount());
repaint();
revalidate();
}
public void DrawLoadingPage(){
getContentPane().removeAll();
repaint();
revalidate();
layout = new BorderLayout();
setLayout(layout);
setVisible(true);
JButton bn = new JButton("next");
add(new JLabel("text",JLabel.CENTER),BorderLayout.CENTER);
add(bn,BorderLayout.PAGE_END);
repaint();
revalidate();
}
public void actionPerformed(ActionEvent e) {
if("goto2".equals(e.getActionCommand())) DrawBrowsePage();
if("goto3".equals(e.getActionCommand())) DrawLoadingPage();
}
}
From those few System.out.println functions, I can see, that DrawBrowsePage() executes, old elements are removed and new ones added. Would apprecieate any help you can spare.
FAQ: No. I can't use jwizz.
Instead of remove and add and then revalidate()/repaint(), you can simply use a CardLayout.
Short explanation of what I mean :
Simply create a stack or list (using Collections or Array) of JPanels, each representing different views. At the click of the next button, simply show the next JPanel by using cardLayout.show(container, "StringNameOfThePanelYouWantToShow"). More info can be found on CardLayout API, A small example for help, to see it functioning :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/* Here we are first declaring our class that will act as the
* base for other panels or in other terms the base for CardLayout.
*/
public class CardLayoutTest {
private static final String CARD_JBUTTON = "Card JButton";
private static final String CARD_JTEXTFIELD = "Card JTextField";
private static final String CARD_JRADIOBUTTON = "Card JRadioButton";
private static void createAndShowGUI() {
JFrame frame = new JFrame("Card Layout Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
// This JPanel is the base for CardLayout for other JPanels.
final JPanel contentPane = new JPanel();
contentPane.setLayout(new CardLayout(20, 20));
/* Here we be making objects of the Window Series classes
* so that, each one of them can be added to the JPanel
* having CardLayout.
*/
Window1 win1 = new Window1();
contentPane.add(win1, CARD_JBUTTON);
Window2 win2 = new Window2();
contentPane.add(win2, CARD_JTEXTFIELD);
Window3 win3 = new Window3();
contentPane.add(win3, CARD_JRADIOBUTTON);
/* We need two JButtons to go to the next Card
* or come back to the previous Card, as and when
* desired by the User.
*/
JPanel buttonPanel = new JPanel();
final JButton previousButton = new JButton("PREVIOUS");
previousButton.setBackground(Color.BLACK);
previousButton.setForeground(Color.WHITE);
final JButton nextButton = new JButton("NEXT");
nextButton.setBackground(Color.RED);
nextButton.setForeground(Color.WHITE);
buttonPanel.add(previousButton);
buttonPanel.add(nextButton);
/* Adding the ActionListeners to the JButton,
* so that the user can see the next Card or
* come back to the previous Card, as desired.
*/
previousButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
CardLayout cardLayout = (CardLayout) contentPane.getLayout();
cardLayout.previous(contentPane);
}
});
nextButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
CardLayout cardLayout = (CardLayout) contentPane.getLayout();
cardLayout.next(contentPane);
}
});
// Adding the contentPane (JPanel) and buttonPanel to JFrame.
frame.add(contentPane, BorderLayout.CENTER);
frame.add(buttonPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
class Window1 extends JPanel {
/*
* Here this is our first Card of CardLayout, which will
* be added to the contentPane object of JPanel, which
* has the LayoutManager set to CardLayout.
* This card consists of Two JButtons.
*/
private ActionListener action;
public Window1() {
init();
}
private void init() {
final JButton clickButton = new JButton("CLICK ME");
final JButton dontClickButton = new JButton("DON\'T CLICK ME");
action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == clickButton) {
JOptionPane.showMessageDialog(null, "Hello there dude!"
, "Right Button", JOptionPane.INFORMATION_MESSAGE);
} else if (ae.getSource() == dontClickButton) {
JOptionPane.showMessageDialog(null, "I told you not to click me!"
, "Wrong Button", JOptionPane.PLAIN_MESSAGE);
}
}
};
clickButton.addActionListener(action);
dontClickButton.addActionListener(action);
add(clickButton);
add(dontClickButton);
}
}
class Window2 extends JPanel implements ActionListener {
/*
* Here this is our second Card of CardLayout, which will
* be added to the contentPane object of JPanel, which
* has the LayoutManager set to CardLayout.
* This card consists of a JLabel and a JTextField
* with GridLayout.
*/
private JTextField textField;
public Window2() {
init();
}
private void init() {
setLayout(new GridLayout(1, 2));
JLabel userLabel = new JLabel("Your Name : ");
textField = new JTextField();
textField.addActionListener(this);
add(userLabel);
add(textField);
}
#Override
public void actionPerformed(ActionEvent e) {
if (textField.getDocument().getLength() > 0)
JOptionPane.showMessageDialog(null, "Your Name is : " + textField.getText()
, "User\'s Name : ", JOptionPane.QUESTION_MESSAGE);
}
}
class Window3 extends JPanel {
/*
* Here this is our third Card of CardLayout, which will
* be added to the contentPane object of JPanel, which
* has the LayoutManager set to CardLayout.
* This card consists of Two JLabels and two JCheckBox
* with GridLayout.
*/
private ActionListener state;
public Window3() {
init();
}
public void init() {
setLayout(new GridLayout(2, 2));
JLabel maleLabel = new JLabel("MALE", JLabel.CENTER);
final JCheckBox maleBox = new JCheckBox();
JLabel femaleLabel = new JLabel("FEMALE", JLabel.CENTER);
final JCheckBox femaleBox = new JCheckBox();
state = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
if (maleBox == (JCheckBox) ae.getSource()) {
femaleBox.setSelected(false);
JOptionPane.showMessageDialog(null, "Congrats you are a Male"
, "Gender : ", JOptionPane.INFORMATION_MESSAGE);
} else if (femaleBox == (JCheckBox) ae.getSource()) {
maleBox.setSelected(false);
JOptionPane.showMessageDialog(null, "Congrats you are a Female"
, "Gender : ", JOptionPane.INFORMATION_MESSAGE);
}
}
};
maleBox.addActionListener(state);
femaleBox.addActionListener(state);
add(maleLabel);
add(maleBox);
add(femaleLabel);
add(femaleBox);
}
}

Why JScrollBar doesn't respond to arrow keystrokes when coexist with editable component?

(Things will be clear in the code after description)
I have a program that contains JScrollBar, JTextArea, JTextFields, JButtons and some other things. (I only added SSCCE codes)
OK, I added key bindings for left and right JButtons of the JScrollBar , I used getInputMap, and getActionMap to achieve that.
If I have the JScrollBar in my GUI program without editable components such as JTextField or JTextArea, then the keyStrokes KeyEvent.VK_LEFT and KeyEvent.VK_RIGHT will work OK (as expected)
But if I have an editable component with the JScrollBar then it will not respond to KeyEvent.VK_LEFT and KeyEvent.VK_RIGHT (not expected) why does that happen!?
The more strange thing is that if I chose different keyStrokes to bind with such as KeyEvent.VK_S for left and KeyEvent.VK_F for right, it will work!! now why?
How to fix, how to make key bindings of left and right arrows for JScrollBar JButtons while editable component coexist?
These are two working codes in SSCCE style. The first contains JScrollBar without editable components, the second contains JScrollBar with JTextArea.
The first will work OK that is it will respond to left and right arrows of the keyboard. (it doesn't contain editable components)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ScrollTest extends JPanel
{
JPanel panel;
JScrollBar scrollBar;
JButton sliderLeftButton;
JButton sliderRightButton;
public ScrollTest()
{
scrollBar = new JScrollBar(JScrollBar.HORIZONTAL, 0, 6, 0, 300);
sliderLeftButton = (JButton) scrollBar.getAccessibleContext().getAccessibleChild(1);
sliderLeftButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "leftmove");
sliderLeftButton.getActionMap().put("leftmove", leftmove);
sliderRightButton = (JButton) scrollBar.getAccessibleContext().getAccessibleChild(0);
sliderRightButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "rightmove");
sliderRightButton.getActionMap().put("rightmove", rightMove);
panel = new JPanel(new GridLayout(2, 0));
panel.add(scrollBar);
this.setLayout(new BorderLayout());
this.add(panel, BorderLayout.NORTH);
}
AbstractAction leftmove = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent ae)
{
int increment = scrollBar.getBlockIncrement();
int current = scrollBar.getValue();
current -= increment;
scrollBar.setValue(current);
System.out.println("left");
}
};
AbstractAction rightMove = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent ae)
{
int increment = scrollBar.getBlockIncrement();
int current = scrollBar.getValue();
current += increment;
scrollBar.setValue(current);
System.out.println("right");
}
};
private static void createAndShowGUI()
{
JFrame frame;
frame = new JFrame("Scroll Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(880, 100);
frame.add(new ScrollTest(), BorderLayout.CENTER);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
}
The second will NOT work that is it will not respond to left and right arrows of the keyboard. (it does contain editable components - one JTextArea)
The more strange thing
Replace KeyEvent.VK_LEFT with KeyEvent.VK_S and replace KeyEvent.VK_RIGHT with KeyEvent.VK_F. Now S will work for left and F will work for right! why didn't it work with left and right arrows.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ScrollTest extends JPanel
{
JPanel panel;
JPanel panel2;
JScrollBar scrollBar;
JButton sliderLeftButton;
JButton sliderRightButton;
public ScrollTest()
{
scrollBar = new JScrollBar(JScrollBar.HORIZONTAL, 0, 6, 0, 300);
sliderLeftButton = (JButton) scrollBar.getAccessibleContext().getAccessibleChild(1);
sliderLeftButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "leftmove");
sliderLeftButton.getActionMap().put("leftmove", leftmove);
sliderRightButton = (JButton) scrollBar.getAccessibleContext().getAccessibleChild(0);
sliderRightButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "rightmove");
sliderRightButton.getActionMap().put("rightmove", rightMove);
panel = new JPanel(new GridLayout(2, 0));
panel.add(scrollBar);
panel2 = new JPanel(new GridLayout(1, 0));
panel2.add(new JTextArea(50, 10));
this.setLayout(new BorderLayout());
this.add(panel, BorderLayout.NORTH);
this.add(panel2, BorderLayout.SOUTH);
}
AbstractAction leftmove = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent ae)
{
int increment = scrollBar.getBlockIncrement();
int current = scrollBar.getValue();
current -= increment;
scrollBar.setValue(current);
System.out.println("left");
}
};
AbstractAction rightMove = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent ae)
{
int increment = scrollBar.getBlockIncrement();
int current = scrollBar.getValue();
current += increment;
scrollBar.setValue(current);
System.out.println("right");
}
};
private static void createAndShowGUI()
{
JFrame frame;
frame = new JFrame("Scroll Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(880, 100);
frame.add(new ScrollTest(), BorderLayout.CENTER);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
}
This is because JTextArea is stealing your key strokes:
textPane.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "pressed RIGHT");
textPane.getActionMap().put("pressed RIGHT", rightMove);
Have a look at BasicTextUI.installKeyboardActions()

Adding JPanels from other classes to the cardLayout

I've got 3 windows in 3 separate classes and I would like to use cardLayout so that when you click the next button, the next window will appear. How do I add JPanels containing different elements to one cardLayout? This is the first window: (the only difference is the background though - but it represents the idea of how I got it actually)
public class Window1 extends JPanel implements ActionListener {
static CardLayout cardLayout = new CardLayout();
public Window1() {
init();
}
private void init() {
JPanel jp = new JPanel(new BorderLayout());
JPanel jp2 = new Window2();
//JPanel jp3 = new Window3();
JLabel textLabel = new JLabel("Window1");
jp.setBackground(Color.GREEN);
jp.add(textLabel, BorderLayout.CENTER);
JButton nextButton = new JButton("NEXT");
nextButton.setActionCommand("next");
nextButton.addActionListener(this);
jp.add(nextButton, BorderLayout.EAST);
setLayout(cardLayout);
add(jp, "string");
add(jp2, "string");
//add(jp3, "string");
}
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equalsIgnoreCase("next")) {
// go to the next window
cardLayout.next(this);
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("test");
frame.getContentPane().setLayout(Window1.cardLayout);
frame.getContentPane().add(new Window1(), "Center");
frame.getContentPane().add(new Window2(), "Center");
frame.getContentPane().add(new Window3(), "Center");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(550, 450);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
The second window:
public class Window2 extends JPanel implements ActionListener {
//static CardLayout cardLayout = new CardLayout();
public Window2() {
init();
}
private void init() {
setLayout(new BorderLayout());
JLabel textLabel = new JLabel("Window2");
setBackground(Color.RED);
add(textLabel, BorderLayout.CENTER);
JButton nextButton = new JButton("NEXT");
nextButton.setActionCommand("next");
nextButton.addActionListener(this);
add(nextButton, BorderLayout.EAST);
//setLayout(cardLayout);
//JPanel jp3 = new Window3();
//add(jp3, "string");
}
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equalsIgnoreCase("next")) {
// go to the next window??
System.out.println("window2");
Window1.cardLayout.next(this);
}
}
}
And the last one:
public class Window3 extends JPanel implements ActionListener {
public Window3() {
init();
}
private void init() {
setLayout(new BorderLayout());
JLabel textLabel = new JLabel("Window3");
setBackground(Color.BLUE);
add(textLabel, BorderLayout.CENTER);
JButton nextButton = new JButton("NEXT");
nextButton.setActionCommand("next");
nextButton.addActionListener(this);
add(nextButton, BorderLayout.EAST);
}
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equalsIgnoreCase("next")) {
// go to the next window
// Window1.cardLayout.next(this);
}
}
}
I had made a small program, hopefully the comments written in the program, might be able to guide you, to understand how to use CardLayout.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/* Here we are first declaring our class that will act as the
* base for other panels or in other terms the base for CardLayout.
*/
public class CardLayoutTest
{
private static final String CARD_JBUTTON = "Card JButton";
private static final String CARD_JTEXTFIELD = "Card JTextField";
private static final String CARD_JRADIOBUTTON = "Card JRadioButton";
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Card Layout Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
// This JPanel is the base for CardLayout for other JPanels.
final JPanel contentPane = new JPanel();
contentPane.setLayout(new CardLayout(20, 20));
/* Here we be making objects of the Window Series classes
* so that, each one of them can be added to the JPanel
* having CardLayout.
*/
Window1 win1 = new Window1();
contentPane.add(win1, CARD_JBUTTON);
Window2 win2 = new Window2();
contentPane.add(win2, CARD_JTEXTFIELD);
Window3 win3 = new Window3();
contentPane.add(win3, CARD_JRADIOBUTTON);
/* We need two JButtons to go to the next Card
* or come back to the previous Card, as and when
* desired by the User.
*/
JPanel buttonPanel = new JPanel();
final JButton previousButton = new JButton("PREVIOUS");
previousButton.setBackground(Color.BLACK);
previousButton.setForeground(Color.WHITE);
final JButton nextButton = new JButton("NEXT");
nextButton.setBackground(Color.RED);
nextButton.setForeground(Color.WHITE);
buttonPanel.add(previousButton);
buttonPanel.add(nextButton);
/* Adding the ActionListeners to the JButton,
* so that the user can see the next Card or
* come back to the previous Card, as desired.
*/
previousButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
CardLayout cardLayout = (CardLayout) contentPane.getLayout();
cardLayout.previous(contentPane);
}
});
nextButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
CardLayout cardLayout = (CardLayout) contentPane.getLayout();
cardLayout.next(contentPane);
}
});
// Adding the contentPane (JPanel) and buttonPanel to JFrame.
frame.add(contentPane, BorderLayout.CENTER);
frame.add(buttonPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
class Window1 extends JPanel
{
/*
* Here this is our first Card of CardLayout, which will
* be added to the contentPane object of JPanel, which
* has the LayoutManager set to CardLayout.
* This card consists of Two JButtons.
*/
private ActionListener action;
public Window1()
{
init();
}
private void init()
{
final JButton clickButton = new JButton("CLICK ME");
final JButton dontClickButton = new JButton("DON\'T CLICK ME");
action = new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
if (ae.getSource() == clickButton)
{
JOptionPane.showMessageDialog(null, "Hello there dude!"
, "Right Button", JOptionPane.INFORMATION_MESSAGE);
}
else if (ae.getSource() == dontClickButton)
{
JOptionPane.showMessageDialog(null, "I told you not to click me!"
, "Wrong Button", JOptionPane.PLAIN_MESSAGE);
}
}
};
clickButton.addActionListener(action);
dontClickButton.addActionListener(action);
add(clickButton);
add(dontClickButton);
}
}
class Window2 extends JPanel implements ActionListener
{
/*
* Here this is our second Card of CardLayout, which will
* be added to the contentPane object of JPanel, which
* has the LayoutManager set to CardLayout.
* This card consists of a JLabel and a JTextField
* with GridLayout.
*/
private JTextField textField;
public Window2()
{
init();
}
private void init()
{
setLayout(new GridLayout(1, 2));
JLabel userLabel = new JLabel("Your Name : ");
textField = new JTextField();
textField.addActionListener(this);
add(userLabel);
add(textField);
}
public void actionPerformed(ActionEvent e)
{
if (textField.getDocument().getLength() > 0)
JOptionPane.showMessageDialog(null, "Your Name is : " + textField.getText()
, "User\'s Name : ", JOptionPane.QUESTION_MESSAGE);
}
}
class Window3 extends JPanel
{
/*
* Here this is our third Card of CardLayout, which will
* be added to the contentPane object of JPanel, which
* has the LayoutManager set to CardLayout.
* This card consists of Two JLabels and two JCheckBox
* with GridLayout.
*/
private ActionListener state;
public Window3()
{
init();
}
public void init()
{
setLayout(new GridLayout(2, 2));
JLabel maleLabel = new JLabel("MALE", JLabel.CENTER);
final JCheckBox maleBox = new JCheckBox();
JLabel femaleLabel = new JLabel("FEMALE", JLabel.CENTER);
final JCheckBox femaleBox = new JCheckBox();
state = new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
if (maleBox == (JCheckBox) ae.getSource())
{
femaleBox.setSelected(false);
JOptionPane.showMessageDialog(null, "Congrats you are a Male"
, "Gender : ", JOptionPane.INFORMATION_MESSAGE);
}
else if (femaleBox == (JCheckBox) ae.getSource())
{
maleBox.setSelected(false);
JOptionPane.showMessageDialog(null, "Congrats you are a Female"
, "Gender : ", JOptionPane.INFORMATION_MESSAGE);
}
}
};
maleBox.addActionListener(state);
femaleBox.addActionListener(state);
add(maleLabel);
add(maleBox);
add(femaleLabel);
add(femaleBox);
}
}
There are a couple things:
As your program can only run 1 main method, each class doesnt need one, but whichever you do run should create an instance of each panel you want to add to your CardLayout.
You also do not seem to add your Windows to your CardLayout at all. You could try the following (uncomplied). This would only be needed in one class:
private static void createAndShowGUI() {
JFrame frame = new JFrame("test");
frame.getContentPane().setLayout(Window1.cardLayout);
frame.getContentPane().add(new Window1());
frame.getContentPane().add(new Window2());
frame.getContentPane().add(new Window3());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(550, 450);
frame.setVisible(true);
}
Further (and this might just be due to the simplicity of your example), I would just have 1 class, and it would take the name of the panel and the color of the background to the constructor. These could be passed into your init() method and your design would be somewhat streamlined.

Categories