JPanel doesn't want to focus so KeyAdapter doesn't work - java

Background
I had a simple game. I had one JPanel class and there were every thing (menu, game, game end).
Then I decided, that I should make my game better and I made two panels(one for menu, and second for game lvls).
Every thing were good, but my KeyAdapter class doesn't work at my JPanel. I don't know why it doesn't want to focus.
There is what I have:
Main class which extends JFrame and here I add my panels (and KeyListener to first panel)
public class JavaGame2 extends JFrame {
public JavaGame2(){
gamePanel = new GamePanel();
menuPanel = new MenuPanel();
setContentPane(menuPanel);
menuPanel.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
menuPanel.changeCursor();
}
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
menuPanel.changeCursor();
}
if (menuPanel.getCursorPos()==1){
if ((e.getKeyCode() == KeyEvent.VK_ENTER)) {
setContentPane(gamePanel);
//add(gamePanel);
}
}
else{
if ((e.getKeyCode() == KeyEvent.VK_ENTER)) {
System.exit(0);
}
}
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
setLocationRelativeTo(null);
setTitle("JavaGame2");
setResizable(false);
}
public static void main(String[] args) {
JFrame jgame = new JavaGame2();
jgame.setVisible(true);
}
}
MenuPanel class extends JPanel
public class MenuPanel extends JPanel implements ActionListener{
public MenuPanel(){
setFocusable(true);
setBackground(Color.BLACK);
setDoubleBuffered(true);
setSize(800,600);
}
public void actionPerformed(ActionEvent e) {
}
}
And here GamePanel class
public class GamePanel extends JPanel implements ActionListener{
public GamePanel (){
addKeyListener(new GameAdapter());
setFocusable(true);
requestFocus();
setBackground(Color.BLACK);
setDoubleBuffered(true);
setSize(800,600);
}
private class GameAdapter extends KeyAdapter{
public void keyReleased(KeyEvent e) {
ship.keyReleased(e);
}
public void keyPressed(KeyEvent e) {
ship.keyPressed(e);
}
}
}
It doesn't work. GamePanel don't want to focus, I tried to do every thing that I read.
I think u will say that JPanel is not focusable component. But when there was one panel it somehow worked.
How can I fix this focus problem?
Maybe u will say that u prefer don't use KeyAdapter, but I think it looks pretty nice in my code.
setFocusable()? or requestFocus()? requestFocusInWindow()? How should I use them? Maybe I have mistake before and this is not my first problem?
Thanks in advance.
And Thanks for editing.

Short answer, use the key bindings API, it allows you to control the level of focus that a component requires before key events are triggered.
Longer answer, the active panel needs to have keyboard focus. You can use requestFocusInWindow, but there is no guarantee that the component will actually receive focus. When to call this is a tricky thing. You could try overriding addNotify of the panels and calling to there, just make sure you call super.addNotify first, weird things happen when you don't
You will also need to consider what will happen if the component loses focus
As a side note:
setDoubleBuffered(true); is irrelevant, as Swing components are double buffered by default. Generally you might disable this if you wanted to print the component. No harm in calling it though
Calling setSize on your components is irrelevant, as you components will be under the control of a layout manager, which will determine the size of the component itself. You'd be better off overriding getPreferredSize and returning an appropriate size for the layout manager
Calling setSize on JFrame is also a bad idea. Frames have borders, this means that your viewable area will be the frame size - the frames border insets, which will be less the 800x600 you've specified. Better to utilise the previous comment and call pack on the JFrame, which will pack the frame around the content so that it meets the contents requirements...
Personally, I would also localise the KeyListener to the actually component itself, this allows the component to act as it's own controller making it more portable...IMHO
Updated with controller idea...
A "really" simplified concept would be to have some kind of "controller" that the menu and game panel could communicate through, for example...
public interface GameController {
public void showMenu();
public void showGame();
}
You would then pass a reference of this interface to the MenuPanel...
public class MenuPanel extends JPanel implements ActionListener{
public MenuPanel(GameController controller){
//...
}
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
And the GamePanel...
public class GamePanel extends JPanel implements ActionListener{
public GamePanel (GameController controller){
//...
}
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
You would, obviously, need to construct a implementation of the controller...
public class CardLayoutGameController implements GameController {
public static final String GAME_PANEL = "GamePanel";
public static final String MENU_PANEL = "MenuPanel";
private Container container;
private CardLayout cardLayout;
public CardLayoutGameController(Container parent, CardLayout layout) {
container = parent;
cardLayout = layout;
}
public void showMenu() {
cardLayout.show(container, MENU_PANEL);
}
public void showGame() {
cardLayout.show(container, GAME_PANEL);
}
}
You would then construct your UI around this controller, for example...
public class JavaGame2 extends JFrame {
public JavaGame2(){
CardLayout layout = new CardLayout();
setLayout(layout);
GameController controller = new CardLayoutGameController(getContentPane(), layout);
gamePanel = new GamePanel(controller);
menuPanel = new MenuPanel(controller);
add(gamePanel, CardLayoutGameController.GAME_PANEL);
add(menuPanel, CardLayoutGameController.MENU_PANEL);
controller.showMenu();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setTitle("JavaGame2");
setResizable(false);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame jgame = new JavaGame2();
jgame.setVisible(true);
}
});
}
}
Is this just an "example" to help spark the idea, haven't tested this, just hacked out here so it might blow up :P

