Im trying to get the point of the cursor in the paint part and simply draw an oval. No luck though!
public void paint(Graphics g){
Point ComponentPoint = PaintPanel.getLocationOnScreen();
Point CursorPoint= MouseInfo.getPointerInfo().getLocation(); //gets cursor point
int ComPX = ComponentPoint.x;
int ComPY = ComponentPoint.y;
int CurPX = CursorPoint.x;
int CurPY = CursorPoint.y;
int FinalX = CurPX - ComPX;
int FinalY = CurPY - ComPY;
g.drawOval(FinalX, FinalY, 20, 20);
}
private void PaintPanelMouseDragged(java.awt.event.MouseEvent evt) {
//when mouse is moved over paintpanel
//PaintPanel.repaint();
not working
}
This is it without paint method, the image:
http://i.stack.imgur.com/VOyhr.png
You can't add code in the paint method like that. YOu would not reference the MouseInfo class in the paint method since you have no control over when the paint() method is invoked. You should be using a MouseListener and MouseMotionListner to do custom painting. Also, custom painting should not be done in the paint method.
See Custom Painting Approaches for two solutions.
Related
Hello fellow programmers,
I've ran into a little issue in my code that I can't seem to crack. It has to do with the Jframe; Graphics area of Java. The code that I'll post below, is over a drawing method. Which purpose is to draw the "rooms" that are in a ArrayList roomList which is located in another class hence lvl. before. This off-course doesn't happen, hence the post on here.
public class LevelGUI implements Observer {
private Level lv;
private Display d;
public LevelGUI(Level level, String name) {
this.lv = level;
JFrame frame = new JFrame(name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
d = new Display(lv, 500, 500);
frame.getContentPane().add(d);
frame.pack();
frame.setLocation(0, 0);
frame.setVisible(true);
}
private class Display extends JPanel {
public Display(Level fp, int x, int y) {
addKeyListener(new Listener());
setBackground(Color.GRAY);
setPreferredSize(new Dimension(x + 20, y + 20));
setFocusable(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
private void draw(Graphics g) {
Level lvl = new Level();
for(int i = 0; i < lvl.roomList.size(); i++) {
Room room = lvl.roomList.get(i);
g.setColor(room.floorColor);
g.drawRect(room.posX, room.posY, room.roomWidth, room.roomHeight);
}
}
}
}
To get some background info on the program. roomList is the ArrayList, and it is filled with various different sized and colored rooms. The rooms themselves are objects.
Here comes first Level class:
public class Level extends Observable {
private boolean Switch = true;
public ArrayList<Room> roomList = new ArrayList<Room>();
(...)
}
Here is the Class Room() that is used to create the rooms.
public class Room {
Color floorColor;
int roomWidth;
int roomHeight;
int posX;
int posY;
public Room(int dx, int dy, Color color) {
this.floorColor = color;
this.roomHeight = dy;
this.roomWidth = dx;
this.posY = 0;
this.posX = 0;
}
(...)
}
I've managed to locate where the problem is thought to occur, and it's the code in the for-loop. I tried switching the roomList.size() for an integer to test if it was the loop., But it wasn't. It is possible to draw a figure outside of the for-loop.
and again, the problem isn't an error message, the program simply doesn't draw the rooms that I've instructed it to draw in the method draw().
The display output looks like this:
Thanks beforehand!
Be aware that the paintComponent() method is invoked by Swing whenever the framework thinks the component needs to be rendered on screen. This usually is when the window is getting visible - initially or because some other window no longer hides the component. Such events are out of your control.
So your application should create a state and be ready to draw it anytime. Therefore you do not create state (like a level) inside the paint() or paintComponent() method. Put that elsewhere - if need be into the constructor.
Looking at you code:
As you are creating a new level inside paintComponent()/draw(), is it correct to assume that this level has no rooms associated? In that case the method is right to return without having painted anything.
If your application thinks the screen should be updated call repaint(), knowing that the paint() method will be called by the framework soon.
Hello fellow programmers!
So to be honest here, i'm not sure if the title question is correct, and you will see why.
Before i explain what i do, and why, here is the code snippet:
JPanel playerPanel = new JPanel() {
public void paint(Graphics g) {
X = 1;
Y = 1;
g.drawImage(player.getScaledInstance(player.getHeight()/2, player.getWidth()/2, Image.SCALE_DEFAULT), X, Y, null);
}
};
So this a snippet from a custom class i made, and my question would be that, you see there is an X and Y variable, i can change their values , but that changes nothing on the impact of the actual program, my first question would be that can i change the X, and Y of this JPanel's image, and if so , how can i "refresh" the actual JPanel/Image so that it looks like it moved?
Some notes:
-the X, Y are global variables
-playerPanel is inside a procedure, and a global variable
-i can access X, Y since they are global variables from outside the class
I'm having a hard time actually writing down my problem... Hopefully you understand what i would like to accomplish.
You're main problem: Don't use an anonymous inner class if you want to give the class new mutable fields. Instead, create a separate class, it can be an inner class, but it can't be anonymous, give it fields that are needed with getters and setters. Also all that Luxx recommends is correct -- override paintCompoent, call the super method, don't declare the fields within a method...
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
public class PlayerDrawingPanel extends JPanel {
private int playerX;
private int playerY;
private Player player;
public PlayerDrawingPanel(int playerX, int playerY, Player player) {
this.playerX = playerX;
this.playerY = playerY;
this.player = player;
}
public void setPlayerX(int playerX) {
this.playerX = playerX;
repaint();
}
public void setPlayerY(int playerY) {
this.playerY = playerY;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(player.getImage(), playerX, playerY, this);
}
}
There is no need to create global variables.
You can use setBounds(x, y, w, h) from Swing's JComponent to move and resize the JPanel.
Though, you have to keep in mind that a Component cannot draw outside its borders. Meaning that the Graphics object that is passed into paint(Graphics g) comes clipped and translated to fit the Component from it's parent.
So, to solve your case, you can either make your JPanel take over the whole area in which you want to draw by using setBounds() or you can you the LayeredLayout from your root panel to draw anywhere.
Let me exemplify the last solution. Consider frame to be your JFrame and playerPanel the JPanel that you overwrote the paint() method:
frame.setGlassPane(playerPanel);
frame.getGlassPane().setVisible(true);
Now your playerPanel is at the topmost layer of your application, covering the whole area. This means you can draw anywhere over anything.
I'm using Swing to create a small GUI in Java. All I am trying to get it to do is take an ArrayListof Circles and draw them. I've run into two problems:
1) I have to call my draw method repeatedly before it draws the circle. If I just call my draw method once nothing happens, I get a blank drawing. If I call it in a loop that runs for less than 30 milliseconds it only draws the first of two circles that I want to draw. Finally, if I call it for more than 30 milliseconds it draws both circles I am trying to draw.
and
2) When I move one of the circles, I get a "flicker" on the drawing.
I'm not too familiar with Swing programming. I've looked at sample code and watched a few videos - and what I have looks right to me. But I figure I must have messed something up, because it doesn't look like this in the videos I've watched.
Here is my GUI class:
package gui;
import draw.*;
import java.util.List;
import javax.swing.*;
public class GUI extends JFrame {
private CirclePainter drawingBoard = new CirclePainter();
public GUI()
{
setSize(500, 500);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setVisible(true);
this.add(drawingBoard);
drawingBoard.setVisible(true);
}
public void draw(List<Circle> circles)
{
drawingBoard.paintComponent(drawingBoard.getGraphics(), circles);
}
}
my CirclePainter class
package gui;
import draw.Circle;
import javax.swing.*;
import java.awt.*;
import java.util.List;
class CirclePainter extends JPanel
{
public void paintComponent(Graphics graphics, List<Circle> circles)
{
super.paintComponent(graphics);
for(Circle circle : circles)
graphics.fillOval(circle.getX(), circle.getY(), circle.getRadius() * 2, circle.getRadius() * 2);
}
}
EDIT: redacted some code since this is for a school project. The remaining code should be enough for someone visiting in the future to still understand the question.
Never call paintComponent(...) directly as you're doing.
Instead suggest a draw by calling repaint() on a component when necessary.
Don't draw with a Graphics object obtained via a getGraphics() call on a component. Instead, draw with the Graphics object provided in the paintComponent method.
Avoid using while (true) loops in a Swing GUI as you risk tying up the Swing event thread and freezing the GUI. Use a Swing Timer for simple animations.
You probably don't even need a Swing Timer since your animation can be driven by your MouseListener/MouseMotionListener.
Most important -- do read the Swing painting and other tutorials, as most of this information can be found there. It looks like you're guessing how to do some of your coding and that's a dangerous thing to do when it comes to drawing or animating a GUI. You can find most tutorials in the Swing info link.
Consider using a Shape object to represent your Circle, such as an ellipse2D. The reason that this will help is that it has some very useful methods, including a contains(Point p) method that will help you determine if a mouse click lands inside of your circle.
You will want to decide where _x and _y represent the center point of your circle or not. If so, then you'll need to adjust your drawing some, by shifting it left and up by _radius amount.
Consider casting your Graphics object into a Graphics2D object in order to use its extra methods and properties.
One such property are the RenderingHings. Set your Graphics2D RenderingHints to allow for anti-aliasing to get rid of your image "jaggies". This can be done with: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); where g2 is your Graphics2D object.
Your paintComponent method is not a true paintComponent override and thus won't work correctly. It should be a protected method, not public, it should have one parameter, a Graphics object, and nto a second parameter, and you should place the #Override annotation above it.
For example, please have a look at this answer of mine to a similar problem.
An example of a paintComponent method that centers the circles on _x and _y and that uses rendering hints:
class CirclePainter extends JPanel implements Iterable<Circle> {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private CircleList circleList = new CircleList();
#Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D g2 = (Graphics2D) graphics;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Circle circle : circleList) {
// if x and y are the center points, then you must subtract the radius.
int x = circle.getX() - circle.getRadius();
int y = circle.getY() - circle.getRadius();
int width = circle.getRadius() * 2;
int height = width;
g2.fillOval(x, y, width, height);
}
}
Building on your code and the suggestions from Hovercraft Full Of Eels, a small step in the right direction could be taken with these modifications to the GUI and CirclePainter classes:
// GUI.draw
public void draw(List<Circle> circles)
{
// drawingBoard.paintComponent(drawingBoard.getGraphics(), circles);
drawingBoard.setCircles(circles);
drawingBoard.repaint();
}
class CirclePainter extends JPanel
{
// public void paintComponent(Graphics graphics, List<Circle> circles)
// {
// super.paintComponent(graphics);
// for(Circle circle : circles)
// graphics.fillOval(circle.getX(), circle.getY(), circle.getRadius() * 2, circle.getRadius() * 2);
// }
private List<Circle> circles;
public void setCircles(final List<Circle> circles) {
this.circles = circles;
}
#Override
protected void paintComponent(final Graphics graphics) {
super.paintComponent(graphics);
for (Circle circle : circles)
graphics.fillOval(circle.getX(), circle.getY(), circle.getRadius() * 2, circle.getRadius() * 2);
}
}
This way, you might not have fixed all the fundamental issues, but you get your program to work with only minor changes. And Swing is a very nice library that can be much fun to learn more about.
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 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.