I'm just getting into graphics in Java and I have a problem. I created a JFrame window (NetBeans Designer) with a JPanel panel and I drew some graphics on it. Then I added a JButton that changed a variable, which would change the X position of a square on JPanel.
On button press this code would execute:
drawObject.setX(150);
drawObject.repaint();
drawObject is an instance of this class:
public class sola extends JPanel {
private int x = 10;
#Override
public void paintComponent(Graphics g){
super.paintComponents(g);
super.setBackground(Color.WHITE);
g.setColor(Color.ORANGE);
g.fill3DRect(x, 160, 100, 50, true);
}
public void setX(int xX){
x = xX;
}
}
Now, when I press the JButton, the rectangle does move to the new position, however it is still visible in the old position. Only when i resize the window does it refresh and the old rectangle disappears. How can i solve this problem, so that when i press the button, the rectangle is only visible in the new position?
It's
super.paintComponent(g);
not
super.paintComponents(g); // note the s at the edn
Big difference between the two! The first one tells your JPanel to do all the housekeeping functions normally performed by the paintComponent method, including repainting the background (key for your project). The second, the one your calling doesn't do any of the above functionality. So my advice is to get rid of the trailing s in your super call.
You can use the following methods from JComponent: ( http://download.oracle.com/javase/6/docs/api/javax/swing/JComponent.html )
void repaint(long tm, int x, int y, int width, int height)
Adds the specified region to the dirty region list if the component is showing.
void repaint(Rectangle r)
Adds the specified region to the dirty region list if the component is showing.
You can call those before redraw()
You could use repaint() method to do tis.
If you use the paintComponent() on the panel. You should IMHO take care of the painting in the whole panel. There is no code in your example which takes care about deleting the old painted rectangles.
What i recommend is creating an own Component for your rectangles. (You could extend from Component) then you can override the paintComponent method of these classes as you did in your panel. Because the Panel should act as a container component. Not as drawing the rectangles itsself.
Know add instances of these components to a normal JPanel. This should then update as expected.
Related
What my application is meant to do is change the background and foreground on a click of a radio button and change the shape of the item based on a radio button.I am trying to get my application to actively change shape based on the radio button that is selected.I have the background and foreground working just not the shape. I have seen another post kinda like this but it has a submit button and does not use the JSlider
Below is what I have been messing with and cannot seem to get the program to execute correctly. I have gotten the shape to change but then the slider breaks. Am i approaching this the wrong way?
public class OvalPanel extends JPanel
{
private int diameter = 10; // default diameter
// draw an oval of the specified diameter
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(rectFillRadioButton.isSelected()){
g.fillRect(10,10,10,10);
//repaint();
}
if(ovalFillRadioButton.isSelected()){
g.fillOval(10,10,10,10);
//repaint();
}
}
// validate and set diameter, then repaint
public void setDiameter(int newDiameter)
{
// if diameter invalid, default to 10
diameter = (newDiameter >= 0 ? newDiameter : 10);
repaint(); // repaint panel
}
// used by layout manager to determine preferred size
public Dimension getPreferredSize()
{
return new Dimension(200, 200);
}
// used by layout manager to determine minimum size
public Dimension getMinimumSize()
{
return getPreferredSize();
}
}
this is the class that i initially have that sets the paintComponent. I also have
private class TopRadioButtonHandler extends JFrame implements ItemListener {
private Graphics panel;
public TopRadioButtonHandler(Graphics p) {
panel = p;
}
#Override
public void itemStateChanged(ItemEvent event) {
if(rectFillRadioButton.isSelected()){
panel = myPanel.getGraphics();
panel.fillRect(10,10,10,10);
repaint();
}
if(ovalFillRadioButton.isSelected()){
panel = myPanel.getGraphics();
panel.fillOval(10,10,10,10);
repaint();
}
}
}
i dont think i need the repaint but when i use this method my JSlider stops working.
Am i approaching this the wrong way?
Yes, the paintComponent() method should not be referencing another Swing component.
When you do custom painting, the paintComponent() should only paint the current state of your component.
For example when you use a Jlabel you have methods like setText() and setIcon() to set the text and icon you want to paint.
You already have a method, setDiameter() which is a good start. However, your painting code just hard codes the size of the oval/rectangle. The painting methods should reference you diameter variable.
Now, you need another property to idicate whether to paint an oval or a rectangle. So maybe you need a property like setPaintOval(boolean paintOval).
Then your painting code could be:
If (paintOval)
g.fillOval(10, 10, diameter, diameter);
else
g.fillRect(10, 10, diameter, diameter);
Of course the problem with this approach is that you can only paint two objects.
Also, you should never invoke repaint() in a painting method. The repaint() should only be invoked from your setter methods when you change the state of the component.
but then the slider breaks
The code you posted has nothing to do with a slider.
I'm guessing you want the slider to change the diameter of the oval? Well you need to add a ChangeListener to the slider and then invoke your setDiameter() method with the slider value.
Read the section from the Swing tutorial on How to Use Sliders for a working example.
This question already has answers here:
Repaint without clearing
(2 answers)
Closed 3 years ago.
I am implementing a simple Canvas where items can be drawn like a person would in real life with a paper and a pencil, without clearing the entire page every time an object is drawn.
What I have so far...
A Canvas to implement the drawing:
public class Canvas extends JPanel {
private final Random random = new Random();
public Canvas() {
setOpaque(false); // I thought setting this flag makes the drawn pixels be preserved...
}
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(random.nextInt(getWidth()), random.nextInt(getHeight()), 5, 5);
}
}
The Window as an actual window:
public class Window extends JFrame {
public Window(Canvas canvas) {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(canvas);
pack();
setVisible(true);
}
}
And the Controller with an entry-point to the application. Also starts a timer so the repaint on Canvas is called every second to force drawing another circle.
public class Controller {
public static void main(String[] args) {
Canvas canvas = new Canvas();
SwingUtilities.invokeLater(() -> new Window(canvas));
new Timer(1000, e -> canvas.repaint()).start();
}
}
The problem is that whenever a new circle is drawn, the previous one is cleared. Seems like there is still some process filling the JPanel or maybe the entire JFrame with white color.
Painting in Swing is destructive. It is an expected requirement that each time a component is painted, it is painted from scratch, again.
You need to define a model which maintains the information needed in order to restore the state from scratch.
Your paint routines would then iterate this model and draw the elements each time.
This has the benefit of allowing you to modify the model, removing or inserting elements, which would allow you to update what is been painted simply.
Alternatively, you could use a "buffer" (ie a BufferedImage) on to which all you painting is done, you would then simply paint the image to the component each time the component is painted.
This, however, means that you can't undo or layer the paintings, it's drawn directly to the image. It also makes resizing the drawing image area more difficult, as you need to make these updates manually, where as the "model" based implementation is far more adaptable
Consider calling the alternate constructor of repaint(...)
repaint(long tm, int x, int y, int width, int height)
This allows you to set a specified area to be repainted.
Also you can just store what you drew in a list and then reprint the drawing to the canvas after repaint is called.
I'm trying to make a simple application to draw shapes to a panel in a jFrame. The GUI is a NetBeans generated jFrame. The application has three panels. the first two hold button groups to select the shape and a color. There's a button to draw the shape to the third panel once the selections are made.
What the GUI looks like
Unfortunately I'm having no love and can't make it work. For now I just want to get the button to draw a shape then I'll add the button functionality. Here's the code I have so far.
private void btnDrawShapeActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Draw shape = new Draw();
pnlDrawPad.add(shape); //pnlDrawPad is the name of the jPanel
shape.drawing();
}
import javax.swing.*;
import java.awt.*;
public class Draw extends JPanel{
public Draw(){
super();
}
public void drawing(){
repaint();
}
//#Override <-- gives error "method does not override or implement a method from a supertype"
public void paintCompnent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.drawString("Hello World!",40,30);
}
}
When I click the "Draw Shape" button nothing happens. I've been searching for a couple days now and am not finding the answer. Actually, its making me more confused as to what to do.
How to deal with this?
Don't keep creating new panels for each shape.
Instead you need a single panel with an ArrayList of objects you want to paint. Then you customize the paintComponent(...) method of the panel to iterate through all the objects in the panel and paint them.
So you button will just add another object to the ArrayList and then invoke repaint() on the panel.
Check out Custom Painting Approaches. The DrawOnComponent example shows how to paint a Rectangle objects from an ArrayList to get you started.
I found that with NetBeans you don't need to Override the paintComponents() method. Since all the GUI stuff is done when you design it, you merely need to add the drawings to the panel or what ever you need to do. I found you that by declaring and instantiating a 2D graphics object then drawing it to the place you want.
private void btnDrawShapeActionPerformed(java.awt.event.ActionEvent evt) {
int centerX;
int centerY;
Graphics2D drawFx = (Graphics2D)pnlDrawPad.getGraphics();
drawFx.setColor(Color.ORANGE);
centerX = (int)(458*Math.random());
centerY = (int)(440*Math.random());
drawFx.fillOval(centerX-50, centerY-50, 100, 100);
drawFx.dispose();
}
Worked like a charm so now every time I push the draw shape button it draws a shape to the panel in a different location every time. I've since put in the radio button functionality to change shape color and fill and added a button to clear the screen.
Not sure if this is the ideal way to do it but for this stupid simple little program it works well enough and I'm done stressing over it.
I have a little issue with my GUI in NetBeans. I draw images (dots) when a user clics in a JPanel at the mouse clic location. This part works just fine. I store each image locations in two different ArrayList that contains the X location and Y location. Now what I want to do is to delete the latest image drawn in the Panel after a button is clicked. So what I did is remove the last index of both ArrayList, and then call repaint() to draw all the images from the locations in both X and Y ArrayList (code below).
What is weird is that I need to resize the GUI (put it in full screen or just change its' size) in order for the drawn images to show up again in the JPanel otherwise, the panel remains empty.
Here's the parts of code that are affected :
public void paint(Graphics g) {
super.paint(g);
for(int i=0;i<=listePointsX.size()-1;i++) {
try{
BufferedImage icon = ImageIO.read(getClass().getResourceAsStream("/myimage.png"));
Graphics graphe = jPanel1.getGraphics();
graphe.setColor(Color.BLACK);
graphe.drawImage(icon, this.listePointsX.get(i),this.listePointsY.get(i), rootPane);
}catch(Exception e1){
}
}
private void jButtonUndoActionPerformed(java.awt.event.ActionEvent evt) {
if(listePointsX.size()>0){
int lastObject= listePointsX.size();
listePointsX.remove(lastObject-1);
listePointsY.remove(lastObject-1);
jPanel1.repaint();
}
else{
}
}
Any idea what I need to do to some kind of "refresh" the whole thing? Am I doing something wrong? Tried searching about that but did not find anyting...
Graphics graphe = jPanel1.getGraphics(); is NOT how painting should work, instead, you should have overriden the panel's paintComponent method and painted the points within in.
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works in Swing
Instead, your panel should be doing ALL the work, managing the points in the ArrayList and painting them. You parent component "might" have the ability to add or remove points if that meets your design requirements, but the core responsibility remains with the panel.
Avoid performing any long running or block operations within the paint methods, they should run as fast as possible. Since the image never changes, you should simply load once (either when the class is constructed or when you first need the image) and keep using the same reference.
Alright it worked just fine now. I had to do it the way you told me up here. I created a new class that extends jPanel (below). Then in my main Form, had to create an object of this class. Whenever a user makes a click, it will call this Drawing class object and add an item to the ArrayList (this object manages everything regarding created points... It looks like this :
public class MyDrawingClass extends JPanel {
ArrayList<Integer> arrayListPointX = new ArrayList<>();
ArrayList<Integer> arrayListPointY = new ArrayList<>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
try{
BufferedImage icon = ImageIO.read(getClass().getResourceAsStream("/images/dot.png"));
g.setColor(Color.BLACK);
if(arrayListPointX.size()<=0){
...
}
else{
for(int i=0;i<listePointsX.size();i++){
g.setColor(Color.BLACK);
g.drawImage(icon, listePointsX.get(i), listePointsY.get(i), rootPane);
}
}
}catch(Exception e){
...
}
}
So if I want to "undo", let's say my object of "MyDrawingClass" is called "draw". I can do : draw.arrayListPointX.remove(draw.arrayListPointX.size()-1); and call repaint(); to display remaining points.
Thanks for your tips appreciate it ! :)
Ok I'm wondering why the code below will not display the JLabel.
MyPanel is getting added correctly to a JFrame and everything because it all displays but will not draw the JLabel. Any help is appreciated.
public class MyPanel extends JPanel {
private Root root;
...
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
root.paint(g2);
}
}
class Root {
private Node1 node1;
...
public void paint(Graphics g) {
node1.paint(g);
}
}
class Node1 {
...
public void paint(Graphics g) {
JLabel jtp = new JLabel();
jtp.setLocation((int) x, (int) y);
jtp.setSize((int) width, (int) height);
jtp.setLocation(40, 40);
jtp.setSize(40, 40);
jtp.setText("Hello world");
//jtp.setVisible(true);
jtp.paint(g);
}
}
I suggest that you don't add Components to a Container in a paint method as 1) you do not have absolute control when or even if a paint method will be called and 2) paint and paintComponent have to be as blazing fast as possible, and this is not the time or place to update a GUI. 3) Since paint is often called many times, you will be adding components many times to your container, and all out of your direct control.
Also, while you're adding a component into Root (whatever Root is since it doesn't extend JComponent, JPanel, or similar) in the paint method, the Root object is never added to anything else that I can tell, and so it makes sense that nothing "added" to a component that is not added eventually to a top-level window will be visible.
Bottom line: I think you need a gui re-design as your solution. If you tell us more about it we can help you with it. Next we'll need to talk about use of layout managers and why setting absolute position and sizes of components is usually frowned on.
If anything I say is confusing, please ask for clarification, or if anything is wrong, please help me correct it!
You should not create your JLabel inside the paint method - instead, create it once when initializing your MyPanel. Your label is kind of a renderer component for your nodes, which in principle is a good thing. You may look how the renderers for JTable, JList, JTree work.
In your case, don't set the location of your label (it does not change anything, since it's paint-method expects its graphics object to be oriented by its own upper left corner), instead translate the Graphics-context:
Graphics copy = g.create((int)x, (int)y, (int)width, (int)height);
jtp.paint(copy);
(Graphics2D has some more fancy methods for shifting, rotating, scaling the context, too.)
Other than this, I don't see any problems. Make sure your Node1.paint() method gets actually called by putting some System.out.println() in there.