Graphics2D Problem
Hi all,
I have a program which creates a guitar fretboard by using geometric primitives. I have all that stuff sorted out.
Inside the paint(Graphics g) method of the class, I have a nested class which handles the mouse events. This class works fine.
In the mouseClicked method, I have it set to draw a dot where the mouse was clicked. The dot draws but here is my problem.
When I click on the image drawn by the paint method once, all the lines and what not, turn bold. I think it has something to do with the rendering but I wasn't able to change it.
In the mouseClicked method I tried what worked for me OUTSIDE of the nested method in paint(Graphics g), which was just
g2.fill(new Ellipse(x, y, w, h);
But that did not work.
Currently what I am doing inside the mouseClicked method is:
g2.fill(new Ellipse(x, y, w, h);
repaint();
but this causes the image to change and isn't drawn until the second click.
The first few lines of my paint(Graphics g) method are as follows, I have included the rendering hints
g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
I am not sure why it takes one mouse click to 'initialize' the image, and I'm not sure why the image changes to a bold formatting. Also, I have tried to put another setRendering statement before I draw the ellipse but no success.
Thanks for the help!
Message was edited by:
kerryblue19
When you say "turn bold" it makes me think you are painting on top of existing graphics repeatedly. But that's just a guess.
Unfortunatley the code snippets you provided do not elucidate enough of your problem to allow more useful answers to be rendered.
You need to create a [url=http://homepage1.nifty.com/algafield/sscce.html]Short, Self Contained, Compilable and Executable, Example Program[/url] (SSCCE) that demonstrates the incorrect behaviour, because we can't guess exactly what you are doing based on the information provided.
The best strategy is to do *no* drawing in event handlers -- do no drawing in mouseClicked or mousePressed.
Instead, do all your drawing in paintComponent, and code called from paintComponent.
But how can you get clicking to yeild changes? In your mouse mousePressed
handler, change the state of the component -- add a finger position to its state, then call repaint.
That will cause the component to redraw itself.
thanks for the help, I appreciate it!
cotton.m: I dont have my laptop setup at work yet but once I do I will get the SSCCE going. I agree that it is painting on top of the existing graphics, infact I'm positive, and that was a good suggestion.
Hippolyte: I don't quite understand. Right now I have a paint() method that is automatically called, and inside that method is my nested mouse events class. I'm not sure how to change the state of the component with regards to a user clicking on the diagram.
Thanks again!
Demo:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class ComponentExample extends JPanel {
private static final long serialVersionUID = 0;
private java.util.List < Point > points = new ArrayList < Point > ();
private MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
points.add(evt.getPoint());
repaint();
}
};
public ComponentExample() {
addMouseListener(ml);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(Point p : points)
g.drawRect(p.x-5, p.y-5, 5, 5);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable(){
public void run() {
JComponent comp = new ComponentExample();
comp.setPreferredSize(new Dimension(400,300));
JFrame f = new JFrame("ComponentExample");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(comp);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
Hey, I have partially solved the problem.
cotton was right in that is was repainting the whole diagram, so what I did was in my paint(Graphics g) method I surrounded all of the code except the mouse listener and the
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(...);
with an if statement such as
if(counter==0){
//create diagram
}
and then in the mouse clicked method, before I repaint i just change the counter to 1 and there is no redrawing, and the dots are drawn.
What still remains though is that I still need to click once on the diagram to 'instantiate' it, it seems, and then from that point on I can click and the ellipses will show up.
I'm not sure why this is.
Message was edited by:
kerryblue19
I'm having a hard time believing that your mouse listener is truly "in" your paintComponent method.
I'm confused. I have a nested class in my paint(Graphics g) method.
> I'm confused. I have a nested class in my> paint(Graphics g) method.I don't know if I want to see the whole code, but could you write a sample program that demonstrates what you are trying to do with a nested class there?
here is the paint method, the formatting doesn't translate very well.
public void paint(Graphics g) {
g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if(counter==0){
initBoundMap();
//Create Point2D.Double
Point2D.Double point = new Point2D.Double(x1, y1);
//Create Point2D.DoubleX
Point2D.Double point2 = new Point2D.Double(x2, y1);
// DRAWS THE NUT
g2.setStroke(new BasicStroke(nutThick));
g2.draw(new Line2D.Double(point, point2));
// SETS STRING THICKNESS
g2.setStroke(new BasicStroke(stringThick));
// DRAWS THE STRINGS
drawStrings(g2);
// LABELLING THE NOTE AND DEGREE ROWS
g2.setFont(new Font("Dialog", Font.BOLD, 15));
g2.drawString( "Notes: ", x1-labelOffset,
y1+stringLen+letterOffsetY);
g2.drawString( "Degrees: ", x1-labelOffset,
y1+stringLen+letterOffsetY+lineSpace);
// SETS FRET NUMBER LABEL FONT
g2.setFont(new Font("Dialog", Font.PLAIN, 13));
// DRAW AND NUMBER FRETS
numberDrawFrets(g2);
// DOT DRAW
//g2.setPaint(black);
//g2.fill (new Ellipse2D.Double(50, 50, 20, 20));
}
class MouseClickListener implements MouseListener{
public void mousePressed(MouseEvent event) {
int x = event.getX();
int y = event.getY();
double ellipseDim = 15;
for(int i=0; i<boundMap.size(); i++){
if( ((Rectangle2D)(boundMap.elementAt(i))).contains(x, y) ){
g2.fill(new
Ellipse2D.Double(((Rectangle2D)(boundMap.elementAt(i))).getX()+distBnSt/2-ellipseDim/2,
((Rectangle2D)(boundMap.elementAt(i))).getY()+distBnFt/2-ellipseDim/2,
15, 15));
counter=1;
break;
}//end if
}//end for
repaint();
}//end mousePressed
}//end MouseClickListener
MouseClickListener listener = new MouseClickListener();
addMouseListener(listener);
}
}//end paint
>
Is this a good summary of that paint:
public void paint(Graphics g) {
//yada yada
class MouseClickListener extends MouseAdapter {
public void mousePressed(MouseEvent event) {
//yada yada
}
}
MouseClickListener listener = new MouseClickListener();
addMouseListener(listener);
}
My trouble with that is that *every time* paint is called (which is a lot!)
you create another listener and add it to the collection of listeners for
that component. Are you sure you want to do that -- have tons of listeners?
hey, great point.
I now have a constructor for my class, and in that i have added the mouseclicklistener, and have moved the MouseClickListener class out of the paint method.
But I still get the annoying, first click initializes, and then every subsequent click creates a dot.
Thanks for the help, i appreciate it
I still don't think you mousePressed method should mention g/g2. See my example -- change the state and call repaint.
Is that code for real?!
You do realize that everytime the paint method is called you are adding a new MouseListener to your listener pool right?
Don't believe me?
Just use the mouse to drag the window frame for your application all across the screen and keep doing that until you get an OutOfMemoryError.
You need to have a variable of some kind that keeps track of a state. Have your mouse listener SEPERATE from your paint method and when the mouse is clicked the state is changed. The paint method would then paint differently depending on the state of the variable.
I am also willing to wager that you have to click once to get graphics because your mouse listener contains your graphics context. It will never paint anything to screen until a mouse event is recieved.
That's what I've been saying.
Okay well that's why I'm here asking how I can fix the one click thing. This is my first time using any sort of paint/mouselistener/graphics stuff.
If you understand reply 4, you know all you need, to correct your code.
If you read my posts you would see that the one click issue IS what I'm looking to fix.
Maple_shaft: I do get graphics without having to click, but as I said before, the problem comes when I am trying to click on the diagram so that a dot will appear where I have clicked.
> Maple_shaft: I do get graphics without having to
> click, but as I said before, the problem comes when I
> am trying to click on the diagram so that a dot will
> appear where I have clicked.
Within your class you can specify an anonymous inner class for your mouselistener like so.
MouseListener ml = new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
//Your code here
repaint();
}
} ;
If you don't know anything about anonymous inner classes then I suggest you learn about them. There are plenty of tutorials that explain the concepts.
The MouseEvent object in the mouseClicked method contains a method that will return the coordinate at which the click was made on your screen. You can assign that coordinate to a Point object. That point object must be a member variable of your class and not your MouseListener so that your paint method will be able to access its data.
The mouse listener can be added ONCE to your button or any component for that matter.
component.addMouseListener(ml);
Dukes?
