Simple custom Swing JComponent always flat - java

I'm just playing with Swing and I'm working on a really simple Swing component. I have a component inherited from JComponent class and its UI inherited from ComponentUI. The paint() method looks like this:
public void paint(Graphics g, JComponent c) {
int x = c.getX();
int y = c.getY();
c.setBounds(x, y, 100, 25);
int width = c.getWidth();
int height = c.getHeight();
Rectangle r = g.getClipBounds();
g.fillRect(0, 0, 10, 10);
g.drawString("Baf!", 3, 3);
}
But it is totally impossible to get another value of r.height than 1. The component is width as given, but height allways one point only. Has anybody else experiences with suchlike components? Unfortunately there is no any easy tutorial. All tutorials are incomprehensible complicated (or obsolete).
It seems, that the layout manager resizes this component allways to 1 height (regardless to minimal value).

Never invoke setBound() in a painting method. That is a job for the layout manager, not your painting code.
I would guess the main problem (other than Heisenbug's points) are that you don't give you component a size. This is done by overriding the getPreferredSize() to return a size appropriate to your component.
Read the section from the Swing tutorial on Custom Painting for more information and working examples.

There are several problems with your code:
For what concern the class extending JComponent:
public void paint(Graphics g, JComponent c) {}
isn't a valid signature so you are not overriding the method paint, but create a new paint method.
you should override paintComponent(Graphics g) method instead of paint.
Because of you are extending a JComponent you need first call super.paintComponent(g) inside your overridden paintComponent method:
public class JPanelExtended{
public void paintComponent(Graphics g){
super.paintComponent(g);
...
}
}
For what concern the class extending ComponentUI, you should even there call explicitly the method paint on the super class:
public void paint(Graphics g, JComponent c) {
super.paint(g,c);
}
EDIT:
a little suggestion: when you want to override a method, it's quite useful to put the #override notation before the signature:
#Override
public void superMethodToBeOverridden(){}
This way you will be notified by the compiler with an error message, in the case you are defining a new method and not overriding an existing one.

Related

Drawing shapes does not work in graphics2D

The below code is to display a triangle in applet.But it does not work for me. If i pass Graphics g instead of Graphics2D g then it works fine.But i want to know what the mistake i am doing while using Graphics g. I am new to java and learning from some online tutorials. So please correct my program and tell me what mistake i am doing.
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Vehicle extends JFrame {
final int WIDTH = 900;
int HEIGHT = 650;
public void init() {
setSize(WIDTH, HEIGHT);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public void paint(Graphics2D g) {
Graphics2D g2 = (Graphics2D) g;
Dimension d = getSize();
System.out.println(d);
int[] yPoints={150,250,250};
int[] xPoints={200,150,250};
g2.setColor(Color.green);
g2.drawPolygon(xPoints, yPoints, 3);
g2.drawRect(100, 100, 100, 100);
g2.setColor(Color.red);
g2.fillPolygon(xPoints, yPoints, 3);
}
public static void main(String[] args) {
Vehicle v= new Vehicle();
v.init();
}
}
The method paint(Graphics g) is defined in java.awt.Component. This is more or less legacy code. However, this API hasn't changed for a long time and is inherited by modern Swing components, such as JFrame.
You can't simply change the method signature (in your case to paint(Graphics2D g)), you have to live with the signature defined by the API.
So, in your case, the paint(Graphics2D g) does not override the API method paint(Graphics g), and thus is not called when updating the JFrame. Nothing is painted.
Change your code like this:
----------------%<----------------------
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public void paint(Graphics g) { // <-- Change from Graphics2D to Graphics
Graphics2D g2 = (Graphics2D) g;
Dimension d = getSize();
System.out.println(d);
int[] yPoints={150,250,250};
int[] xPoints={200,150,250};
---------------->%----------------------
Take a good look at what your program is doing before looking for assistance. (Not scolding you, just trying to help for the future.)
You have public void paint(Graphics2D g), which, as Petersen stated, is not a method in the JFrame class. A good trick to use to check if your methods in swing will do what tutorials say they will do is to put #Override above the method. If it errors, it means that method is not in JFrame or whatever superclass you have. Otherwise, it is overriding the method.
Additionally, another flag would be the next line: Graphics2D g2 = (Graphics2D) g;
Here you're casting a Graphics2D object as a Graphics2D object... which should never need to happen.. haha
If you are just getting started with swing and Graphics2D I suggest learning some of the Panels systems and other low-level things like drawImage or drawString, if you haven't done so already.
Working with Java2D it often helps to imagine your application as an animation. Imagine that at one point one specific frame(I am talking about animation frames okay) is drawn on the screen. When some event occurs we replace this frame with another frame incorporating the change much like the way cartoons or animation movies are built. How we draw animation frames? How do we switch between frames?
Java API provides a paint(graphics g) method for its components to be drawn. And then to update the same component you really call repaint() which simply calls paint(Graphics g) again and displays any changes on to the screen. This API method has a strict signature that we shall follow. It is paint(Graphics g). But Graphics2D is much like a cool kid on the block that provides more functionality because Graphics was too old(legacy) and not cool anymore. So how do we do this because paint(Graphics g) only accepts Graphics not Graphics2D? Graphics2D is a subclass of Graphics okay.
Protocol is simple,
Use the legacy paint(Graphics g) method of every component.
Once we have used super.paint(Graphics g) (if needed) ,we can typecast the Graphic object that we were using for legacy purposes to Graphics2D to get enhance functionality.(you have done this step right)
Problem with your code:
It is only the signature. Java and the repaint() expect the method to be paint(Graphics g) but you have changed the signature and now the above protocol (or contract) is broken. The idea is to keep the contract(by not changing the signature) and then typecast and override to add extended features. Java is not able to find paint(Graphics g) and so repaint() will not also find paint(Graphics g).

Java swing draw objects over other components

I am trying to make a small paint program. I am drawing objects over a JPanel which is on top of JFrame (I am using Netbeans 6.9). I have some basic functionality like font, line and fillRectangle. I am using the standard method to draw which is to override paintComponent().
class .... extends JPanel
{
#Override
void paintComponents(Graphics g)
{
.......
}
}
The problem is that when I draw a text,line over a region then it is drawn behind it rather than on top of it. Basically I want to draw objects on top of all other objects that have previously been drawn on the JPanel. I really do not want to switch to other types of layered pane. One very naive method will be to undo every object and paint them in reverse order (last one first).
You will need to override paintComponent(Graphics g) and do not forget to call super.paintComponent(Graphics g);
class .... extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);//honor paintComponent an call super to draw other components that were added to the JPanel
.......
}
}
You may also need to override getPreferredSize(..) of JPanel and return an appropriate size so the JPanel will be visible:
class .... extends JPanel
{
#Override
public Dimension getPreferredSize()
{
return new Dimension(300,300);
}
}
EDIT:
Depending on what you are doing you may also want to have a look at the GlassPane which will allow you to set a transparent pane over the entire JFrame window and can be painted too, which will cause the graphics to be drawn above all others like so:
This is an old question, but I had the same problem and solved it by overriding paintChildren(Graphics g) instead of paintComponent.
As the Oracle documentation states, the order of execution for the three paint methods is:
protected void paintComponent(Graphics g)
protected void paintBorder(Graphics g)
protected void paintChildren(Graphics g)
So, paintChildren(g) is run last, which means whatever we draw inside it is drawn on top of all previously drawn components.

