I have nested a Jpanel within another JPanel. I want to refresh one of JPanels while not refreshing the other one. I have the following code, I can use the repaint() function however it will update all of the JPanels not just the one that I want (the Time JPanel).
How would I go about refreshing just the Time JPanel? Leaving the Weather JPanel untouched?
I would like to be able to do this from an external thread.
public class MainPanel extends JPanel{
public static JPanel TimePanel = new Time();
public static Weather WeatherPanel = new Weather();
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.BLACK);
this.setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0));
TimePanel.setLocation(0, 0);
TimePanel.setSize(new Dimension(500, 300));
this.add(TimePanel);
WeatherPanel.setLocation(0,300);
WeatherPanel.setSize(new Dimension(100, 100));
this.add(WeatherPanel);
//repaint();//Just causes recursion
}
}
Your code is completely wrong. The paintComponent() method is used for painting with the Graphics object. You should NEVER add components to a panel or change the size, or change the location of a component in a painting method.
There is no need for you to override the paintComponent() method.
In the constructor of your class is where you create the child panels and add them to the main panel. Something like:
public class MainPanel extends JPanel
{
public JPanel timePanel = new Time();
public Weather teatherPanel = new Weather();
public MainPanel()
{
this.setBackground(Color.BLACK);
this.setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0));
this.add(timePanel);
this.add(weatherPanel);
}
}
Notice how I also changed your variables:
you should not be using static
variable names start with a lower case character.
I suggest you start by reading the Swing Tutorial for Swing basics. You can check out the section on How To Use Panels for a working example.
Related
I am creating a minesweeper game, and what I want to do is to have a JTextField where the user inputs his name in order for his score to be saved in a file.
My problem is that when I create a JTextField and add it to my Jpanel it appears in 2 locations. Here is an image of what is happening
(https://i.imgur.com/Ao8dRo1.jpg)
This is my code over-simplified. I believe that I don't properly understand something about how the mechanism of the GUI works.
GUI.java
public class GUI extends JFrame {
//..
//some variables here
//...
public GUI() {
this.setTitle("Minesweeper Game");
this.setSize(WIDTH, HEIGHT);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.setResizable(false);
this.setLayout(null);
//This method does not involve any drawing, it only places data in some arrays that I later use in paintComponent() to draw stuff accordingly to what the data is
setMinefield();
Board board = new Board();
this.setContentPane(board);
Click click = new Click();
this.addMouseListener(click);
}
public class Board extends JPanel {
public void paintComponent (Graphics g) {
//...
//Drawing tiles, smiley, counters
//...
//And now I draw the area for the JTextField, and I also create it and add it in the Jpanel
JTextField textField = new JTextField();
textField.setFont(new Font("Tahoma", Font.PLAIN, 35));
textField.setBounds(290, 80, 135, 40); //<-- This correctly places the textField where I want. The second textField seems to appear in the exact center of the X axis of my window
add(textField); //<-- Adding the textField to the Jpanel
} //End of paintComponent()
}//End of Board class
}//End of GUI class
Main.java
public class Main implements Runnable {
GUI gui = new GUI();
public static void main(String[] args) {
new Thread (new Main()).start();
}
#Override
public void run() {
while (true) {
gui.repaint();
}
}
}
I think the problem is that you have overridden paintComponent in your Board class. This method gets called every time the component needs to be drawn so a new text field will be added each time.
It would be better to add the text field in the constructor for your board class.
I use the command textField.setBounds(290, 80, 135, 40); to place it where I want but it doesn't work. Why could this happen?
Swing was designed to used with layout managers. The default layout manager for a JPanel is the FlowLayout. The FlowLayout will ignore the setBounds(...) statement and set the size/location of the text field based on the rules of the FlowLayout.
So don't attempt to use a null layout and don't use setBounds(). Instead let the layout manager do its job.
Also, you should be adding the components to the frame BEFORE you make the frame visible.
I would suggest your code should be something like:
JTextField textField = new JTextField(10);
JPanel top = new JPanel();
top.add( textField );
Board board = new Board();
add(top, BorderLayout.PAGE_START);
add(board, BorderLayout.CENTER);
setResizable( false );
pack();
setVisible( true );
The Board class should override the getPreferredSize() method of the Board to return your desired size so that the pack() method works properly.
The default layout manager for a JFrame is the BorderLayout. So, now the top part of the frame will contain the text field centered and the main part of the frame will contain your Board class. Read the section from the Swing tutorial on How to Use BorderLayout to understand how the above code works.
Also, the MouseListener should be added to the Board, not the JFrame.
After making an instance in a previous frame, I'm trying to the background image on the next frame but as a result, I just saw the debugged result and found out that the paint method was not called. From what I know, the paint method is inherited by the JFrame class and with this logic, I've made it overrided. As I guess, the reason happen the logical error is from what I used the event handler and made the instance in the EventHandlerClass.
if(e.getActionCommand().equals(ButtonTo))
if(idString.equals("USER"))
{
{
if("1234".equals(pwSt))
{
System.out.println("Wellcome");
if(gs==null)
{
gs=new GameStart();
}
}
else
{
System.out.println("Confirm your password");
}
}
}
This is a code that If an action is performed it will make an instance(gs). After doing this, I noticed that the instance has been used as to make a new console frame.
class GameStart extends JFrame {
private Image screenImage;
private Graphics screenGraphic;
private Image introBackgroundImage;
private ImageIcon img;
GameStart()
{
JFrame jf=new JFrame("Game Set");
jf.setBounds(300, 300, 400, 200);
jf.setLayout(new BorderLayout());
JButton bt1=new JButton("Start");
JButton bt2=new JButton("Exit");
JPanel panel1=new JPanel();
panel1.add(bt1);panel1.add(bt2);
setContentPane(panel1);
jf.add(panel1, BorderLayout.SOUTH);
bt1.addActionListener(new Choice());
bt2.addActionListener(new Choice());
jf.setVisible(true);
img=new ImageIcon("./Images/backGroundImage.jpg");
System.out.println("1");
}
public void paint(Graphics g) {
screenImage=createImage(200, 200);
screenGraphic=screenImage.getGraphics();
screenDraw(screenGraphic);
g.drawImage(screenImage, 0, 0, null);
System.out.println("2");
}
public void screenDraw(Graphics g)
{
this.repaint();
System.out.println("3");
}
Now, With making a frame and some buttons, I expect to show all the numbers(1, 2, 3) that indicate the result but Just did number 1.
There are some errors in your code that I can see at first glance:
You're extending JFrame, but you're not adding any extra functionality to it, see: Extends JFrame vs. creating it inside the program. Instead, build your GUI towards the use of JPanels and override their paintComponent(...) method and not the paint(...) one.
You're breaking the paint-chain: After doing the above point, in paintComponent(), call super.paintComponent(...)
Maybe there are others but I'm currently busy and can't test your code, but the ones above should help with your issue.
I have a custom JLayeredPane, and I am repainting it in my game loop. There are two custom JPanels added into the JLayeredPane. These are foreground and background JPanels. How do I successfully only draw my background JPanel once, (And repaint when window is re-sized or any other reason) to reduce impact on system resources, while continuing to update my foreground JPanel constantly.
To re-iterate, I dont want to constantly repaint the background JPanel in a loop. I want to repaint it only when it is nessessary, as the background does not change. and is large.
In my attempt to do this, I have only drawn the background once. However. the background JPanel is simply not visible. while the foreground JPanel updates as normal. It is almost as if the foreground JPanel paints ontop of the background JPanel, even though I have both of the JPanels set to setOpaque(false)
I have made a mvce which shows my attempt at only drawing the background JPanel once, while updating the foreground JPanel constantly.
The problem with my code is that the background JPanel does not show.
Now. I know that if I were to draw it constantly it would show. But that defeats the purpose of what i'm trying to do. I am trying to only draw it once, and have be seen at the same time
My code successfully only draws the background JPanel once. The problem is that the background JPanel does not show. How do I fix THIS problem
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main extends JLayeredPane {
static JFrame frame;
static Main main;
static Dimension screenSize;
public Main() {
JPanel backPanel = new BackPanel();
JPanel frontPanel = new FrontPanel();
add(backPanel, new Integer(7));
add(frontPanel, new Integer(8));
new Thread(() -> {
while (true){
repaint();
}
}).start();
}
public static void main(String[] args) {
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame = new JFrame("Game"); // Just use the constructor
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main = new Main();
frame.add(main, BorderLayout.CENTER);
frame.pack();
frame.setSize(screenSize);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class BackPanel extends JPanel{
public boolean drawn = false;
public BackPanel(){
setVisible(true);
setOpaque(false);
setSize(screenSize);
JLabel test1 = new JLabel("Test1");
JLabel test2 = new JLabel("Test2");
add(test1);
add(test2);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
drawOnce(g);
}
public void drawOnce(Graphics g){
if (!drawn){
g.setColor(Color.red);
g.fillRect(0, 0, screenSize.width, 200);
drawn=true;
}
}
}
public class FrontPanel extends JPanel{
public FrontPanel(){
setVisible(true);
setOpaque(false);
setSize(screenSize);
JLabel test = new JLabel("Test");
add(test);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.blue);
g.fillRect(0+screenSize.width/2, 0, screenSize.width/4, 300);
}
}
}
Try RepaintManager.currentManager(component).markCompletelyClean(component). It will prevent the component from repainting. You might need to do this after each time you add new components.
http://docs.oracle.com/javase/6/docs/api/javax/swing/RepaintManager.html#markCompletelyClean%28javax.swing.JComponent%29
I don't know if this two lines of code
super.paintComponent(g);
drawOnce(g);
are the root of problem, I sincerly don't remember how paintComponent works (a test could help) but try to swap them :
drawOnce(g);
super.paintComponent(g);
maybe, on your original version, you tells JVM to paint the whole component and, only after the AWTEvent has been added to the queue, to draw what you need.
I guess that the awt's documentation will explain it.
I have looked online, but I am still having trouble understanding how to add graphics to a JPanel
Here is the code for my panel class:
public class GamePanel extends JPanel {
public GamePanel(){
}
public void paintComponent(Graphics g) {
g.drawString("asd", 5, 5);
}
}
And my main method:
public static void main(String[] args) {
frame.setLayout(new FlowLayout());
frame.getContentPane().setBackground(Color.WHITE);
//i is an instance of GamePanel
frame.add(i);
frame.setPreferredSize(new Dimension(500, 500));
frame.pack();
frame.setVisible(true);
}
Text will only appear in a very tiny section of the screen (this applies to any graphics object I try to draw). What am I doing wrong?
FlowLayout respects preferred sizes of components. Therefore override getPreferredSize to give your JPanel a visible size rather than the default zero size Dimension that the panel currently has after JFrame#pack is called:
class GamePanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g); // added to paint child components
g.drawString("asd", 5, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
Edit:
To eliminate gap between JPanel and its containing JFrame, set the vertical gap to 0:
frame.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
Two things jump out
Your Game panel has no preferred size, which, by default, makes 0x0. FlowLayout will use this information to make decisions about how best to layout your panel. Because the size is 0x0, the repaint manager will ignore it. Try overriding the getPreferredSize method and return a appropriate size or use a layout manager that does not use the preferred size, like BorderLayout
Your paintComponent method MUST call super.paintComponet
I have no idea why I can't find a solution for this... I am trying to layout some AWT components in a flow layout. The only problem is the 'padding' between the components (Panels) when using a flow layout. This is what the applet currently looks like: http://i.stack.imgur.com/2KZgD.png
I need a way to set the Applet/Panels so that the two panels (black boxes) are touching (no 'padding'). The entire program is Swing free, all AWT, and I plan on keeping it that way. I feel this is a very simple solution, but I have not been able to find an answer.
This is the init() code from the applet class:
public void init() {
setLayout(new FlowLayout());
c1 = new TestPanel();
c2 = new TestPanel();
c1.setPreferredSize(new Dimension(640, 480));
c2.setPreferredSize(new Dimension(100, 480));
add(c1);
add(c2);
}
This is the TestPanel class I'm using:
public class TestPanel extends Panel {
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, this.getPreferredSize().width, this.getPreferredSize().height);
}
}
The default horizontal (and vertical) gap of FlowLayout is set to 5. Therefore, you must explicitly set the horizontal gap to 0.
FIRST APPROACH
Invoke setHgap(...) on the component's layout. Since the default layout of a JPanel is FlowLayout, simply do the following:
((FlowLayout)getLayout()).setHgap(0);
SECOND APPROACH
Use another FlowLayout constructor. That is, FlowLayout(int align, int hgap, int vgap). And simply do the following:
setLayout(new FlowLayout(FlowLayout.CENTER, 0, 5));