I am creating an applet that lets the user draw different shapes using the rubber band effect, letting the user see the shape while it is being drawn. What I want is for the program to draw shapes that stay on the screen. The problem is that the program draws a shape wherever the mouse is.
Take the program below, for example. Say the user clicks the applet at point (50,50) and drags the mouse to draw a rectangle with the bottom-right corner at (70,70). The program will draw several rectangles inside the final rectangle (i.e. rectangles with the bottom-right corner at (54,56), (63,61), etc.). I only want the final rectangle to be shown, but also while using the rubber band effect. If the user were to draw a second rectangle, the first one would remain on the screen while the user draws the second one.
How can I alter the code to make this work?
import java.awt.Graphics;
import java.awt.event.*;
public class Test extends java.applet.Applet implements MouseListener, MouseMotionListener {
int downX, downY, dragX, dragY;
public void init() {
downX = downY = dragX = dragY = 0;
addMouseListener(this);
addMouseMotionListener(this);
}
public void paint(Graphics g) {
g.drawRect(downX,downY,dragX-downX,dragY-downY);
}
public void update(Graphics g) {
paint(g);
}
public void mouseClicked(MouseEvent e) {
downX = e.getX();
downY = e.getY();
}
public void mouseDragged(MouseEvent e) {
dragX = e.getX();
dragY = e.getY();
repaint();
}
public void /*Other MouseEvent methods*/ {}
}
You've broken the paint chain. Failing to call super.paint is preventing the applet from preparing the Graphics context for painting, by removing anything that might have been painted to it before. There's no need to override update, as you're not doing anything with it.
Typically, you should avoid overriding paint of top level containers, as they aren't double buffered and will flicker as they are repainted
You should avoid using Applet and instead use a combination of JApplet a (for example) JPanel as your drawing surface . In fact, if you're just learning. It would be better to use JFrame as applets have a lot of additional management
Painting by its very nature is destructive. You need to maintain a list of things that you want to draw. In this, I would recommend a List of Points, which can be used to paint lines, where the last point is the current drag point
Also take a look at Painting in AWT and Swing for details about how painting works
Related
I'm creating a game using Java Swing, and I'm finding the need for graphical displays of what's going on at this point. The current display uses a grid of JButtons to represent the tiles on a battlefield. Would it be possible to display floating/disappearing damage numbers over the JButtons, using perhaps custom made pixel art GIFs? If so, how would I go about implementing this?
Almost all Swing components can be extended to change its presentation.
Create an own button class extending JButton and override its paintComponent method to display the required damage. This method receives a Graphic (actually an instance of Graphics2D) on which you can draw the damage, if one is active.
Very simple example:
public class DamageButton extends JButton {
private String damage = null;
public DamageButton(String text) {
super(text);
}
public void setDamage(String damage) {
this.damage = damage;
System.out.println(damage);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (damage != null) {
Graphics2D gg = (Graphics2D) g.create();
try {
gg.setColor(Color.RED);
gg.drawString(damage, 10, 10);
} finally {
gg.dispose();
}
}
}
}
Creating a new Graphics (gg) so the settings of g are not changed and need not to be restored.
Call setDamage() with some text to have it displayed over the button or with null to cancel the effect.
Animation or other effects can (more or less) easily be added. Just be aware that the given Graphics2D has a clipping area set to the dimension of the button.
I know there's no direct replacement for java.awt.Canvas in swing, and I know I'm supposed to use a JPanel and override paintComponent, for example like so:
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.drawOval(0, 0, 100, 100);
}
And this would draw a black circle on the JPanel when it is created. The problem I have is that I want a dynamic canvas: I want to be able to draw things in response to user input, and redraw continuously, not just once when the application starts. An example would be having a moving object on a canvas, that would need to be redrawn at a rate of say 60 frames per second. How could I achieve this without using AWT components?
EDIT: what I mean is, in an actual canvas, I'd be able to arbitrarily call, say, drawOval anywhere in my code, and that would draw an oval on the canvas; is this doable with JPanel?
Store the information to be drawn (e.g. a Shape or a group of them) and call repaint() from a Swing Timer. Each time the paintComponent(..) method is called, first call the super(..) method to erase the previous drawings, then iterate the list of shapes, move them if necessary, and draw each one.
Here's one way to do it:
public class Renderer extends JComponent implements ActionListener {
private int x;
public Renderer() {
Timer timer = new Timer(1000/60, this);
timer.start();
x = 0;
}
#Override
public void paintComponent(Graphics g) {
super.paint(g);
// drawing code
g.setColor(Color.black);
g.drawOval(x, 0, 100, 100);
}
private void update() {
this.x++;
}
#Override
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
}
Now just add this to your component (JPanel or whatever):
comp.add(new Renderer());
I am making a molecule designing application. I can draw the lines and circles, but it clears the old lines each time you click, so basically, you can only design molecules with 2 atoms.
Also, the mouseEvents don't deliver if you click very fast which is also a problem.
Here is the code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class MoleculeDesigner extends JComponent implements MouseListener {
private Point op, cp;
private boolean first = true;
public static final Color linecolor = new Color(0, 255, 0);
private static final long serialVersionUID = 1L;
private BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
public MoleculeDesigner() {
JFrame f = new JFrame("Molecule Designer");
f.setBackground(Color.WHITE);
f.addMouseListener(this);
f.add(this);
f.setSize(100, 100);
f.setDefaultCloseOperation(3);
f.setVisible(true);
}
public static void main(String[] args) {
new MoleculeDesigner();
}
#Override
protected void paintComponent(Graphics g) {
if(op != null && cp != null) {
Graphics2D g2 = img.createGraphics();
super.paintComponent(g2);
g2.setColor(linecolor);
g2.drawLine((int) op.getX(), (int) op.getY(), (int) cp.getX(), (int) cp.getY());
g2.setColor(Color.BLACK);
g2.fillOval((int) cp.getX(), (int) cp.getY(), 10, 10);
op = (Point) cp.clone();
g2.dispose();
}
}
#Override
public Dimension getPreferredSize() {
return getParent().getMaximumSize();
}
#Override
public void mouseClicked(MouseEvent e) {
if(!first) {
cp = e.getPoint();
cp.setLocation(cp.getX(), cp.getY() - 8);
}
else {
op = e.getPoint();
first = false;
}
repaint();
}
#Override public void mousePressed(MouseEvent e) {}
#Override public void mouseReleased(MouseEvent e) {}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
}
All help appreciated!
Either 1) draw in a BufferedImage which is then displayed inside of your paintComponent override, or 2) put your data into an ArrayList or other collection, and then iterate through the collection inside of paintComponent. I'd do the latter if I needed the data for other purposes. Also, never ever do this:
public void update(Graphics g) {
paintComponent(g);
}
This is not how Swing graphics are supposed to be done and is potentially dangerous code. Please read:
Basic Swing Graphics Tutorial
Advanced Swing Graphics Information
Edit
More detail regarding option 1:
Create a BufferedImage using one of its constructors.
Do your drawing on the image.
When you need to draw, get a Graphics object from the BufferedImage using getGraphics() or createGrahpics() (for a Graphics2D object)
Draw with this Graphics object
Then dispose() the Graphics object.
Then call repaint() to ask the JVM to repaint the component.
Draw the image in your paintComponent method by calling g.drawImage(...), passing in your buffered image.
Benefits: often the drawing is quicker, and I often use this to draw background images.
Drawbacks: the data points are not available, and so if you need to do manipulation or animation of your data points, this is not the way to go.
You don't, nor should you.
paint in Swing is a destructive process, this is the way it was designed. That is, there is an expectation that when you component is requested to paint itself, it will clean up the Graphics context before painting anything (this is slightly different for transparent components though).
Swing has no concept of what was painted on your component before and because the Graphics context is shared amongst all the components been painted, unless you clear the graphics first, you could end up with unwanted paint artifacts
Possible solutions might include...
Painting to some kind of backing buffer (such as a BufferedImage), which you use the paintComponent method to draw. This is limited in the fact that it just acts like a paint program, painting pixels to the image. You will also need to provide functionality when the size of the viewable area changes, as the BufferedImage won't know.
Place each object you wanted painted into some kind of List and iterate this list when paintComponent is called. This is a little more flexible in that you can control the order of the objects drawn, remove objects and insert new ones where you like
I have been looking at the Java2D tutorials and was wondering how to draw a shape using the mouse to define its size(i.e the size of the shape is not fixed). I haven't come across a tutorial specifically for this and was wondering how I could implement this for a rectangle for example.
Basically, the size is FIXED at every moment. When you add a MouseMotionListener, before the next event is captured, you can paint the shape on the screen with the size depending on current MouseEvent.getPoint() which tells you the coordinates of your mouse location.
Override the paintComponent(Graphics g) method of the component. and call repaint() method after each update of the mouse location and the size of the shape:
class YourPanel extends JPanel extends MouseMotionListener, MouseListener {
private Rectangle rect = new Rectangle();
public YourPanel () {
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
g.draw(rect);
}
#Override
public void mouseDragged (MouseEvent me) {
rect.setSize(me.getX() - rect.x, me.getY() - rect.y);
repaint();
}
#Override
public void mousePressed (MouseEvent me) {
rect.setLocation(me.getX(), me.getY());
repaint();
}
// Other methods...
}
Shapes is a word and java class that represents differnet geometric figures like rectangles, ellipses, poly lines, etc.
So first the user would have to decide which shape, e.g a poly line.
You then would catch mouse left click events, and for each click read the mouse coordinates and add that coordinate pair (e.g java.awt.geom.Point2D()) to an ArrayList<Point2D>.
The size of such an list is (practically) unbounded. On each click you will create a current shaped object that will be drawn. Once the user clicks right mouse, the shape is ready und you store it in list of shapes.
You should have your class implement a mouse listener, then save the variables of the mouse listener with getX and getY to draw the shape.
I have a Ball class which I want to have extend JComponent and implement mouseListener.
public class Ball extends JComponent implements MouseListener {
Int x, y, radius;
public Ball(int X, int Y, int Radius){
//contains only three ints and redefines x,y,radius
x=X;
y=Y;
radius=Radius;
}
public void draw(Graphics g){
//draw oval using x,y,radius
}
//5 mouselisteners undefined yet
}
So ball is the constructor which is used by a panel which is within a frame.
Sorry I have not yet entered all the code. I will submit my complete code soon.
So what I would have to do is use MouseEntered listener in the ball class so that when the mouse enters the component (the ball/oval) .
But I don't know how to define the component so that it knows it has been entered.
Does it need some dimensions? Because all I am doing is using the draw function in a panel.
If this were my class, I wouldn't have it extend JComponent and wouldn't give it a MouseListener or MouseMotionListener especially if I wanted to display multiple balls in a single JComponent. Instead I would give it public methods that allow other classes to get its boundaries (such as is available from the Shape interface), and whether something is contained in the shape or not (again the Shape interface works well for this), and other public methods that allow outside classes to change the state (appearance?) of this object.
I would then have a JComponent hold one Ball or an ArrayList<Ball>, and in the MouseListener/MouseMotionListener/MouseAdapter for this JComponent, iterate through the ArrayList<Ball> seeing if the mouse is inside of any ball, and if so, change that ball's state. Then in the JComponent's paintComponent method, I'd iterate through the ArrayList<Ball> calling draw(g) on each Ball contained.
You should call
this.addMouseListener(new MouseAdapter() {
....
});
in the constructor. Override mouseEntered() and mouseExited() inside of the adapter.
I would prefer that you implement the whole MouseListener. It could be possible that you want later more from your Ball-Component, like for example that it moves if you click on it or something. If you implement the interface you are applicable for later changes.
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JComponent;
public class Ball extends JComponent implements MouseListener {
int x, y, radius;
public Ball(int x, int y, int radius){
//contains only three ints and redefines x,y,radius
this.x= x;
this.y= y;
this.radius= radius;
}
public void draw(Graphics g){
//draw oval using x,y,radius
}
#Override
public void mouseClicked(MouseEvent arg0) {
}
#Override
public void mouseEntered(MouseEvent arg0) {
//your code to do things, when the mouse entered your ball
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
Besides the remarks already made by Hovercraft Full Of Eels, I think you confuse the concept of a being a listener, and adding a listener to something.
It is not by implementing MouseListener that those methods will be called. A listener is the interested party and you add it to the object in which you are interested. So in this case you want to add a MouseListener to your Ball class, which is completely different from letting your Ball class implement MouseListener.
More information can be found on Wikipedia: Observer pattern or a more simple and more Swing oriented document can be found in the Swing tutorials