I'm feeling quite stupid. But what is the reason why this simple piece of code doesn't change the ellipse's color?
Basically I want to add a mouse listener to the oval - a graphic object. when the mouse cursor is in oval, the oval changes its color. But this code doesn't change at all... This code is for testing only.
public class Help extends JFrame{
public static void main(String [] agrs){
Help h = new Help();
h.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
h.add(new Help_Option());
h.setSize(2000, 1000);
h.setVisible(true);
}
}
class Help_Option extends JComponent implements MouseListener{
Ellipse2D ellipse = new Ellipse2D.Double(0, 0, 1000, 500);
Color c = Color.BLACK;
public Help_Option(){
this.addMouseListener(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
g2d.draw(ellipse);
g2d.setColor(c);
g2d.fill(ellipse);
}
public void setColor(Color c){
this.c = c;
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {
if (ellipse.contains(e.getX(), e.getY()) ) {
setColor(Color.GREEN);
repaint();
}
}
#Override
public void mouseExited(MouseEvent e) {
}
}
you are adding a MouseListener and waiting for mouseEntered events. These are fired when the mouse enters a Component, not a region of it. Try entering the component's boundary where the ellipse is shown and observe.
What you need is a MouseMotionListener, so that you can observe the mouse pixel by pixel; use the mouseMoved or mouseDragged events.
You might still need to listen for mouseEntered or mouseExited events, as MouseMotionEvents are only fired while inside the component's boundary, so you might miss the mouse exiting the component while still inside the ellipse.
A good and simple way for debugging this is adding prints inside the event handler. You would then see that the handler was called, but only once or a few times, and not when you move the mouse within the component.
class Help_Option extends JComponent implements MouseListener, MouseMotionListener {
Ellipse2D ellipse = ...;
public Help_Option() {
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
public void mouseMoved(MouseEvent e) {
if (ellipse.contains(e.getX(), e.getY()) ) {
//mouse is inside the ellipse
} else {
//mouse is outside the ellipse
}
}
public void mouseExited(MouseEvent e) {
//mouse is outside the ellipse
}
//more method stubs
}
..if i use the boundary of the ellipse, it would be a rectangle, so whenever my mouse enter the rectangle-but-not-in-ellipse, the color will change
See:
Shape.contains(x,y): Tests if the specified coordinates are inside the boundary of the Shape, as described by the definition of insideness.
Shape.contains(Point2D): Tests if a specified Point2D is inside the boundary of the Shape, as described by the definition of insideness.
See also this answer for a demo showing collisions between 2 shapes.
Related
I'm having trouble with my jbutton not displaying. If i do not use paintComponent, then my JButtons with images show up no problem. However now, the image does not show up. If i hover over where it should be, the image shows up for one second. So it's like the button is still there, just maybe below the background?
public class Game extends JPanel implements KeyListener, ActionListener {
//layout variables
private ImageIcon right,left,up,down;
private JButton rightButton,leftButton,upButton,downButton;
//play variables
private boolean play=false;
private int score=0;
private int paddleX= 200; //paddle X position
private int paddleY= 300; //paddle Y pos
private int ballX= 210; //ball x position
private int ballY= 260; //ball y position
private int ballXdir=-1; //x direction
private int ballYdir=-2; //y direction
private Timer time; //my timer
public Game() {
Display(); //display the layout
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
time= new Timer(8,this);
time.start();
}
public void Display(){
//setLayout
this.setLayout(null);
//Setting the Images
//right = new ImageIcon(getClass().getResource("images\\rightIcon.png"));
left = new ImageIcon(getClass().getResource("images\\leftIcon.png"));
up = new ImageIcon(getClass().getResource("images\\upIcon.png"));
down = new ImageIcon(getClass().getResource("images\\downIcon.png"));
//Setting the JButtons for the arrow images
rightButton= new JButton("right");
rightButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(paddleX>=400){
paddleX=400;
}
else{
moveRight();
}
repaint();
}
});
//rightButton.setOpaque(false);
//rightButton.setContentAreaFilled(false);
//rightButton.setBorderPainted(false);
leftButton= new JButton(left);
leftButton.setOpaque(false);
leftButton.setContentAreaFilled(false);
leftButton.setBorderPainted(false);
upButton= new JButton(up);
upButton.setOpaque(false);
upButton.setContentAreaFilled(false);
upButton.setBorderPainted(false);
downButton= new JButton(down);
downButton.setOpaque(false);
downButton.setContentAreaFilled(false);
downButton.setBorderPainted(false);
//setting image bounds and adding it to screen
rightButton.setBounds(135,450,50,50);
leftButton.setBounds(45,450,50,50);
upButton.setBounds(90,400,50,50);
downButton.setBounds(90,500,50,50);
//rightButton.addActionListener(this);
leftButton.addActionListener(this);
add(upButton);
add(downButton);
add(leftButton);
add(rightButton);
}
//painting the screen with graphics
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(25,25,425,300); //game area
//drawing the paddle
g.setColor(Color.YELLOW);
g.fillRect(paddleX,paddleY,50,8);
//drawing the ball
g.setColor(Color.PINK);
g.fillOval(ballX,ballY,20,20);
g.dispose();
}
#Override
public void actionPerformed(ActionEvent e) {
time.start();
if(e.getSource()==right) {
if(paddleX>=400){
paddleX=400;
}
else{
moveRight();
}
}
if(e.getSource()==left) {
if(paddleX<35){
paddleX=35;
}
else{
moveLeft();
}
}
repaint();
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
if(paddleX>=400){
paddleX=400;
}
else{
moveRight();
}
}
if(e.getKeyCode()==KeyEvent.VK_LEFT){
if(paddleX<35){
paddleX=35;
}
else{
moveLeft();
}
}
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
public void moveRight(){
play=true;
paddleX+=10;
}
public void moveLeft(){
play=true;
paddleX-=10;
}
}
I highly recommend having a look at Performing Custom Painting and Painting in AWT and Swing as it will explain how the painting system works.
Basically, you are passed a reference to the Graphics context which is currently been used to perform the current paint pass, via the paintComponent method. This is a shared resource. All components involved in the paint pass are passed the same Graphics context.
By calling dispose, you are releasing all the underlying resources for the context, which can, on some systems, prevent other components from been painted.
But they paint when I move my mouse over them
Because components can be painted independently of their parent, but you also call repaint on the parent component, which will, you guessed it, paint it's children.
Recommendations
Create a custom component dedicated solely to performing the custom painting operations (and possible some of the other basic game functions)
Create another component to hold the buttons (and make use of appropriate layouts)
Use a some kind of data model which is shared between them. This model will hold the state of the "actions" (ie up/down/left/right) and the "engine" will use this information to update the state of the game.
Make use of the Key bindings API which will solve the unreliability issues associated with KeyListener
I want the user of my program to be able to highlight an area on a JFrame to select a group of items on screen. The code I posted below works, but it is very choppy, which becomes quite an eyesore every single time the JFrame repaints. Here's an image of what I'm talking about: http://i1065.photobucket.com/albums/u400/mfgravesjr/choppy%20draw%20rectangle_zpsspqsqnyf.png
Maybe someone here has suggestions to improve my code?
This is the mouseDragged method in MouseMotionListener:
public void mouseDragged(MouseEvent me)
{
if(groupingTerr&&me.getSource()==background)
{
endPoint = MouseInfo.getPointerInfo().getLocation();
topLeftRect = new Point(Math.min(startPoint.x,endPoint.x),Math.min(startPoint.y,endPoint.y));
bottomRightRect = new Point(Math.max(startPoint.x,endPoint.x),Math.max(startPoint.y,endPoint.y));
for(Point p:map.territoryPoints)
{
if(p.x>topLeftRect.x&&p.x<bottomRightRect.x&&p.y>topLeftRect.y&&p.y<bottomRightRect.y)img.getGraphics().drawImage(selectedIco,p.x-selectedIco.getWidth()/2,p.y-selectedIco.getHeight()/2,null);
else img.getGraphics().drawImage(defaultIco,p.x-defaultIco.getWidth()/2,p.y-defaultIco.getHeight()/2,null);
}
background.repaint();
}
}
This is the private overridden JFrame class
private static class DrawableJFrame extends JFrame
{
#Override
public void paint(Graphics g)
{
super.paint(g);
g.setColor(new Color(0,0,0,100));
g.drawRect(topLeftRect.x,topLeftRect.y,bottomRightRect.x-topLeftRect.x,bottomRightRect.y-topLeftRect.y);
g.setColor(new Color(200,10,10,100));
g.fillRect(topLeftRect.x,topLeftRect.y,bottomRightRect.x-topLeftRect.x,bottomRightRect.y-topLeftRect.y);
}
}
The image is original artwork. Please do not use it.
I have a basic knowledge of Java and I need to create a Sunset App.
If you press right on the keyboard the sun needs to rotate at an angle.
When I press the right button it will not redraw or move the ellipse, I have checked with syso if the listener works and it outputs the test so I have a problem with the rotate and the fill... can anybody help me?
This is my Jpanel class...
public class OEF7 extends JPanel implements KeyListener{
private Graphics2D g2d;
private Ellipse2D sun;
public void paintComponent( Graphics g){
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.setPaint(new GradientPaint(50,50,Color.RED, 100,100,Color.YELLOW,true));
sun = new Ellipse2D.Double(0,0,50,50);
g2d.translate(350, 200);
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == 39) {
g2d.rotate(Math.PI/10.0);
g2d.fill(sun);
//---- repaint() ?
}
}
A Graphics object is only valid during painting. If you save it and try to use it in other methods, such as a KeyListener method, it is no longer valid. Modifying it at that point does nothing.
Your class needs to store the rotation in a field, then make use of that field during painting:
private double rotation;
// ...
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
rotation = (rotation + Math.toRadians(10)) % (Math.PI * 2);
repaint();
}
}
Hey Everyone I am trying to create a somewhat dynamic program in which you can add shapes or images to a JPanel and then select and move the shapes after you have added them. The problem is that when I click on the specific JComponent nothing happens. In fact clicking on any of the components I have created to test the project returns false for all JComponents. However it seems that if I click inside the bounds of my JComponent in the top left corner I will get returned true for all JComponents, ie click in the area bounded by (0,0,50,68).
The idea is that if I click one of the JComponents it will set that specific JComponent to be movable however I cannot get past the part of actually selecting a specific JComponent.
Here is a basic SSCE that I built to recreate the problem:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class SSCE1 extends JPanel {
private ArrayList<Shape> shapeList = new ArrayList<Shape>();
SSCE1() {
setLayout(null);
/* Debug Stuff */
System.out.println("Debug:");
/* Add The First Shape To The List */
shapeList.add(0, new Shape(100, 100));
add(shapeList.get(0));
shapeList.add(1, new Shape(610, 0));
add(shapeList.get(1));
shapeList.add(2, new Shape(500, 900));
add(shapeList.get(2));
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
for (Shape shape : shapeList) {
if (shape.contains(e.getPoint())) {
System.out.println("Hello");
} else {
System.out.println("Goodbye");
}
}
}
});
}
}
class Shape extends JComponent {
int xLocation, yLocation, xBounds1, yBounds1;
Shape(int xLocation, int yLocation) {
this.xLocation = xLocation;
this.yLocation = yLocation;
this.xBounds1 = 50;
this.yBounds1 = 68;
setBounds(xLocation, yLocation, xBounds1, yBounds1);
setLocation(xLocation, yLocation);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.blue);
g.fillRect(0, 0, 100, 100);
}
}
class Run {
public static void main(String[] args) {
JFrame main = new JFrame();
SSCE1 p1 = new SSCE1();
main.setSize(new Dimension(1000, 1000));
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setLocation(new Point(0, 0));
main.setVisible(true);
main.add(p1);
}
}
Basically, the problem is, Shape is expecting any mouse coordinates you pass it to be defined within the context of the Shape. That is, the top, left corner of Shape is always 0x0
The mouse point you are processing is within the context of the parent container, therefore, unless Shape is positioned at 0x0 within the parent container, Shape will never contain the mouse point.
You need to translate the mouse point to the context of the Shape before checking it
For example...
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
for (Shape shape : shapeList) {
Point shapePoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), shape);
if (shape.contains(shapePoint) {
System.out.println("Hello");
} else {
System.out.println("Goodbye");
}
}
}
});
Use next mouseListener it works:
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
for (Shape shape : shapeList) {
Point convertPoint = SwingUtilities.convertPoint(SSCE1.this, e.getPoint(), shape);
if (shape.contains(convertPoint)) {
System.out.println("Hello");
} else {
System.out.println("Goodbye");
}
}
}
});
The reason is next according docs in contains method the point's x and y coordinates are defined to be relative to the coordinate system of this component. because of that works for (0,0,50,68). All what you need convert point from JPanel to Shape with help of SwingUtilities.convertPoint(...)
I've got a problem in Java using a "canvas" class I created, which is an extended JPanel, to draw an animated ring chart. This chart is using a MouseListener to fetch click events.
The problem is that the mouse position does not seem to be accurate, meaning it does not seem to be relative to the "canvas" but instead relative to the window (in the left, upper corner I got about 30px for y coord).
This is my code:
I created a class, that extends JPanel and does have a BufferedImage as member.
public class Canvas extends JPanel {
public BufferedImage buf;
private RingChart _parent;
public Canvas(int width, int height, RingChart parent){
buf = new BufferedImage(width, height, 1);
...
In the paint component method I just draw the buffered image, so I am able to paint on the canvas from 'outside' by painting on the buffered image, which is public.
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(buf, null, 0, 0);
}
Now there's a class RingChart which contains a "canvas":
public class RingChart extends JFrame{
public Canvas c;
...
And I create a Graphics2D from the bufferedImage in the canvas class. This g2d is used for painting:
public RingChart(){
c = new Canvas(1500,980,this);
add(c);
setSize(1500, 1000);
setTitle("Hans");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
g2d = (Graphics2D)c.buf.createGraphics();
...
What I now was trying to achieve, was a mouse listener that listened to mouse events happening on the canvas. So when the user clicks on the canvas I could retrieve the position he clicked on, upon the canvas, through the event variable.
So I created a mouse listener:
class MouseHandler implements MouseListener {
#Override
public void mouseClicked(MouseEvent e){
RingChart r = ((Canvas)e.getSource()).getParent();
r.mouseClick(e);
}
...
...and added this mouse listener to the canvas of the RingChart class (myChart is an instance of RingChart and c is the canvas it contains):
...
MouseHandler mouse = new MouseHandler();
myChart.c.addMouseListener(mouse);
...
But as I mentioned above, the mouse position, that's returned when the click event is called, does not seem to be accurate. I think the mistake must be somehow in the way I created that mouseListener or maybe assigned it to the wrong element or something like that. But I've tried quite a couple of things and it didn't change. Can maybe someone tell me, what I've done wrong?
UPDATE:
The code of the function "mouseClick" that is a member of RingChart and is called in the mouse listener:
public void mouseClick(MouseEvent evt){
//evt = SwingUtilities.convertMouseEvent(this, evt, c);
if(evt.getButton() == MouseEvent.BUTTON1 && animation == null){
for(Element e : elements){
if(e.getShape() != null && e.getShape().contains(evt.getPoint())){
//do some stuff
}
}
}
}
Again, the hierarchy of my classes:
RingChart --contains a--> Canvas --got a--> MouseListener.
The shapes in this function are shapes that have been painted on the canvas c. Now I want to check, if the user has clicked on one of them. So as I thought, the shapes should be in canvas-coordinates and the event position should be in canvas-coordinates and everything should fit together. But it doesn't.
Now user MadProgrammer told me, to use the ConvertMouseEvent function. But I currently don't see which exact way I should use this sensibly.
UPDATE:
I found a solution: All I had to do is adding the canvas not directly to the JFrame but to the ContentPane of the JFrame instead:
So instead:
public RingChart(){
c = new Canvas(1500,980,this);
add(c);
...
I do:
public RingChart(){
c = new Canvas(1500,980,this);
getContentPane().add(c);
...
Then I give the MouseListener to the ContentPane.
getContentPane().addMouseListener(new MouseHandler());
getContentPane().addMouseMotionListener(new MouseMoveHandler());
I don't know, if this is an elegant solution, but it works.
The mouse event is automatically converted to be relative to the component that it occurred in that is, point 0x0 is always the top left corner of the component.
By using RingChart r = ((Canvas)e.getSource()).getParent(), you've effectively changed the reference, which now means the location is no longer valid.
You need to convert the location so that its coordinates are in the context of the parent component. Take a look at SwingUtilities.convertMouseEvent(Component, MouseEvent, Component)
UPDATE with PICTURES
Lets take this example...
The blue box has a relative position of 50px x 50px to the red box. If you click in the blue box, lets say at 25x25, the mouse coordinates will be relative to the blue box (0x0 will be the top left of the blue box).
If you then pass this event to the red box and try and use the coordinates from it, you will find that the coordinates will now be half way between the top left of the red box and the blue box, because the coordinates are context sensitive.
In order to get it to work, you need to translate the mouse events location from the blue box to the red box, which would make it 75x75
Now, I don't know what you're doing when you pass the mouse event to the RingChart so I'm only guessing that this is the issue you're facing.
UPDATED with Click Code
Okay, lets say, you have a Canvas at 100x100. You click on that Canvas at 50x50. You then pass that value back up the chain.
public void mouseClick(MouseEvent evt){
//evt = SwingUtilities.convertMouseEvent(this, evt, c);
if(evt.getButton() == MouseEvent.BUTTON1 && animation == null){
for(Element e : elements){
// Here, we are asking the shape if it contains the point 50x50...
// Not 150x150 which would be the relative position of the click
// in the context to the RingChart, which is where all your objects
// are laid out.
// So even the original Canvas you clicked on will return
// false because it's position + size (100x100x width x height)
// does not contain the specified point of 50x50...
if(e.getShape() != null && e.getShape().contains(evt.getPoint())){
//do some stuff
}
}
}
}
UPDATED
I think you have your references around the wrong way...
public static MouseEvent convertMouseEvent(Component source,
MouseEvent sourceEvent,
Component destination)
I think it should read something like
evt = SwingUtilities.convertMouseEvent(evt.getComponent(), evt, this);
UPDATE with Code Example
Okay, so, I put this little example together...
public class TestMouseClickPoint extends JFrame {
private ContentPane content;
public TestMouseClickPoint() throws HeadlessException {
setSize(600, 600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new BorderLayout());
content = new ContentPane();
add(content);
}
protected void updateClickPoint(MouseEvent evt) {
content.updateClickPoint(evt);
}
protected class ContentPane extends JPanel {
private Point relativePoint;
private Point absolutePoint;
public ContentPane() {
setPreferredSize(new Dimension(600, 600));
setLayout(null); // For testing purpose only...
MousePane mousePane = new MousePane();
mousePane.setBounds(100, 100, 400, 400);
add(mousePane);
}
protected void updateClickPoint(MouseEvent evt) {
absolutePoint = new Point(evt.getPoint());
evt = SwingUtilities.convertMouseEvent(evt.getComponent(), evt, this);
relativePoint = new Point(evt.getPoint());
System.out.println(absolutePoint);
System.out.println(relativePoint);
repaint();
}
protected void paintCross(Graphics2D g2d, Point p) {
g2d.drawLine(p.x - 5, p.y - 5, p.x + 5, p.y + 5);
g2d.drawLine(p.x - 5, p.y + 5, p.x + 5, p.y - 5);
}
/*
* This is not recommended, but I want to paint ontop of everything...
*/
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
if (relativePoint != null) {
g2d.setColor(Color.BLACK);
paintCross(g2d, relativePoint);
}
if (absolutePoint != null) {
g2d.setColor(Color.RED);
paintCross(g2d, absolutePoint);
}
}
}
protected class MousePane extends JPanel {
private Point clickPoint;
public MousePane() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
clickPoint = e.getPoint();
TestMouseClickPoint.this.updateClickPoint(e);
repaint();
}
});
setBorder(new LineBorder(Color.RED));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
if (clickPoint != null) {
g2d.drawLine(clickPoint.x, clickPoint.y - 5, clickPoint.x, clickPoint.y + 5);
g2d.drawLine(clickPoint.x - 5, clickPoint.y, clickPoint.x + 5, clickPoint.y);
}
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
new TestMouseClickPoint().setVisible(true);
}
}
Basically, it will paint three points. The point that the mouse was clicked (relative to the source of the event), the unconverted point in the parent container and the converted point with the parent container.
The next thing you need to do is determine the mouse location is actually been converted, failing that. I'd probably need to see a working example of your code to determine what it is you're actually doing.