How to replace button shown on code with a filled circled.The circle (e.g. red) must move away from cursor just as same it was with button.
Is it another possible way to implement this type of task?
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class NewSwingTemplate {
private Random d = new Random();
public NewSwingTemplate() {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.add(createButton());
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JButton createButton() {
final JButton button = new JButton("Button");
button.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
int a = d.nextInt(200);
int b = d.nextInt(200);
button.setLocation(a, b);
}
});
return button;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new NewSwingTemplate();
}
});
}
}
Create your own button.
That is, you derive from JComponent and overwrite the paint method. It needs to render your circle - the way you want it. Add some keyboard/mouse handling and maybe an ActionListener to the circle can react similar to a JButton.
Finally you use it just like a JButton - I guess you have the code that makes your button move somewhere already.
Related
I am trying to implement hotkeys in a JFrame with a split pane that has a JTree on one of the panes. The key bindings work great, except that when a user is editing the name of a JTree node if they press a key that has a key binding the keystroke is typed in the text area and it triggers the key binding. Any ideas on how to allow the editing of nodes, while still implementing hotkeys?
Below is an example that demonstrates the behavior. The "1" and "2" keys are bound, so if you type either one in the text area, you'll see the popup.
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.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class KeyBindingTest {
static JButton button1;
static JButton button2;
static JPanel panel;
public KeyBindingTest() {
panel = new JPanel(new BorderLayout());
JToolBar tb = new JToolBar();
tb.setFloatable(false);
button1 = new JButton("First Button");
button1.addActionListener(new ButtonAction());
button2 = new JButton("Second Button");
button2.addActionListener(new ButtonAction());
tb.add(button1);
tb.add(button2);
panel.add(tb, BorderLayout.PAGE_START);
JTextArea ta = new JTextArea(10, 30);
JScrollPane opts = new JScrollPane(ta);
panel.add(opts, BorderLayout.PAGE_END);
setKeyBindings(tb);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
KeyBindingTest test = new KeyBindingTest();
test.createAndShowUI();
}
});
}
private void setKeyBindings(JToolBar tb) {
tb.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_1, 0), "first");
tb.getActionMap().put("first", new ButtonAction());
tb.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_2, 0), "second");
tb.getActionMap().put("second", new ButtonAction());
}
private void createAndShowUI() {
JFrame frame = new JFrame();
frame.getRootPane().setDefaultButton(button1);
frame.setLayout(new BorderLayout());
frame.add(panel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public class ButtonAction extends AbstractAction {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(panel, "You pressed a button \n" + e.paramString());
}
}
}
This is a reason why typically hotkeys have an Alt or Control modifier.
You can modify the Action to determine which component has focus:
#Override
public void actionPerformed(ActionEvent e)
{
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Component focusedComponent = kfm.getFocusOwner();
if (focusedComponent instanceof JTextArea)
return;
JOptionPane.showMessageDialog(panel, "You pressed a button \n"+e.paramString());
}
It's been a while since I've done Swing, but I think you can call consume() on the ActionEvent to prevent it from being passed to other listeners. I.e., if you call consume before the keystroke hits the other listeners, you can prevent the numbers from showing up in the field.
To clarify:
public class ButtonAction extends AbstractAction
{
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(panel, "You pressed a button \n"+e.paramString());
e.consume();
}
}
You might have to account for the order of the listeners though (like I said, it's been a while.)
I have written a custom glasspane component and set that to my JFrame and JDialog classes.
I don't intercept mouse events in the custom glasspane, as I don't need to and also because there are a number of components on the GUI e.g. trees, popups etc which makes it complicated trying to intercept and forward mouse events.
Everything works fine as is - except that whenever I call getMousePosition() on any of the other components (e.g. JPanel) in the frame/dialog it returns null.
On my JFrame and JDialog classes, I have set the glasspane to disabled (but visible) and it works for the most part. What do I need to do so the getMousePosition() method returns the correct value without having to add mouse event listeners to the glasspane component.
Sample code that demonstrates the problem.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GlassPaneExample
{
private JPanel panel;
private JButton btn;
private JLabel response;
private int counter = 0;
GlassPaneExample()
{
buildUI();
}
private void buildUI()
{
JFrame frame = new JFrame("GlassPane Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(getPanel());
frame.setGlassPane(new MyGlassPane());
frame.getGlassPane().setVisible(true);
frame.getGlassPane().setEnabled(false);
frame.setPreferredSize(new Dimension(200, 220));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
JPanel getPanel()
{
panel = new JPanel(new BorderLayout());
panel.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e)
{
}
#Override
public void mouseMoved(MouseEvent e)
{
System.out.println("mousePosition : " + panel.getMousePosition());
System.out.println("mousePosition(true) : " + panel.getMousePosition(true));
}
});
btn = new JButton("Click here...");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
response.setText("Button click number " + getButtonClickCount());
}
});
response = new JLabel();
response.setToolTipText("Response label");
panel.add(btn, BorderLayout.NORTH);
panel.add(response, BorderLayout.SOUTH);
return panel;
}
private int getButtonClickCount()
{
return counter++;
}
public static void main(String[] arg)
{
new GlassPaneExample();
}
class MyGlassPane extends JComponent
{
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(10,40,120,120);
}
}
}
If you comment out the 3 lines to do with adding the glasspane to the Frame, then the mouse position point is printed out correctly.
This is a simple example to illustrate the problem, and I've said above, I dont want to add mouse listeners to the custom glasspane component as it introduces other complications.
I have this class and I want to switch focus to the Game class right after it was invoked. I might've not understand the purpose of focus but when I press start I have to click on the game canvas itself so I can use the keyboard . In other words: How can I make it so I don't have to click on it to use the keyboard?
package com.runner.panels;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
import com.runner.main.Game;
import com.runner.main.Main;
public class PlayPanel extends JPanel{
private static final long serialVersionUID = 1L;
public PlayPanel(){
//setting the layout of the playpanel to null
setLayout(null);
//setting up the info panel : high score, meters ran, pause button etc...
JPanel info = new JPanel();
info.setBounds(0,0,1200,50);
add(info);
//back button
JButton back = new JButton("Back");
info.add(back);
Game game = new Game();
game.setBounds(0,50,1200,521);
game.setBackground(Color.black);
add(game);
back.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout) Main.mainp.getLayout();
cl.show(Main.mainp, "Menu");
}
});
}
}
Off topic: (kinda)
The fact that you are doing Main.mainp.getLayout();, calling the panel statically tells me you have poor design and should be looking into other options like an Model-view-controller pattern, an Observer pattern, or at the very least passing a reference of of the Main to the panel, instead of using static objects/calls.
Back on topic
Sounds like a common KeyListener problem. Generally to gain focus you call requestFocusInWindow(). But you still have to worry about other components stealing the focus way after the fact.
I would instead recommend using Key Bindings instead of KeyListener. You have more control over the focus. For instance, by using
InputMap im = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke("SPACE"), "hitSpace");
panel.getActionMap().put("hitSpace", new AbstractAction(){
public void actionPerformed(ActionEvent e) {
// do something.
}
});
The panel will have immediate access to the action once you show it from the CardLayout. If you happen to use any other components that would steal the focus away from the panel, the action is still accessible because of the WHEN_IN_FOCUSED_WINDOW input map
Here's a simple example. Type A if it is on panel A, you will see it print. If you type B, it won't print because panel A is in the window. Also if you try and press the button in the panel to steal the focus, you can still type and it will still print. Same goes for panel B
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Main {
CardLayout layout = new CardLayout();
JPanel panel = new JPanel(layout);
JPanel p1 = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
};
JPanel p2 = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
};
JButton b1 = new JButton("panelA");
JButton b2 = new JButton("panelB");
public Main() {
addKeyBind(p1, "pressA", "A");
addKeyBind(p2, "pressB", "B");
p1.add(new JButton("Button for Panel A"));
p2.add(new JButton("Button for Panel B"));
panel.add(p1, "panelA");
panel.add(p2, "panelB");
b1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
show("panelA");
}
});
b2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
show("panelB");
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(b1);
buttonPanel.add(b2);
JFrame frame = new JFrame();
frame.add(panel);
frame.add(buttonPanel, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void show(String panelName) {
layout.show(panel, panelName);
}
private void addKeyBind(JComponent comp, String name, final String stroke) {
InputMap im = comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(stroke), name);
comp.getActionMap().put(name, new AbstractAction(){
public void actionPerformed(ActionEvent e) {
System.out.println(stroke + " pressed");
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new Main();
}
});
}
}
Take some time to go over the link I gave you for Key Bindings to learn more.
Back off-topic
Take a look at #AndrewThompson's comment about the null layouts. Learn how to use the LayoutManagers
I believe your problem would be fixed if you add
setFocusable(true);
to your PlayPanel constructor (works for me).
Also, if you want a specific Panel in your GUI to have focus when you start your application, follow the link in the comment of "user3218114", as this will explain how to implement this functionality with Listeners.
Good luck!
You don't need to do anything. The container should be focusable for that.
Here is a code to demonstrate.
package one;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JFrame;
public class PlayPanel extends Canvas {
public static void main(String... args) {
PlayPanel p = new PlayPanel();
p.addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
p.msg = "Focus gained";
p.repaint();
}
#Override
public void focusLost(FocusEvent e) {
p.msg = "Focus Lost";
p.repaint();
}
});
p.setBackground(Color.GRAY);
JFrame f = new JFrame();
f.add(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 200);
f.setLocation(300, 300);
f.setVisible(true);
}
String msg = "NO FOCUS";
public void paint(Graphics g) {
g.drawString(msg, 50, 50);
}
}
In a linux text editor, Kate, there is this nice functionality that when I click and drag the scroll bar it shows the current line numbers that are currently in view in the text component.
My question is how can I add this function in Java to my scroll pane containing a JTextArea. Which component can I use to show this notification?
Apparently you can do this with a JPopupMenu:
I tried with this class because I knew it had the method show(Component, x, y). But it might be possible with other classes, or trying to implement whatever that method does yourself.
I put a couple of mouse listeners to the scrollBar and toyed a little with the values x, y in the show() call until I was satisfied with the position where it was being drawn at.
Full code:
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class ScrollbarTest
{
private JScrollPane scrollPane;
private JScrollBar scrollBar;
private JPopupMenu popupMenu;
private JLabel popupLabel;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
new ScrollbarTest();
}
});
}
public ScrollbarTest()
{
JFrame frame = new JFrame("Test");
popupMenu = new JPopupMenu();
popupLabel = new JLabel();
popupMenu.add(popupLabel);
scrollPane = new JScrollPane(buildTestTextArea());
scrollBar = scrollPane.getVerticalScrollBar();
scrollBar.addMouseMotionListener(new PopUpMouseMotionListener());
scrollBar.addMouseListener(new PopUpMouseListener());
frame.add(scrollPane);
frame.setSize(new Dimension(400, 400));
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public JTextArea buildTestTextArea()
{
JTextArea textArea = new JTextArea();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 10000; ++i)
{
builder.append("X");
}
textArea.setText(builder.toString());
textArea.setLineWrap(true);
return textArea;
}
private class PopUpMouseMotionListener extends MouseMotionAdapter
{
#Override
public void mouseDragged(MouseEvent e)
{
double value = scrollBar.getValue();
double max = scrollBar.getMaximum() - scrollBar.getVisibleAmount();
double h = scrollBar.getHeight();
popupLabel.setText("" + (int) (100*value/max) + "%");
popupMenu.show(scrollPane, scrollBar.getX() - popupMenu.getWidth() - 2, (int) ((h - popupMenu.getHeight())*value/max));
}
}
private class PopUpMouseListener extends MouseAdapter
{
#Override
public void mouseReleased(MouseEvent e)
{
popupMenu.setVisible(false);
}
}
}
I have a MainPanel which uses the Gridlayout. Consequently I have created four JPanel classes for the: NORTH, EAST, CENTER and EAST layouts respectively. I then add all four to my MainPanel.
However, on my WEST panel I use another grid layout to store JButtons and JTextFields. I want to constantly update my JTextFields as they display a value (that changes when a button on another panel is clicked). How do I allow that value to be changed when the JFrame is running?
I tried using paintComponent, but it keeps on adding multiple copies of the same JTextField after each other, as I add it in my paintComponent method. If I remove the add method the values won't update.
Action works well to encapsulate such functionality. In the example below, a number of text fields listen for an ActionEvent received from a single Update button. The common UpdateHandler is derived from AbstractAction.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
/** #see http://stackoverflow.com/a/14947144/230513 */
public class Test {
private JButton button = new JButton("Update");
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(createPanel(button), BorderLayout.NORTH);
f.add(createPanel(button), BorderLayout.WEST);
f.add(createPanel(button), BorderLayout.EAST);
f.add(createPanel(button), BorderLayout.SOUTH);
JPanel p = new JPanel();
p.add(button);
f.add(p, BorderLayout.CENTER);
f.getRootPane().setDefaultButton(button);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static JPanel createPanel(JButton b) {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER));
final JTextField text = new JTextField();
b.addActionListener(new UpdateHandler(text));
panel.add(text);
return panel;
}
private static class UpdateHandler extends AbstractAction {
private JTextField text;
private DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");
public UpdateHandler(JTextField t) {
super("update");
t.setText(df.format(new Date()));
this.text = t;
}
#Override
public void actionPerformed(ActionEvent e) {
text.setText(df.format(new Date()));
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
}