Related

How do i switch between screens in swing

This has been asked before but I would like clarification, I'm new to java coding (sort of, started coding last month) and would like to know simply how can I switch between UIs in one JFrame, Picture this, a settings menu, How do I make it in one JFrame window instead of just make a new window with all the settings, If you don't get it, ask for clarification.
You can implement a frame (JFrame) and, for example, two panels (JPanel). Initially you embed panel A inside frame, when you want to show panel B then call the method showPanelB()
public class MyFrame extends JFrame {
PanelA panelA = new PanelA();
PanelB panelB = new PanelB();
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MyFrame().setVisible(true);
}
});
}
public MyFrame() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new BorderLayout());
showPanelA();
}
public void showPanelA() {
getContentPane().add(panelA, BorderLayout.CENTER);
}
public void showPanelB() {
getContentPane().add(panelB, BorderLayout.CENTER);
}
}
class PanelA extends JPanel {
// Panel implementation
}
class PanelB extends JPanel {
// Panel implementation
}

Components in JPanel only show after Resizing

The Buttons in my JPanel don't show up when it's loaded, only when I resize the window or move my mouse over it. In other discussions the use of "validate()" or "repaint()" was suggested, but that doesn't work for me.
I'm using a basic model view controller design and I am pretty sure that I'm doing everything else correctly.
Just in case you wonder, of course more panels will be added to the frame, that's the purpose of the update() and changeCards() methods.
Here's my frame:
public class View extends JFrame {
private MainMenuPanel mainMenu;
private final String MAIN_MENU_CONSTRAINTS = "MAIN_MENU";
public View() {
super();
init();
mainMenu = new MainMenuPanel();
add(mainMenu;MAIN_MENU_CONSTRAINTS);
validate();
repaint(0,0,getWidth(),getHeight());
setVisible(true);
}
private void init() {
setVisible(false);
setTitle("Test");
// set card-layout
setRootPaneCheckingEnabled(false);
CardLayout cl = new CardLayout();
this.setLayout(cl);
// expand frame to whole display size
setExtendedState(MAXIMIZED_BOTH);
// set unclosable
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public void update (Mode mode) {
switch (mode) {
case MAIN_MENU:
changeCard(MAIN_MENU_CONSTRAINTS);
break;
}
}
public void changeCard(String card) {
// update cards
CardLayout cl = (CardLayout) getLayout();
cl.show(this, card);
}
}
And here's the Panel:
public class MainMenuPanel extends Panel implements ActionListener{
private JButton startButton;
private JButton quitButton;
private final String START_ACTION_COMMAND = "START";
private final String QUIT_ACTION_COMMAND = "QUIT";
private MainMenuPanelListenerImpl listener;
public MainMenuPanel() {
super();
init();
initComponents();
configureComponents();
configureListeners();
addComponents();
revalidate();
}
private void init() {
setLayout(null);
}
private void initComponents() {
startButton = new JButton();
quitButton = new JButton();
}
private void configureComponents() {
startButton.setText("Start");
quitButton.setText("End");
startButton.setBounds((int)(0.5*getWidth()-200), (int)(0.5*getHeight()-75), 400, 75);
quitButton.setBounds((int)(0.5*getWidth()-200), (int)(0.5*getHeight()+25),400,75);
}
private void configureListeners() {
startButton.addActionListener(this);
startButton.setActionCommand(START_ACTION_COMMAND);
quitButton.addActionListener(this);
quitButton.setActionCommand(QUIT_ACTION_COMMAND);
}
private void addComponents() {
add(startButton);
add(quitButton);
startButton.validate();
quitButton.validate();
}
#Override
public void actionPerformed(ActionEvent e) {
switch (e.getActionCommand()) {
case START_ACTION_COMMAND:
listener.start();
break;
case QUIT_ACTION_COMMAND:
System.exit(0);
break;
}
}
public void setListener(MainMenuPanelListenerImpl listener) {
this.listener = listener;
}
}
After you paint the elements, you put the setvisible(true) because if you put it before, the Jframe will paint no elements
Well first of all you mix the old AWT-Components like Panel with newer SWING-Components like JFrame. Those don´t really work well together so I would try to fix that first. I would highly recommend using SWING or if you want to learn the newest Java GUI Library then JavaFX.
Don´t use the method repaint in the constructor of your JFrame, actually you shouldn´t use repaint in SWING at all. Nor do you need validate in the constructor. If you want to position your JFrame somewhere you should use something like this this.setLocation(0,0)
And to the main question: The panel probably only shows it´s components after resizing because you add it to the JFrame the wrong way. In SWING there is something called a content pane where you should add all of your stuff onto (except JMenuBar but that is a different story).
Simply set the layout of the content pane to the card layout that you want to use and then add your panel onto the content pane.
Here a link regarding the panel levels: https://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html

Clicking JPanel with inner components

I have a main JPanel class (not exact code):
class Panel extends JPanel {
public void initGUI() {
setLayout(...);
JTabbedPane tabbedPane = new JTabbedPane();
JPanel boxPanel = new JPanel(...);
tabbedPane.addTab("test", boxPanel);
JLabel label = new JLabel("Label")
boxPanel.add(label);
add(tabbedPane);
}
}
I want to be able to click anywhere on the Panel or its inner components and return the Panel.
public class PanelMouseAdapter extends MouseAdapter {
public void mouseReleased(MouseEvent e) {
Panel panel = (Panel)e.getSource();
//do other stuff
}
}
And for each Panel I'm adding this mouse listener.
But it only works around the edges of the Panel, any inner components are ignored. I need it to be able to click anywhere in that Panel.
I need to maintain that anywhere I click it will return the Panel object (as in the mouse listener).
Thanks for any feedback.
Not sure I understand the question. Your demo code only shows a single panel, so why do you care what parent panel is clicked on? The better your explanation of the requirement the better the solution we can provide.
Anyway, check out Global Event Listeners. This will allow you to listen for a mousePressed event (which is a better then listening for a mouseClicked).
Next you need to create a custom panel (MyCustomPanel) that you use for the top level panel.
Now, whenever a mousePressed is generated you can get the source of the event and then use:
MyCustomPanel panel = SwingUtilties.getAncestorOfClass(MyCustomPanel.class, (Component)event.getSource());
You can use Container#getComponents() for this case. For example consider the code snippet given below (look for addListeners(Container) method):
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.awt.*;
class SPanel extends JPanel
{
public void init()
{
this.add(new JButton("Button"));
this.add(new JLabel("Click"));
JPanel pan = new JPanel();
pan.add(new JButton("PanButton"));
pan.add(new JTextField(29));
add(pan);
addListeners(this);
}
public void addListeners(Container comp)
{
Component[] components = comp.getComponents();
for (Component component : components)
{
if (component instanceof Container)
{
Component[] child = ((Container)component).getComponents();
if (child != null && child.length > 0)
{
addListeners((Container)component);
}
}
component.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent evt)
{
System.out.println(evt.getSource().getClass());
}
});
}
}
public static void main(String[] args)
{
SwingUtilities.invokeLater( new Runnable()
{
#Override
public void run()
{
SPanel sp = new SPanel();
sp.init();
JFrame frame = new JFrame("Frame");
frame.getContentPane().add(sp);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
Solved issue.
Initially I was going to open a popup menu once I had the mouse listener in place.
But now I added JComponent.setComponentPopupMenu as Polum suggested, not to the Panel but to the tabbedPane.
Then I added a listener to the popup menu, got the source object via event in popupMenuWillBecomeVisible method, then the component via source.getInvoker(), then get the parent of the invoker component and check if its an instance of PairBox.

JTextField focus

I have a frame and a panel.Permanently I remove the panel and add another panel.After adding a new panel I need the JTextField to get focused.How can I do this?
I tried panel.requestFocus() method but it didnt work.
Example Code:
public class Test{
public static void main(String[] args){
JFrame frame = new JFrame();
// ... frame options
// MyPanel extends JPanel
// and has a JTextField
contentPane.add(new MyPanel());
// Permanently I need to add another panel
contentPane.removeAll();
contentPane.add(new MyPanel());
}
}
Calling panel.requestFocus() attempts to give focus to the container itself rather than on any of its child components.
Use requestFocusInWindow on the JTextField after the component has been added to the JFrame. Add an public method in MyPanel for calling this method.
Avoid using requestFocus. From the docs:
requestFocus, is discouraged because it tries to give the focus to the component's window, which is not always possible. As of JDK 1.4, you should instead use the requestFocusInWindow method, which does not attempt to make the component's window focused.
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Focus JTextField");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyPanel myPanel = new MyPanel();
frame.add(myPanel);
frame.setVisible(true);
frame.pack();
myPanel.focusTextField();
}
});
}
}
class MyPanel extends JPanel {
private JTextField textField;
public MyPanel() {
textField = new JTextField(20);
add(textField);
}
public void focusTextField() {
textField.requestFocusInWindow();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 100);
}
}
Add a method like this to MyPanel:
public void gainFocus() {
tf.requestFocus();
}
Call it from the main method, or elsewhere whenever you need it to be focused.
Use. textfield.setText(""); when you need to get the focus or try something like you will take your control to your field try
You will need a method either to get the TextField from MyPanel, like getTextField, or a method to just directly focus on the TextField. These methods must be inside your MyPanel class.
Example method:
public class MyPanel extends JPanel {
private JTextField textField;
//your code here
public void getTextFieldFocus() {
textField.requestFocus();
}
}
Then you call this getTextFieldFocus method when you need to focus.
Else, if you extract the TextField from the MyPanel class using a getTextField method, you call this when you need the focus:
panel.getTextField().requestFocus();

