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.
Related
I have been having trouble rotating one of my sprites in a game I'm working on. I have followed a tutorial about rotating images on JPanels about the center of the image (which was very well-done). I even created a simple project that works just fine.
However, when I tried to use the same technique on my game, my sprite will not rotate. I have determined that the problem is drawing the sprite, as I have checked in the paintComponent(Graphics g) method via a println() statement that the rotation value is updated properly and that the repaint() method is being called when appropriate.
Here is the relevant code to the issue (excludes unnecessary methods and such):
Highest-level class:
public abstract class GameObject extends JPanel {
protected BufferedImage image;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Draw sprite
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, 0, 0, null);
// Clean up
g.dispose();
g2.dispose();
}
}
Lowest-level class:
// Entity is a subclass of GameObject.
// It does not override paintComponent.
// All it does is add an update method that is called every game tick.
public abstract class MicroObject extends Entity {
protected Location location; // variable to store position and rotation
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.translate(this.getWidth() / 2, this.getHeight() / 2);
g2.rotate(Math.toRadians(location.getRotation()));
// In the following two lines, image is inherited from GameObject
g2.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
g2.drawImage(image, 0, 0, null);
g2.dispose();
g.dispose();
}
}
I know this isn't necessarily a unique question, but I've looked at all of the "duplicate" threads, and they've all left me with similar answers, but the same problem in the end. I would appreciate it if someone took the time to look at my code and see where I went wrong.
Thank you all!
Don't dispose those Graphics objects! They're re-used by Swing to draw children components & borders.
The reason why it's not working is because GameObject is disposing the graphics object before MicroObject can use it.
Also, there's no reason to draw the image twice. Remove the code in GameObject's paintComponent().
Lastly, just use classes. There's no reason for those to be abstract.
So:
Highest-level class:
public class GameObject extends JPanel {
protected BufferedImage image;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Don't draw sprite. Subclass will do that.
// Don't clean up. Swing does that for us.
}
}
Lowest-level class:
// Entity is a subclass of GameObject.
// It does not override paintComponent.
// All it does is add an update method that is called every game tick.
public class MicroObject extends Entity {
protected Location location; // variable to store position and rotation
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.translate(this.getWidth() / 2, this.getHeight() / 2);
g2.rotate(Math.toRadians(location.getRotation()));
// In the following two lines, image is inherited from GameObject
g2.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
g2.drawImage(image, 0, 0, null);
}
}
I've extended the JPanel class to draw a graph. The problem that I've got is that I need a global graphics object in order to call it in multiple methods... As an example, here's what I'm trying to do:
public class Graph extends JPanel {
private Graphics2D g2d;
public void paintComponent(Graphics g){
g2d = (Graphics2D)g;
}
public void drawGridLines(int hor, int vert){
g2d.someLogicToDrawMyGridLines(someparams);
}
}
This returns a null pointer exception - so my question is: how do I create a global graphics object? What's the best practice in this situation?
My suggestion would be this:
public class Graph extends JPanel {
public void paintComponent(Graphics g){
super.paintComponent(g);
g2d = (Graphics2D) g;
drawGridLines(g2d, ......);
}
private void drawGridLines(Graphics2D g2d, int hor, int vert){
g2d.someLogicToDrawMyGridLines(someparams);
}
}
i.e. keep all the uses of your graphics context inside the paintComponent call.
How would I pass in the graphics object externally?
Don't. The graphics context is only valid during the invocation of paintComponent(). Instead, use the MVC pattern, discussed here, to update a model that notifies any listening view to render itself. JFreeChart is a complete example.
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.
I'm trying to draw objects onto a canvas from an array, but the thing is, I have no clue how to? This must include the position and sizes of the shapes, and there will be more than one type of shape. The code I've got so far(It's inefficient/bad though)
public class MCanvas extends Canvas {
private Object[] world = {};
public void paint(Graphics g){
try{
// How to paint all the shapes from world here?
} catch (NullPointerException e) {
System.out.println(e.toString());
}
}
}
Any ideas? Thanks.
If your using objects that extend from java.awt.Shape, you can translate and draw them by using a Graphics2D context
Since (some whe around Java 1.3/4), the paint engine is guaranteed to use Graphics2D instance.
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
for (Object o : world) {
if (o instanceof Shape) {
Shape shape = (Shape)o;
//if the shape isn't created with
// a location, you can translate them
shape.translate(...,...);
g2d.setColor(....);
g2d.draw(shape);
//g2d.fill(...);
}
}
}
You might like to take a look at 2D Graphics for more details.
Also, use a JPanel instead of Canvas and then override its paintComponent method
Have a look at Custom Painting for more details
I have a JComponent with a listener on it. On the JComponent, I draw a big image and the mouse listener adds small images where clicks occur (one big map on which I add some dots).
How can I programatically draw something outside the paintComponent method?
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(img1, 0, 0, this);
g2.finalize();
}
private MouseListener listener;
public void initListener() {
myCanvas = this;
listener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
myCanvas.getGraphics().drawImage(img,e.getX(),e.getY(), myCanvas);
}
};
addMouseListener(listener);
}
My problem is with this:
public void drawDot(int x, int y){
myCanvas.getGraphics().drawImage(img, x, y, myCanvas);
}
It doesn't do anything. I have tried repaint().
You can't do this. All drawing occurs in the paintComponent() method. What you should do is build a model that represents what you want to draw, and modify the model in your mouse listener. Then call repaint() to ask that this component be redrawn when the model is modified. Inside your paint() method render the full paint from the model. For example:
List<Point> pointsToDrawSmallerImage = new ArrayList<Point>();
...
listener = new MouseAdapter() {
public void mouseClicked(MouseEvent evt ) {
pointsToDrawSmallerImage.add( evt.getPoint() );
repaint();
}
}
...
public void paintComponent(Graphics g) {
g.clear(); // clear the canvas
for( Point p : pointsToDrawSmallerImage ) {
g.drawImage(img, p.x, p.y, myCanvas);
}
}
You have to manage the drawing inside the paintComponent method. Java Graphics is not stateful, you have to take care of what you actually need to draw whatever you want inside the method. Every time the paint method is called, everything must be drawn again, there is nothing that "stays" on the canvas while adding other components
This means that you should store a list of elements that the paint method will take care to draw, eg. ArrayList<Point> points, then in paint method you should iterate them:
for (Point p : points)
draw the point
so that you just add the point to the list with the listener and call repaint.
You can find guidelines for Swing/AWT drawing here..
A particual API has the behavior you would like to have though, it is called Cocos2D and it has a port for Android/Java that you can find here.
that is not how draw works, the draw method paints everything which is in the method itself on every repaint,
that means if you call a method to draw something once, it will only be drawed for one repaint cycle and that's it.
if you want something t be drawn on click you have to add it on on click to a collection and draw the whole collection in every paint cycle, so it will stay permanently.