Redrawing graphics in Java

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.

Java Paint Problem

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.

Java Swing NullPointerException when drawing

I'm using a custom JLayeredPane.
I have several Shapes which needed to be drawn on different layers in the JLayeredPane.
To test this I create a JPanel and ask its graphics. Then I draw a test rectangle on that JPanel (preparing the graphics) and in my paintComponent method from the JLayeredPane I finally draw everything. But this fails (NullPointerException).
public class MyCustomPanel extends JLayeredPane {
// test
JPanel testpane;
Graphics g2;
// test
// constructor
public MyCustomPanel() {
testpane = new JPanel();
this.add(testpane, new Integer(14));
g2 = testpane.getGraphics();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2.drawRect(10, 10, 300, 300);
}
}
// run:
//Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
// at view.MyCustomPanel.paintComponent(MyCustomPanel.java:65)
Why can't I draw on such a JPanel from within my JLayeredPane? I can draw directly on my JLayeredPane from within my paintComponent method but that's on the default Panel from the JLayeredPane. I need to create and draw on several layers which are added in my JLayeredPane.
What am I doing wrong? :s
You should use g2 casting the Graphics that is passed to you:
Graphics2D g2 = (Graphics2D)g;
Why don't you try decoupling things?
class InnerPanel extends JPanel
{
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawRect(....);
}
}
class MyLayered extends JLayeredPane()
{
MyLayered()
{
this.add(new InnerPanel(), 14);
}
}
this makes more sense..
Also because you are trying to do something that doesn't agree with Swing behaviour.
Swing will care by itself to call the appropriate paint methods over things that must be displayed, and to go with this protocol you should tell Graphics objects what to draw when Swing asks it to your objects (calling the paint) method, not when you want to do it.
In this way whenever Swing wants to draw your JLayeredPane you just draw things on a Graphic object of other things without considering that Swing will call their appropriate methods when it's the right time to do so.
In conclusion: you can't draw something on a Graphic object when you want it. You can do it just inside methods invoked by Swing, because otherwise Graphics of these objects doesn't mean anything
The variable g2 is probably null because you set it in the constructor, not when drawing. Instead, use the "g" that was passed in.
You can only get a legitimate Graphics from a component that is currently being painted. Otherwise, it's not valid. At the point you're requesting it, the MyCustomPanel() isn't being displayed, and neither is testpane.

Categories