Add JInternalFrame to JDesktopPane in execution time

I have a problem with a JDesktopPane, I add JInternalFrame on it and then show it on a JFrame.
The problem is when I try to add another JInternalFrame in execution time.
I use the same method to add the same JInternalFrame but its dosnt shows up.
public class Desktop extends JDesktopPane {
(...)
public void addJInternalFrameBox(JInternalFrameBox jifb) {
this.add(jifb, desktop.CENTER_ALIGNMENT);
this.repaint();
this.validate();
}
}
JInternalFrameBox class:
public class JInternalFrameBox extends JInternalFrame {
(...)
public JInternalFrameBox(Integer id) {
this.id = id;
setUpFrame();
}
public void setUpFrame() {
JLabel lbl = new JLabel("test");
lbl.setVisible(true);
this.add(lbl);
this.setPreferredSize(INTERNAL_FRAME_SIZE);
this.setLocation(100, 100);
this.setIconifiable(true);
this.setClosable(true);
this.pack();
this.setVisible(true);
}
}
jButtonBox the button that open the JInternalFrameBox:
public class jButtonBox extends JButton implements MouseListener {
public void mouseReleased(MouseEvent e) {
JInternalFrameBox jifb = new JInternalFrameBox(id);
jifb.setVisible(true);
Desktop df = Desktop.getInstance();
df.addJInternalFrameBox(jifb);
}
(...)
}
Read the section from the Swing tutorial on How to Use Internal Frames for a working example.
Don't use a JPanel for your desktop, but rather use a JDesktopPane. That's specifically what its for.
you have to set both location and size of the internal frame, as in
setSize(INTERNAL_FRAME_SIZE); // instead of setPref
setLocation(100, 100);
hm ... maybe not (just saw the pack in your code) - no more guessing without an sscce, as others already stated

Categories