I created a separate class for JFrame and JPanel, then draw (fillOval in a JFrame class) and draw (fillOval in a JPanel class), and a button that will just animate the JPanel components. But the problem is, whenever i repaint the JPanel class; ---- The JFrame components disappeared. I don't understand why is this happening. I want the JFrame component be permanent for every animation done in JPanel class.
Sample Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TryRepaintIssue extends JFrame
{
public TryRepaintIssue(){
thePanel panel = new thePanel();
add(panel);
setSize(1000,1000);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public void paint(Graphics g){
super.paint(g);
g.fillOval(100,500,100,100);
}
public static void main(String[] args){
new TryRepaintIssue();
}
public static class thePanel extends JPanel{
private int y = 100, vector = 1;
public thePanel(){
JButton button = new JButton("Play");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
y += vector;
repaint();
}
});
add(button);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.red);
g2.fillOval(100,y,100,100);
}
}
}
The JFrame components disappeared.
The components do not disappear. The button and panel are still displayed.
I assume you mean the custom painting of the black circle disappears.
I don't understand why is this happening
The paint() method of the frame is responsible for painting all the child components of the frame. So it repaints the JPanel you add to the frame, which in turn paints the JButton you add to the panel.
It then paints the black circle on top of the panel.
When you click the button you repaint only the "panel" which causes the JButton and the red circle to be painted.
You lose the painting of the black circle because you no longer invoke the code to paint that circle.
If you want the black circle to remain you have a couple of options:
The best solution is to NOT override paint() on the frame. Instead do all the custom painting in your panel. So paint both the black and red circles.
repaint the entire frame in your ActionListener code:
//repaint();
SwingUtilities.windowForComponent(button).repaint();
use the Glass Pane as suggested in the answer by Tom.
You shouldn't override JFrame.paint, particularly without calling the super. Usually drawing in these situations is done on a glass pane.
Related
I'm practising to draw a shape on a JPanel by clicking on a Jbutton, but I cannot. It's been five hours that I'm surfing the web, but I cannot find the way to do it.
This is what I want to do: if I click on "Rectangle" button a rectangle appears under the buttons and if I click on "Circle" button a circle appears.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Shape extends JFrame {
JButton rec, circle;
static String botSelected;
Shape (){
frameSet ();
}
void frameSet(){
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(600,300);
rec = new JButton ("Rectangle");
circle = new JButton("Circle");
JPanel panel = new JPanel();
frame.add(panel);
panel.add(rec);
panel.add(circle);
Click clk = new Click();
rec.addActionListener(clk);
circle.addActionListener(clk);
}
public void paint (Graphics g){
super.paint(g);
if (botSelected.equals("Rectangle"))
g.fillRect(50,50,50,50);
else if (botSelected.equals("Circle"))
g.fillOval(50,50,50,50);
}
public static void main (String [] arg){
Shape s = new Shape();
}
}
class Click implements ActionListener{
public void actionPerformed (ActionEvent e){
Shape.botSelected = e.getActionCommand();
}
}
The first thing I would do is have a read through Painting in Swing and Performing custom painting to better understand how the painting process works.
Next you need to understand that JFrame is a bad choice for painting to. Why? Because it's multilayered.
A JFrame contains a JRootPane, which contains a JLayeredPane the contentPane, glassPane and the JMenuBar and in your example, it also contains a JPanel.
With the (default) exception of the glassPane, all these components are opaque.
While it's possible to have something drawn in the paint method show it, if any of the other components paint themselves, it will be wiped clean - this is because Swing components can actually be painted independently of each other, with having to have the parent paint itself first.
A better solution is to start by extending from JPanel and override its paintComponent method.
For simplicity, I'd also encourage you to implement the ActionListener against this class as well, it will allow the actionPerformed method to access the properties of the component and, in your case, call repaint to trigger a paint cycle when you want to update the UI.
Here is a derived example from your code.
As #MadProgrammer said, don't extend JFrame.
In the following example, here are the major changes :
give a non-null value to botSelected, or the first calls to paintComponent will give you a NullPointerException
the class now extends JPanel, and overrides paintComponent for custom painting
the ActionListener is an anonymous class, because you don't need a separate class, and it has direct access to the fields from Shape
botSelected is no longer static (see above point)
.
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Shape extends JPanel {
JButton rec, circle;
String botSelected = "";// don't let it be null, it would make paintComponent crash on startup
Shape() {
frameSet();
}
void frameSet() {
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(600, 300);
rec = new JButton("Rectangle");
circle = new JButton("Circle");
frame.add(this);
this.add(rec);
this.add(circle);
// anonymous class, has access to fields from the outer class Shape
ActionListener clk = new ActionListener() {
public void actionPerformed(final ActionEvent e) {
botSelected = e.getActionCommand();
repaint();
}
};
rec.addActionListener(clk);
circle.addActionListener(clk);
}
//custom painting of the JPanel
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
if (botSelected.equals("Rectangle")) {
g.fillRect(50, 50, 50, 50);
} else if (botSelected.equals("Circle")) {
g.fillOval(50, 50, 50, 50);
}
}
public static void main(final String[] arg) {
Shape s = new Shape();
}
}
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 am relatively new to Java Graphics. I want to draw 20 x 80 rectangle at (X,Y) coordinates in JPanel when user clicks a JButton. (where 'X' and 'Y' are coming from 2 JTextFields) .
I have read many questions and tutorial, but could not solve a problem. In some cases, I can draw rectangle but cannot draw new rectangle without emptying JPanel.
Here is my code :
public class CustomPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // first draw a clear/empty panel
g.draw3DRect(Integer.parseInt(x.getText()),Integer.parseInt(y.getText()), 20, 80, true);
// then draw using your custom logic.
}
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
//Code for frame
//Code for JTextfields x and y
JButton btnDraw = new JButton("Draw");
btnDraw.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
panel= new CustomPanel();
panel.setBounds(406, 59, 407, 297);
frame.getContentPane().add(panel);
frame.revalidate();
}
});
btnDraw.setBounds(286, 339, 89, 23);
frame.getContentPane().add(btnDraw);
}
You ActionListener code is wrong. You don't want to create a new panel, you want to add a Rectangle to the existing panel.
When you create the GUI you should add two panels to the GUI:
The first panel will be an empty panel that will do your custom painting. You would generally add this to the CENTER of the frame
The second panel will contain the "Draw" button. You would generally add this panel to the PAGE_END. Then when you click the Draw button you invoke a method like addRectangle(...) in your custom painting panel so the panel can paint the Rectangle.
Check out Custom Painting Approaches for the two common ways to do custom painting:
Keep a List of Object to paint and then in the paintComponent() method you iterate the LIst an paint each object.
Create a BufferedImage and then just paint the Rectangle onto the BufferedImage, then you can just paint the BufferedImage either in a JLabel or in your paintComponent() method.
I'm building a GUI for a data processing algorithm. I can instantiate the window, give it a background, title, etc., but when I try adding panels to it, I run into trouble. What I'm really looking for more than a proofreader is a suggestion for the sequence in which to build, configure, and add objects in Java Swing so that they behave correctly, in a generic sense. So, is this the best way to build a JFrame with a different-colored panel in it?
Declare JFrame
Set JFrame color (background color)
Declare JPanel (box to represent data graphically)
Set JPanel color (box color)
Add JPanel to JFrame
Set JFrame to visible = true
It makes sense intuitively but it doesn't seem to work, no matter what I do. I've found step-by-step instructions elsewhere but they tend to explain what to type more than why you're typing it, so you get a very narrow understanding of what's going on. Thanks for any help!
Below is the full code; I hesitated to post it because I'd begun experimenting with Graphics2D and it isn't well-commented, but if it helps:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class GUI extends JFrame
{
JFrame mainWindow = new JFrame();
JPanel backgroundPanel = new JPanel();
JPanel subPanel = new JPanel();
Color background = new Color(40,40,40);
Color subWindow = new Color(255, 255, 255);
TitledBorder title = BorderFactory.createTitledBorder("title");
Rectangle rect1 = new Rectangle(10, 10, 40, 40);
Graphics2D g;
public static void main (String[] args)
{
new GUI();
}
public GUI()
{
initializeGUI();
}
private void initializeGUI()
{
mainWindow.setSize(1340, 880);
backgroundPanel.setBackground(background);
subPanel.setBackground(subWindow);
subPanel.setBorder(title);
mainWindow.setTitle("Ed");
mainWindow.setLocationRelativeTo(null);
mainWindow.setDefaultCloseOperation(EXIT_ON_CLOSE);
mainWindow.add(backgroundPanel);
backgroundPanel.add(subPanel);
updateGUI();
}
public void updateGUI()
{
mainWindow.setVisible(false);
mainWindow.setVisible(true);
}
public void paintComponent(Graphics g)
{
this.g.setColor(subWindow);
this.g.fill(rect1);
this.g = (Graphics2D) g;
}
}
Let's break this down....
public class GUI extends JFrame {
JFrame mainWindow = new JFrame();
There is no need to extend from JFrame as you are neither using it nor are you adding any value to the class.
This...
public void paintComponent(Graphics g) {
this.g.setColor(subWindow);
this.g.fill(rect1);
this.g = (Graphics2D) g;
}
is doing nothing and will never be called, as nothing you've extended from implements a paintComponent method (that is, JFrame does not have a paintComponent methd) (and you class is not attached to anything displayed on the screen anyway). Also, you should NEVER maintain a reference to ANY Graphics context you did not create yourself.
The reason that subPanel is appearing so "small" is because it has not definable size, aside from the border.
You could rectify this in one of three ways...
You could change the layout manager of backgroundPanel to something like BorderLayout
You could override the getPreferredSize method of the subPanel to return a more suitable size or
You could add other components to it and let the layout manager figure it out...
In any case, you should have a look at Laying Out Components Within a Container.
You should also have a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting is done in Swing
i have the following code for animating a ball from top left corner towards the bottom right corner.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class MainFrame{
int i=0,j=0;
JFrame frame = new JFrame();
public void go(){
Animation anim = new Animation();
anim.setBackground(Color.red);//Why color is not changing to red for the panel.
frame.getContentPane().add(anim);
frame.setVisible(true);
frame.setSize(475,475);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for(i=0,j=0;i<frame.getHeight()&&j<frame.getWidth();++i,++j){
anim.repaint();//Main problem is here,described below.
try{
Thread.sleep(50);
}
catch(Exception ex){}
}
}
public static void main(String[] args) {
MainFrame mf = new MainFrame();
mf.go();
}
class Animation extends JPanel{
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
g.fillOval(i,j,25,25);
}
}
}
Questions
When i do anim.repaint() inside the method go i don't get the ball animating from top left corner to bottom right corner but it gets smeared down the path.But if i replace it with frame.repaint() i get the desired result that is a moving ball.So what is the difference between these two calls to repaint?
Why the color of panel is not changing after anim.setBackground(Color.red); in go method?
If you run this programe you will find that the ball is not exactly going at the bottom edge,so how can i acheive that?
);//Why color is not changing to red for the panel
You should always invoke super.paintComponent(g) when you override the paintComponent(...) method. The default code is responsible for painting the background.
but it gets smeared down the path
Same answer as above. You need the background to be painted so that all the old paintings are removed.
the ball is not exactly going at the bottom edge
If you mean the ball is not on an exact diagonal on the panel, that is because you set the size of the frame manually and you did not account for the size of the titlebar and borders. If you want the panel to be (475, 475) then override the getPreferredSize() method of the panel to return that dimension. Then in your code you replace the frame.setSize() with frame.pack().