I have a problem making an inner class that extends from JPanel to draw anything on it. I overrided the paintComponent method, and whatever I set to draw from here works fine, but using another method to draw does not work.
Here is my inner class code:
private class Plot extends JPanel {
public Plot() {
this.setBackground(Color.WHITE);
}
#Override
public void paintComponent(Graphics graphic) {
super.paintComponent(graphic);
Graphics2D graphic2d = (Graphics2D) graphic;
graphic2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
graphic2d.drawOval(0, 0, this.getWidth() - 1, this.getHeight() - 1);
}
public void drawTitle(final String title) {
Graphics2D graphic2d = (Graphics2D) this.getGraphics();
graphic2d.setColor(Color.red);
graphic2d.drawString(title, 1, 10);
}
}
Notice the drawTitle method. I just want a custom text to be shown. In my outer class which extends from a JFrame I create an instance of this inner class like this:
private Plot plot;
/** Creates new form GraphicsView */
public GraphicsView() {
initComponents();
plot = new Plot();
this.add(plot, BorderLayout.CENTER);
}
public void drawTitle(final String title) {
this.plot.drawTitle(title);
}
I even create a convenient method to call the inner class drawTitle method (with the same name). I do this because I want this JFrame outer class to be visible on button click, once it is visible (which ensures the init of the graphics) I call the outer class drawTitle which in turn calls the inner class method with the same name and where the string show be drawn... but this does not work, I can't see it on the panel. Here is my button click event:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
GraphicsView view = new GraphicsView();
view.setVisible(true);
view.drawTitle("Hello");
}
Thanks in advance, I will appreciate any help. :)
I overrided the paintComponent method, and whatever I set to draw from here works fine
Well, there is the answer to the question. Do all your drawing from the paintComponent() method.
but using another method to draw does not work.
Don't use the getGraphics() method. You should only ever use the Graphics objects passed to the paintComponent() method.
You can't control when Swing repaints() a component. Therefore every time the component is repainted the paintComponent() method is invoked and your other custom painting code will be lost.
Just call the drawTitle() function in the paintComponent override and pass the graphics as an argument. Something like this:
#Override
public void paintComponent(Graphics graphic) {
super.paintComponent(graphic);
Graphics2D graphic2d = (Graphics2D) graphic;
graphic2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
graphic2d.drawOval(0, 0, this.getWidth() - 1, this.getHeight() - 1);
drawTitle(graphic, title);
}
public void drawTitle(Graphics g, final String title) {
Graphics2D graphic2d = (Graphics2D) g;
graphic2d.setColor(Color.red);
graphic2d.drawString(title, 1, 10);
}
Also try to make the title a data member of the class. This might prove helpful later.
Related
I'm new to Java and don't know exactly what the cause.Let me explain the issue
I created a Rectangle Shape and its working, then i thought about changing its color to black for some testing but it seems not working below is my code.
When i call the method from paintComponent itself then its working but if i do the same from any other method then its not changing the color. I tried calling the method repaint also but still the same
public class Meme extends JPanel {
Rectangle2D.Float myRect = new Rectangle2D.Float(90, 90, 90, 90);
Graphics2D graphics2d;
public void DRAW() {
graphics2d.setColor(new Color(0, 0, 200));
graphics2d.fill(myRect);
}
public void ChangeColour() {
System.out.println("Called");
graphics2d.setPaint(Color.BLACK);
System.out.println("Called2");
graphics2d.fill(myRect);
System.out.println("Called3");
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
graphics2d = (Graphics2D) g;
graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
DRAW();
}
}
Button click listener method
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
meme1.ChangeColour();
}
As far as I can remember, whenever you change some properties (color in this case), you have to call repaint. This will invoke a call to paintComponent and the frame will be drawn once again.
In your case, I am guessing even if you call repaint after changing color, the DRAW method gets called again in paintComponent which resets the changed color back to (0, 0, 200). Therefore, you don't see any change in the screen. But when you call changeColor in paintComponent method (assuming after the call to DRAW), the change of color persists and does not get overridden.
POSSIBLE SOLUTION
Just keep the color stored somewhere else. Like
Color myColor = new Color(0,0,200);
then in DRAW:
private void DRAW() {
graphics2d.setColor(myColor);
graphics2d.fill(myRect);
}
and in ChangeColor:
private void ChangeColour() {
myColor = Color.BLACK;
}
Hope it helps.
update your function like this
public void ChangeColour() {
System.out.println("Called");
graphics2d.setColor(new Color(1, 1, 200));
System.out.println("Called2");
graphics2d.fill(myRect);
System.out.println("Called3");
}
Painting in Swing is both passive and destructive. That is, a paint pass can occur at anytime for any number of reasons, many which you don't control. Destructive means, on each paint pass you are expected to repaint the entire component from scratch.
In Swing, you update the state you want change and then call repaint to trigger a new paint pass.
Painting should only ever paint the current state, it should never try and change it
public class Meme extends JPanel {
Rectangle2D.Float myRect = new Rectangle2D.Float(90, 90, 90, 90);
private Color color;
public void draw(Graphics2D graphics2d) {
graphics2d.setColor(color);
graphics2d.fill(myRect);
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void ChangeColour() {
color = Color.BLACK;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
graphics2d = (Graphics2D) g.create();
graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
draw(graphics2d);
graphics2d.dispose();
}
}
Also, the graphics context passed to your component is shared with all the other components, so it's important that any significant changes you make to the context are undone before the method exists - in most cases, it's just a simple case of calling create on the Graphics context to snapshot it state and dispose (on the copy you created) when you're done
I have a java form implemented using swing on which I want to place a number of panels in which I can draw on using the graphics2D package.
To do this, I implement the panels using an extension of JPanel thus:
public class GraphicsPanel extends JPanel
{
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g2d);
// Need to specify a function from the calling class here
MethodFromCallingClass();
}
}
In the calling class I have
public GraphicsPanel Graphics1= new GraphicsPanel() ;
public void Graphics1_Paint()
{
// Code to draw stuff on the Graphics1 panel
}
So question is how do I pass the function (Graphics1_Paint) from the calling class to the GraphicsPanel class?
I've tried reading about interfaces and lambdas but so far they make no sense.
Can anyone enlighten me please.
I think the easiest way is to pass the calling class (or some other interface) to the constructor of your GraphicsPanel like
public class GraphicsPanel extends JPanel {
private CallingClass cc;
public GraphicsPanel(CallingClass cc) {
this.cc = cc;
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g2d);
cc.MethodFromCallingClass(); // <-- invoke a call-back.
}
}
So it sounds like you want to define what is drawn outside of the custom JPanel class, where you can pass what you want drawn to the JPanel instance at anytime. This is possible.
First, define an interface that contains one method that draws with a Graphics2D instance:
public interface DrawGraphics()
{
public void draw(Graphics2D g);
}
Next you'll want to extend your GraphicsPanel a bit to have the ability to change an underlying instance of DrawGraphics.
So your constructor should now be:
public GraphicsPanel(DrawGraphics graphicsToDraw) { ...
You should also have a set method for the DrawGraphics stored so you can change it at anytime:
public void setDrawGraphics(DrawGraphics newDrawGraphics) { ...
Make sure you add some synchronization somewhere here, or create all DrawGraphics on the EDT because the paintComponents method will execute on the EDT.
Next the paintComponents method can simply draw the graphics:
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g2d);
// Need to specify a function from the calling class here
graphicsToDraw(g2d);
}
And when you need to change the DrawGraphics:
// Calling from the EDT
myGraphicsPanel.setDrawGraphics(g -> g.drawString("Hello World!", 50, 50);
I seem to have solved the problem, but I'd like comments on usability and good practice
public class CallingClass
{
public JPanel Graphics1 ;
public CallingClass()
{
Graphics1 = new Graphics1_Paint();
}
public class Graphics1_Paint extends JPanel
{
public Graphics2D g2d;
public void paintComponent (Graphics g)
{
g2d = (Graphics2D) g;
super.paintComponent(g2d);
g2d.drawString("From Calling Class",10,10);
}
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new CallingClass();}
});
}
Seems I don't need my GraphicsPanel class after all.
so I have class Board that extends JApplet and in it's constructor I make a JPanel that I'll later draw boxes on, but when I try to do getGraphics it returns null :/
JPanel panel;
public Board(int x, int y, int wolfNumber, int hareNumber){
this.x=x;
this.y=y;
wolvesCoords = new int[wolfNumber][2];
haresCoords = new int[hareNumber][2];
panel = new JPanel();
panel.setVisible(true);
add(panel);
}
public synchronized void write(int xx, int yy, Color c){
int width=panel.getWidth()/x;
int height=panel.getHeight()/y;
Graphics g = panel.getGraphics();
System.out.println(g);
g.setColor(c);
g.drawRect(xx*width, yy*height, width, height);
g.fillRect(xx*width, yy*height, width, height);
}
public void paint(Graphics g)
{
super.paint(g);
}
It gives nullpointerexception at line g.setColor(c) as g is null.
You are using the Graphics object wrong. Instead of calling write from wherever you call it, instead override paintComponent. You could do something like:
private int xx;
private int yy;
private Color c;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(c != null) {
int width=panel.getWidth()/x;
int height=panel.getHeight()/y;
g.setColor(c);
g.drawRect(xx*width, yy*height, width, height);
g.fillRect(xx*width, yy*height, width, height);
}
}
public void write(int xx, int yy, Color c) {
this.xx = xx;
this.yy = yy;
this.c = c;
repaint();
}
Yours is a common problem and question and is yet another reason why you shouldn't use a Graphics object obtained by calling getGraphics() on a component. Another reason you shouldn't do this is that if you are successful at getting a non-null Graphics object (which is only available after the component has been rendered), it will not persist, and your image can turn null if any repaints occur.
Instead do what the tutorials advise you to do: Draw with the Graphics object provided to you in the JPanel's paintComponent method. If you want to draw a fixed background, then do so in a BufferedImage, and then draw that BufferedImage in the paintComponent method.
Edit
You ask:
Why would I call drawing code in the paint method? I need to draw only when the method write is called, not when the app starts.
Because that is how Swing graphics is done, because doing it your way is rife with problems (which you're already experiencing). Again, don't guess at this stuff -- read the tutorials where it is all well explained for you.
Edit
You state in comment:
Actually this error shows up when I try to add override - method does not override or implement a method from a supertype. Could it be that I am extending JApplet?
Yes, exactly so.
I have to though
Yes, you have to have a class that extends JApplet in order to produce JApplets, but you don't have to and in fact shouldn't paint directly in them. Instead create a separate class that extends JPanel, and do your graphics inside of that class's paintComponent method. Then display that JPanel in your applet.
how to turn several graphics objects in to one?
(this part of code should generate tetris figure, where generate() create a figure)
public void paint(Graphics g){
Figure f = generate();
int length = f.getX()[0].length;
for(int j =0; j<f.getX().length;j++){
int xr=xs+10;
ys = 0;
for(int i=0;i<length;i++){
if (f.getX()[j][i] == 1){
int yr = ys+10;
Rectangle p = new Rectangle(xs,ys,xr,yr);
g.setColor(f.getY());
g.drawRect(p.x, p.y, p.width, p.height);
g.fillRect(p.x, p.y, p.width, p.height);
//g.translate(xs+40, ys+40);
}
ys+=10;
}
xs+=10;
}
xs=0;
ys=0;
//g.setColor(Color.white);
//g.drawRect(45, 95, 55, 105);
}
Well, I think you are starting with Java 2D, since your code has some problems.
First of all, you always need to call the paint version of the super class. This should be done because the component needs to have a chance to render itself properly. Take a look.
#Override
public void paint( Graphics g ) {
// you MUST do this
super.paint(g);
// continue here...
}
If you are dealing with a JFrame you will override the paint method. If you are working with some JComponent child, like JPanel, you need to override the paintComponent method, which has the same signature of paint, but it is protected, not public. You can override paint too, but in these cases (JComponent and its children), paint is a method that delegates the paint work to three methods (paintComponent, paintBorder, and paintChildren), so the best option is to override paintComponent.
Another detail. The best way to work with graphics is to create a new graphics context based in the current one and dispose of it after using it. Take a look:
#Override
public void paint( Graphics g ) {
// you MUST do this
super.paint(g);
Graphics newG = g.create();
// or Graphics2D newG2d = (Graphics2D) g.create();
// do your work here...
newG.dispose(); // disposes the graphics context
}
The graphics context that is created using the create method is a copy of the current graphics context (with the same states), but changing it does not affect the original one, so doing this, you will not mess with the state of the original graphics context.
To finish, I think that you need to have a draw method in your figure that receives the graphics context. So, the Figure instance will be responsible to draw itself. Something like:
public class Figure {
// figure's members...
public void drawMe( Graphics2D g2d ) {
// do the draw work here...
}
}
public class MyFrame extends JFrame {
#Override
public void paint( Graphics g ) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g.create();
// let's suppose that figureList is a List<Figure> or a Figure[]
for ( Figure f : figureList ) {
f.drawMe( g2d );
}
g2d.dispose();
}
}
Of course, you can create a new graphics context for each Figure if its draw method changes the graphics context too "deeply", like doing translations and rotations. You just need to dispose the new ones after using them.
I assume you are trying to put multiple components inside of an enclosing component so that you can move/manipulate them together.
One suggestion would be to add each of your objects to a panel object, like JPanel.
However it is somewhat unclear what you are trying to achieve exactly.
In the main method, I am trying to run this:
public static void main(String[] args)
{
game.paintBlocks(g);
}
And getting a "cannot be resolved to variable" error for the "g" parameter.
Elsewhere I have this, which calls on another method in another class (paint(g)) to paint a grid of blocks:
public void paintBlocks(Graphics g)
{
for (int r = 0; r<7; r++)
{
for (int c = 0; c<5; c++)
{
block[r][c].paint(g);
}
}
Do I need to tell it that "g" is in another class? I'm new to this and any help would be awesome!
Where do you want to paint to? I'm assuming you probably want to paint to a window on the screen, in which case you won't be calling paint* yourself, you'll let the Swing framework call it at the appropriate times. In this case, if game is a JFrame, then you just need to make it visible; or if game is some other type of component then you'll need to add it to a visible window. This is the pattern I normally use when I'm teaching basic graphics in Java:
public class MyGame extends JPanel {
public static void main() {
JFrame window = new JFrame("My Game");
window.add(new MyGame());
window.pack();
window.setLocationRelativeTo(null); // Centers the window on the screen
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible();
}
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
protected void paintComponent(Graphics g) {
// Do my drawing here
}
}
If you want to paint to an off-screen image, then you'll need to create your own graphics context to pass to the paint* methods:
BufferedImage hello = new BufferedImage(game.getWidth(), game.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = hello.getGraphics();
game.paintBlocks( g );
In the case of paintBlocks, g is a parameter that is being passed in to the method. In the case of main, g is referencing a variable that hasn't been created anywhere.
Graphics and Graphics2D are abstract classes, and aren't generally intended to be instantiated except by Swing. What Graphics and Graphics2D give you is a context for drawing on a component (like a JPanel or a BufferedImage).
Based on your description you probably want to draw blocks on a Swing component of some kind. (Though it's a little unclear, that would be a normal kind of thing to do.) What you would normally want to do if you are drawing the blocks on a JPanel, for example, is to create a class that extends JPanel and override the paintComponent() method. One way you might do that:
public class BlocksPanel extends JPanel {
// Normal class fields, etc.
// ...
// I would consider making this private, but this is your method from above:
public void paintBlocks(Graphics g) {
for (int r = 0; r<7; r++) {
for (int c = 0; c<5; c++) {
block[r][c].paint(g);
}
}
}
#Override
public void paintComponent(Graphics g) {
paintBlocks(Graphics g);
}
}
There is another example that might help you on page 9 of this document. The Java Tutorials for the Java 2D API may also help.
The variable g is undefined in the main context because you havent declared/initialised it. If you look at your paintBlocks(Graphics g) method, g is passed as a parameter, however the scope of that variable(g) is within the braces({}) of the method paintBlocks(Graphics g).
If you have a class called MyClass that extends a Component, say JPanel, you can do something like this:
class MyClass extends JPanel
{
public static void main(String[] args)
{
Graphics g = getGraphics(); //would return the graphics object for the JPanel
game.paintBlocks(g);
}
}
Its also good to note that the above method in some cases would be tagged as bad programming style. There is an alternative. You can make use of the paintComponent(Graphics g) method provided by the component.
Your main would then look like this:
public static void main(String[] args)
{
repaint(); //this repaints the component, calling the paintComponent method
}
Its also bad programming style to call paintComponent(Graphics g) yourself. You should allow your system to call that method and thats why you have the repaint() method. The system automatically invokes paintComponent(Graphics g) when you repaint.
From paintComponent(Graphics g) you can then do this:
public void paintComponent(Graphics g)
{
super.paintComponent(g); //repainting the panel,not necessary in some cases
game.paintBlocks(g); //passing the graphics object used by the component
}
Hope that helped!