How would I change the contents of a JFrame by dynamically clicking keys, in this example I want to change the contents through two JPanels I have created, when i click up I want to change to Panel2, and when I click down I want to change to Panel3, but I want the JFrames contents only to change (I want to stay in the same JFrame) there is no errors with the code, I am just a bit confused on how to solve this issue.
class Drawing extends JFrame implements KeyListener{
int num = 1;
public Drawing() {
Panel2 jPanel2 = new Panel2();
Panel3 jPanel3 = new Panel3();
if(num == 1){
add(jPanel2);
remove(jPanel3);
pack();
}
if(num == 2){
add(jPanel3);
remove(jPanel2);
pack();
}
// be nice to testers..
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
addKeyListener(this);
}
and then i have the keypressed method to change num to either 1 or 2 depending on what key is pressed (i also implemented keyReleased and keyTyped but I didn't include them to add space)
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == e.VK_UP){
num = 1;
}
if(e.getKeyCode() == e.VK_DOWN){
num = 2;
}
}
public static void main(String args[]) {
new Drawing();
}
and here is the two panels that change the graphics of the page
class Panel2 extends JPanel {
Panel2() {
setPreferredSize(new Dimension(500,500));
}
public void paint(Graphics g) {
g.drawString("BLAH", 20, 20);
g.drawRect(200, 200, 200, 200);
repaint();
}
}
class Panel3 extends JPanel {
Panel3() {
// set a preferred size for the custom panel.
setPreferredSize(new Dimension(500,500));
}
public void paint(Graphics g) {
g.drawString("BURP", 20, 20);
g.drawRect(200, 200, 200, 200);
repaint();
}
}
Firstly, in your use case, don't use keyTyped event because it is only triggered
when the unicode character represented by this key is sent by the
keyboard to system input
. There is no system input in your UI. Checkout this for more details: KeyListener, keyPressed versus keyTyped
Instead, use keyPressed or keyReleased event.
Secondly, you need explicitly invoke add(jPanel2);remove(jPanel3); when you are handling the event. Set num = 1 doesn't do anything.
Thirdly, you need to call jPanel2.repaint(); after add(jPanel2);remove(jPanel3); to make sure the panel paint the content you want.
Fourthly, always call super.paint(g); to initialize the painting canvas correctly!
So, to put them all together, you have:
public class Drawing extends JFrame implements KeyListener {
int num = 1;
Panel2 jPanel2 = new Panel2();
Panel3 jPanel3 = new Panel3();
public Drawing() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
addKeyListener(this);
setSize(300, 300);
}
#Override
public void keyTyped(KeyEvent e) {
}
private void changePanel() {
if (num == 1) {
remove(jPanel3);
add(jPanel2);
jPanel2.repaint();
pack();
}
if (num == 2) {
remove(jPanel2);
add(jPanel3);
jPanel3.repaint();
pack();
}
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == e.VK_UP) {
num = 1;
}
if (e.getKeyCode() == e.VK_DOWN) {
num = 2;
}
changePanel(); // do the actual swap of the panels
}
public static void main(String args[]) {
new Drawing();
}
}
class Panel2 extends JPanel {
Panel2() {
setPreferredSize(new Dimension(500, 500));
}
public void paint(Graphics g) {
super.paint(g); //always call!
g.drawString("BLAH", 20, 20);
g.drawRect(200, 200, 200, 200);
}
}
class Panel3 extends JPanel {
Panel3() {
setPreferredSize(new Dimension(500, 500));
}
public void paint(Graphics g) {
super.paint(g); //always call!
g.drawString("BURP", 20, 20);
g.drawOval(200, 200, 200, 200);
}
}
Related
I am trying to add a Rectangle in the panel by button press and remove it with another one. It should work but it doesn't render anything, and I absolutely don't know why.
Can someone explain what I am doing wrong and give me some nice tips what I can improve with my code?
public class GUI extends JPanel {
public static boolean isRecVisible = false;
public static void main(String[] args) {
createGui();
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g);
g2d.drawRect(10, 10, 200, 200);
}
public static void createGui() {
int frameWidth = 550;
int frameHeight = 400;
int buttonWidth1 = 250;
int buttonHeight1 = 30;
int buttonWidth2 = 500;
int buttonHeight2 = 30;
int displayWidth = frameWidth - 20;
int displayHeight = frameHeight - 105;
GUI drawRec = new GUI();
JFrame f = new JFrame("Rectangle");
JPanel p = new JPanel();
JPanel display = new JPanel();
JButton addRec = new JButton("Add Rectangle");
JButton removeRec = new JButton("Remove Rectangle");
JButton colorRec = new JButton("Color Rectangle");
f.add(p);
p.add(addRec);
p.add(removeRec);
p.add(colorRec);
p.add(display);
display.setBackground(Color.LIGHT_GRAY);
f.setSize(frameWidth, frameHeight);
f.setLocationRelativeTo(null);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
display.setBounds(frameWidth / 2 - displayWidth / 2, 10, displayWidth, displayHeight);
addRec.setBounds(frameWidth / 2 - buttonWidth1 / 2 - 250 / 2, frameHeight - 85, buttonWidth1, buttonHeight1);
removeRec.setBounds(frameWidth / 2 - buttonWidth1 / 2 + 250 / 2, frameHeight - 85, buttonWidth1, buttonHeight1);
colorRec.setBounds(frameWidth / 2 - buttonWidth2 / 2, frameHeight - 60, buttonWidth2, buttonHeight2);
addRec.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (isRecVisible == false) {
isRecVisible = true;
display.add(drawRec);
display.repaint();
System.out.println("Rectangle has been drawn!");
} else {
System.out.println("Rectangle has already been drawn!");
return;
}
}
});
removeRec.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (isRecVisible == true) {
isRecVisible = false;
System.out.println("Rectangle has been removed!");
} else {
System.out.println("Rectangle has already been removed");
return;
}
}
});
}
}
display.add(drawRec);
display.repaint();
When you add (or remove) components to a visible frame then the basic logic is:
display.add(...);
display.revalidate();
display.repaint(); // sometimes needed
The revalidate() is the key method because it invokes the layout manager so the size/location of the component can be set.
However, that still won't fix the problem because your custom panel doesn't have a preferred size, so there is nothing to paint for your component.
You need to override the getPreferredSize() method of your custom panel to return the preferred size of your custom component. So in your case you might set the preferred size to be (220, 220) so the rectangle is centered in the panel.
Read the section from the Swing tutorial on Custom Painting for more information and complete working examples.
Note: the tutorial example will also show you how to better structure your code to make sure the GUI is created on the Event Dispatch Thread.
Rather than adding or removing components, it would make more sense here to add the custom painted panel on construction, and use the isRecVisible boolean as a flag to test for drawing the rectangle.
Something like shown here:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GUI extends JPanel {
public static boolean isRecVisible = false;
public static void main(String[] args) {
createGui();
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g);
if (isRecVisible) {
g2d.drawRect(10, 10, 200, 200);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600,400);
}
public static void createGui() {
int frameWidth = 550;
int frameHeight = 400;
GUI drawRec = new GUI();
drawRec.setBackground(Color.LIGHT_GRAY);
JFrame f = new JFrame("Rectangle");
JPanel p = new JPanel();
JButton addRec = new JButton("Add Rectangle");
JButton removeRec = new JButton("Remove Rectangle");
JButton colorRec = new JButton("Color Rectangle");
f.add(p, BorderLayout.PAGE_START);
p.add(addRec);
p.add(removeRec);
p.add(colorRec);
f.add(drawRec);
f.setSize(frameWidth, frameHeight);
f.setLocationRelativeTo(null);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
addRec.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
isRecVisible = true;
drawRec.repaint();
}
});
removeRec.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
isRecVisible = false;
drawRec.repaint();
}
});
}
}
I'm trying to do some basic Java, and I've got my frame with a shape in it. It's using a JComponent class to draw the shape, with the animation being triggered on a button click at the top.
the component code is just this added to the jpanel
public void paintComponent(Graphics g){
Dimension dim = getSize();
g.setColor(Color.GREEN);
g.fillOval(margin, 150, 100, 100);
super.paintComponent(g);
}
The animation is just done inside a for loop, that just edits the left margin so that the circle moves to the right;
int getMarg = cc.getMargin();
for(int i = 1;i < 20;i++){
getMarg = cc.getMargin();
cc.setMargin(getMarg + 1);
reValidate();
System.out.println(i);
But it doesn't seem to move until the end of the loop, moving 20 pixels at a time. I previously had a sleep function but it seemed pointless when it wouldn't animate.
Any insight? Cheers.
The whole code for anyone interested, messy and largely just to get the styling:
class Main extends JFrame{
public JPanel panel = new JPanel();
JButton button1 = new JButton("Move Right");
CreateComps cc = new CreateComps();
Main(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initUI();
}
void initUI(){
setSize(800,800);
setBackground(Color.GRAY);
setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));
JPanel topBar = new JPanel();
topBar.setPreferredSize(new Dimension(800,30));
topBar.setMaximumSize(new Dimension(800,30));
topBar.setLayout(new BorderLayout());
topBar.add(button1, BorderLayout.WEST);
topBar.setBackground(Color.GRAY);
JPanel container = new JPanel();
container.setLayout(new GridBagLayout());
container.setBackground(Color.DARK_GRAY);
panel.setPreferredSize(new Dimension(600,500));
panel.setMinimumSize(new Dimension(600,500));
panel.setBackground(Color.WHITE);
panel.setLayout(new BorderLayout());
panel.add(cc, BorderLayout.CENTER);
add(topBar);
add(container);
container.add(panel);
Listener listen = new Listener();
button1.addActionListener(listen);
setVisible(true);
}
public void reValidate(){
panel.revalidate();
panel.repaint();
}
public static void main (String[] args){
Main main = new Main();
}
class Listener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Listening..");
if(e.getSource().equals(button1)){
int getMarg = cc.getMargin();
for(int i = 1;i < 20;i++){
getMarg = cc.getMargin();
cc.setMargin(getMarg + 1);
reValidate();
System.out.println(i);
}
}
}
}
}
class CreateComps extends JComponent{
int margin = 10;
public void setMargin(int marg){
margin = marg;
}
public int getMargin(){
return margin;
}
#Override
public Dimension getPreferredSize(){
return new Dimension(new Dimension(200,200));
}
#Override
public Dimension getMaximumSize(){
return new Dimension(new Dimension(200,200));
}
#Override
public Dimension getMinimumSize(){
return new Dimension(new Dimension(200,200));
}
public void paintComponent(Graphics g){
Dimension dim = getSize();
g.setColor(Color.GREEN);
g.fillOval(margin, 150, 100, 100);
super.paintComponent(g);
}
}
Without pauses, you're stacking calls to revalidate and will see nothing else than the result of the last call.
The sleep function you previously had, probably was called on the Event Dispatch Thread, which is not good at all, since you're blocking the entire event dispatching and GUI updates.
Consider either using sleep calls on another Thread :
#Override
public void actionPerformed(final ActionEvent e) {
System.out.println("Listening..");
if (e.getSource().equals(button1)) {
new Thread() {
#Override
public void run() {
int getMarg = cc.getMargin();
for (int i = 1; i < 20; i++) {
getMarg = cc.getMargin();
cc.setMargin(getMarg + 1);
reValidate();
System.out.println(i);
try {
Thread.sleep(50);
} catch (Throwable e) {
}
}
}
}.start();
}
}
Or you may also simply use a Timer, which is great for this job.
I have problem with KeyListener, I've been searching solution but it should be working.
Unfortunately, it doesn't and i have no idea why. When i type up arrow, nothing happens.
I was reading about "Focus", but I don't know how this works, maybe you can give me some example, unnecessary to my problem.
public class Trawa extends JPanel implements KeyListener {
int wysokosc = 200;
public Trawa(){
addKeyListener(this);
setSize(200,600);
setBackground(Color.GREEN);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
Color c1 = new Color(36,217,36);
g2d.setColor(c1);
g2d.fillRect(10, wysokosc, 100, 100);
c1 = new Color(0,0,0);
g2d.setColor(c1);
g2d.fillRect(10, wysokosc, 30, 30);
g2d.fillRect(80, wysokosc, 30, 30);
c1 = new Color(252,3,0);
g2d.setColor(c1);
g2d.fillRect(40, wysokosc+60 ,30,30);
}
#Override
public void keyPressed(KeyEvent arg0) {
int key = arg0.getKeyCode();
if(key == KeyEvent.VK_UP)
wysokosc-=100;
repaint();
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
Here is my main class with frame. Maybe the problem is here.
public class GUI extends JFrame{
public GUI(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
add(new Trawa());
setLayout(new GridLayout(1,5));
pack();
setSize(1300, 600);
setLocation(40, 100);
setVisible(true);
}
public static void main(String[] args) {
new GUI();
}
}
You have to understand how focus works before using a KeyListener.
Try calling setFocusable(true) from your JPanel's constructor.
You have done everything correctly, but you forgot to bind the key listener with your frame.
try this version of you GUI class
class GUI extends JFrame {
public GUI() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
Trawa t = new Trawa();//this is a JPanel as well as a KeyListner
add(t);
this.addKeyListener(t);
setLayout(new GridLayout(1, 5));
pack();
setSize(500, 400);
setLocation(40, 100);
setVisible(true);
}
}
as pointed by Kevin: the minimal change required is to set focus on the panel
public Trawa() {
addKeyListener(this);
setSize(200, 600);
setBackground(Color.GREEN);
setFocusable(true);
}
I was drawing my game on Canvas, all was god, but I changed it into a JPanel, but now its not working correctly, here are the codes, you can just copy them and you'll see where is the problem (I have a menu and after clicking on the button it should create new thread and there i want to draw, the problem in JPanel is that the button is able to see, its blinking and i can press it, in canvas it was fine, there wasn't any button). I solved it, that after clicking on the button I setted him unvisible (button.setVisible(false)), but these codes are just examples and in my game I have more buttons, so its not practical because I need them to see after the game ends. I think I just forgot an important method in JPanel, thx for help, codes:
//Main class representing menu
public class Sandbox extends JFrame{
Panel p = new Panel();
public static void main(String[] args) {
new Sandbox();
}
public Sandbox() {
setLayout(null);
setPreferredSize(new Dimension(200, 200));
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
final JButton but = new JButton("Button");
but.setBounds(0, 0, 50, 50);
but.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.start();
}
});
add(p);
add(but);
pack();
setVisible(true);
}
}
//Drawing on Canvas -> working well
public class Panel extends Canvas implements Runnable {
Thread t;
public Panel() {
setSize(new Dimension(200, 200));
setVisible(false);
}
public void start() {
t = new Thread(this);
t.start();
setVisible(true);
}
public void draw() {
BufferStrategy b = getBufferStrategy();
if(b == null) {
createBufferStrategy(3);
return;
}
Graphics g = b.getDrawGraphics();
g.setColor(Color.red);
g.fillRect(0, 0, 200, 200);
g.dispose();
b.show();
}
#Override
public void run() {
while(!t.isInterrupted()) {
try {
draw();
t.sleep(200);
} catch (InterruptedException ex) {}
}
}
}
//Drawing on JPanel -> here i can press the button after first click on it
public class Panel extends JPanel implements Runnable {
Thread t;
public Panel() {
setSize(new Dimension(200, 200));
setVisible(false);
}
public void start() {
t = new Thread(this);
t.start();
setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(0, 0, 200, 200);
}
#Override
public void run() {
while(!t.isInterrupted()) {
try {
repaint();
t.sleep(200);
} catch (InterruptedException ex) {}
}
}
}
Not sure I understand exactly what you are trying to do but you do have a couple of problems:
the setVisible(true) method should be invoked AFTER you have added all the components to the frame and packed the frame.
by default the content pane of a JFrame uses a BorderLayout. You code is adding two components to the CENTER of the BorderLayout, but a BorderLayout only allows you to add one component to the CENTER, so only the last component added will be displayed.
Hi hope someone can tell me what I am doing wrong with my Key Event.
I am using a Card Layout to navigate through two of my JPanels atm. To do so I use Action Events as well as Key Events. The action events will toggle between JPanels when a button is pressed while the key events will hide away the buttons when a key is pressed. All good with the key events, it does what I want (call a method on one of the panels to set the bounds of the buttons placed inside it eq: button.setBounds(-1, -1, 150, 40); but when I press any of the buttons the key event wont respond despite not having any event on the buttons that I press. Below is my code, for simplicity I removed the non relevant parts of the panels like what they are meant to do.
Thanks in advance and please let me know if I need to provide more clues or to edit the code more, I will try my best to make the code clearer.
public class PanelContainer extends JPanel implements ActionListener, KeyListener{
GamePanel game = new GamePanel();
MainMenuPanel mainMenu = new MainMenuPanel();
CardLayout cards = new CardLayout();
public PanelContainer(){
setLayout(cards);
this.setFocusable(true);
this.addKeyListener(this);
mainMenu.newGameButton.addActionListener(this);
add(mainMenu, "Card1");
add(game, "Card2");
}
#Override
public void actionPerformed(ActionEvent aevt){
cards.show(this, "Card2");
game.action();
}
#Override
public void keyTyped(KeyEvent kevt){
}
#Override
public void keyPressed(KeyEvent kevt){
}
#Override
public void keyReleased(KeyEvent kevt){
if(kevt.getKeyCode() == KeyEvent.VK_ESCAPE || kevt.getKeyChar() == 'O' || kevt.getKeyChar() == 'o'){
game.shw(); //shw() is a method inside GamePanel that sets the bounds of the buttons
}
else if (kevt.getKeyChar() == 'h' || kevt.getKeyChar() == 'H'){game.hid();}
}
}
public class MainMenuPanel extends JPanel
{
private URL workingDir = this.getClass().getResource("imgresources/brick_wall.png") ;
private ImageIcon icon = new ImageIcon(workingDir) ;
private Image img = icon.getImage();
//create and initiate buttons;
JButton newGameButton = new JButton("New Game");
JButton highScoreButton = new JButton("High Scores");
JButton controlsButton = new JButton("Controls");
Point[] points = new Point[1000];
public MainMenuPanel(){
add(newGameButton);
add(highScoreButton);
add(controlsButton);
setPoints();
}
public void setButtonsBounds(){
newGameButton.setBounds(450, 200, 200, 40);
highScoreButton.setBounds(450, 250, 200, 40);
controlsButton.setBounds(450, 300, 200, 40);
}
#Override
public void paintComponent(Graphics g){
try{
super.paintComponent(g);
Graphics2D d2 = (Graphics2D) g;
d2.setColor(new Color(0, 0, 0));
d2.fillRect(0, 0, this.getWidth(), this.getHeight());
setButtonsBounds();
for(int i=0; i<275; i++){
d2.drawImage(img, points[i].x +200, points[i].y, this);
}
}catch(Exception e){}
}
}
public class GamePanel extends JPanel implements Runnable{
JButton button = new JButton("Main Menu");
JButton button2 = new JButton("Exit Game");
void shw(){
add(button);
add(button2);
button.setBounds(400, 200, 150, 20);
button2.setBounds(400, 240, 150, 20);
}
void hid(){
button.setBounds(1, 1, 150, 20);
button2.setBounds(1, 40, 150, 20);
}
}
It's a focus issue. Use Key Bindings instead of a KeyListener so you don't have to worry about this issue (and for other benefits as well -- check the Key Bindings tutorial for details).
Here's my SSCCE that demonstrates what I'm talking about. Note that both KeyListener and key bindings work until you press a button, and then only bindings work:
import java.awt.event.*;
import javax.swing.*;
public class KeyBindingsEg {
private static void createAndShowGui() {
PanelContainer mainPanel = new PanelContainer();
JFrame frame = new JFrame("KeyBindingsEg");
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();
}
});
}
}
#SuppressWarnings("serial")
class PanelContainer extends JPanel {
public PanelContainer() {
this.setFocusable(true);
this.addKeyListener(new MyKeyListener());
JButton newGameButton = new JButton("New Game");
newGameButton.addActionListener(new MyActionListener());
add(newGameButton);
setKeyBindings();
}
private void setKeyBindings() {
Action hideAction = new BindingAction(BindingAction.HIDE);
Action showAction = new BindingAction(BindingAction.SHOW);
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
actionMap.put(BindingAction.HIDE, hideAction);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_H, 0), BindingAction.HIDE);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.SHIFT_DOWN_MASK),
BindingAction.HIDE);
int[] showKeys = { KeyEvent.VK_O, KeyEvent.VK_ESCAPE };
actionMap.put(BindingAction.SHOW, showAction);
for (int key : showKeys) {
inputMap.put(KeyStroke.getKeyStroke(key, 0), BindingAction.SHOW);
inputMap.put(KeyStroke.getKeyStroke(key, KeyEvent.SHIFT_DOWN_MASK),
BindingAction.SHOW);
}
}
private class MyActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent aevt) {
System.out.println("button pressed");
}
}
private class MyKeyListener extends KeyAdapter {
public void keyReleased(KeyEvent kevt) {
if (kevt.getKeyCode() == KeyEvent.VK_ESCAPE
|| kevt.getKeyChar() == 'O' || kevt.getKeyChar() == 'o') {
System.out.println("KeyListener: show");
} else if (kevt.getKeyChar() == 'h' || kevt.getKeyChar() == 'H') {
System.out.println("KeyListener: hide");
}
}
}
private class BindingAction extends AbstractAction {
public static final String HIDE = "Hide";
public static final String SHOW = "Show";
public BindingAction(String text) {
super(text);
putValue(ACTION_COMMAND_KEY, text);
}
#Override
public void actionPerformed(ActionEvent evt) {
String actionCommand = evt.getActionCommand();
if (actionCommand.equals(HIDE)) {
System.out.println("key bindings: hide");
} else if (actionCommand.equals(SHOW)) {
System.out.println("key bindings: show");
}
}
}
}