I have asked a similar question a while ago here, but didn't get an answer. The original question was about changing the color of a shape after clicking on it. But I am puzzled on how to access the shape at all after it is drawn.
This is my paintComponent method
#Override
protected void paintComponent(Graphics graph) {
super.paintComponent(graph);
Graphics2D g = (Graphics2D) graph;
// smooth graphics
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// moving to the middle of the panel
g.translate(this.getWidth()/2, this.getHeight()/2);
// painting colored arcs
for(int i = 0; i < 4; i++) {
g.setColor(dimColors[i]);
g.fill(arcs[i]);
}
// painting borders
g.setColor(Color.BLACK);
g.setStroke(new BasicStroke(5F));
g.drawLine(-98, 0, 98, 0);
g.drawLine(0, -98, 0, 98);
g.draw(circle);
// painting central white circle
g.setColor(Color.WHITE);
g.fill(smallCircle);
g.setColor(Color.BLACK);
g.draw(smallCircle);
}
the arcs[] array contains a bunch of Arc2D's that are drawn on the panel. My question is now, if I want to change the color of, for example arcs[0], how do I do that?
Thanks!
EDIT: I now have this MouseAdapter event
private class MyMouseAdapter extends MouseAdapter {
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
Component c = getComponentAt(p);
Graphics g = c.getGraphics();
dimColors[1] = Color.RED;
paintComponent(g);
}
}
And it works, it changes the color of arc[1] because arcs[1] has dimColors[1] set as color when drawing it.
However, I still can't figure out how to check wether the right arc was clicked. Right now you just click anywhere on the graphics panel and it changes the color of that specific arc
This doesn't answer your earlier question, however it does answer your question of click detection. To do this it is best to use Graphics2D because it is a lot easier to write than most other options. Here is an example:
public class GraphicsPanel extends JPanel implements MouseListener
{
private Rectangle2D rect;
First we create our Graphics2D rectangle rect.
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)(g);
g2d.setColor(Color.GREEN);
rect = new Rectangle2D.Double(70, 70, 100, 100);
g2d.fill(rect);
this.addMouseListener(this);
}
And then we override the paintComponent method and create our new Rectangle2D.Double object.
We then fill the rectangle with g2d.fill() and then add a mouse listener to the JPanel.
public void mousePressed(MouseEvent e)
{
if(rect.contains(e.getX(), e.getY()))
System.out.println("Rectangle clicked");
}
}
Finally, we need to see if that rectangle contains the point where the user clicked. To do this, simply see if the rectangle we created contains the user's click location by using the Rectangle2D.double's contains(int x, int y) method. That's it!
if I want to change the color of, for example arcs[0], how do I do that?
A line (or whatever) only exists as a bunch of pixels that were painted in the original color. To change its color you must change the current color and draw it again.
Related
I created a customized Class which extends JButton to create buttons with a Graphic image for a button panel, I override paintComponenet() which also calls a method drawColoredShape().
The Graphic images are mostly GeneralPath Shapes.
When running the application, paintComponent() of each Button is repeatedly called.
Question: Does Overriding paintComponent to add custom GeneralPath graphics on the JButton significantly slow down the application. Is there a way to make it more efficient?
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
drawColoredShape(g2, getText());
g2.setColor(new Color(myColor));
g2.dispose();
}
/**
* Draws Custom Shape for this button.
*/
private void drawColoredShape(Graphics2D g2, String txt) {
Shape sh = null;
case "Cat":
this.setForeground(Color.BLUE);
g2.setPaint(Color.Blue);
g2.draw(SPECIAL_SHAPE); // from General Path defined somewhere
break;
case "Dog":
break;
... etc
}
public void actionPerformed(ActionEvent e)
{
try
{
//récupérer les coordonnées(x,y) du text area
int x=Integer.parseInt(f.x.getText());
int y=Integer.parseInt(f.y.getText());
int puissance=Integer.parseInt(f.p.getText());
f.APs.add(new AccessPoint (x,y,f.APs.size(),puissance));
String ch="Point d'accés "+String.valueOf(f.APs.size())+" Center xc = "+String.valueOf(x)+" yc= "+String.valueOf(x);
System.out.println(ch);
f.t.add(ch);
Graphics g ;
g= f.getGraphics();
paintComponent(g);
}
catch(Exception e1){System.out.println("Erreur");}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if(f.APs.size()!=0)
{
try {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
int currPoint =f.APs.size()-1;
int puissance =f.APs.get(currPoint).p;
Color C= new Color(128,puissance,puissance,puissance);
Shape circle = new Ellipse2D.Float(f.APs.get(currPoint).x-(f.APs.get(currPoint).diametre/2),
f.APs.get(currPoint).y-(f.APs.get(currPoint).diametre/2),
f.APs.get(currPoint).diametre,f.APs.get(currPoint).diametre);
g2d.draw(circle);
g2d.setPaint(C);
g2d.fill(circle);
}catch(Exception e2){System.out.println("Erreur");}}
}
g= f.getGraphics();
paintComponent(g);
Don't use getGraphics(). Any painting done using that approach will only be temporary (as you have noticed)
Don't invoke paintComponent() directly. Swing will invoke the paintComponent(...) method as required and pass in the proper Graphics object.
The painting method should only ever do painting. It should not change the state of the component.
So if you want to dynamically add shapes to be painted you have two approaches:
Keep an ArrayList of the shapes to be painted. Create a method like addShape(..) to update the ArrayLIst. Then your painting code will iterate through the ArrayList to paint each shape.
Paint directlyl to a BufferedImage. Then paint the BufferedImage.
Working example of both approaches can be found in Custom Painting Approaches
I'm trying to fill each circle with a colour. The paintComponent method is supposed to create the general outline of a traffic light.
Then the other methods are supposed to fill each circle with a different color, depending on what color the traffic light is going to be. I get the error, that it cant find symbol( e.g
TrafficLight.java:56: error: cannot find symbol
g2.fill(circle3);
I didnt think I had to pass the circle variables into the other methods, if they were in the same class. I tried adding the circle variables and then i get identifier expected. Pretty sure there's something about the passing of variables that im not getting, but any help would be appreciated
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class TrafficLight extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2= (Graphics2D) g;
Rectangle box= new Rectangle(100,800,200,700);
g2.setPaint(Color.BLACK);
g2.fill(box);
g2.draw(box);
Ellipse2D.Double circle1=new Ellipse2D.Double(200,200,200,200);
g2.draw(circle1);
Ellipse2D.Double circle2=new Ellipse2D.Double(200,200,200,400);
g2.draw(circle2);
Ellipse2D.Double circle3=new Ellipse2D.Double(200,200,200,600);
g2.draw(circle3);
}
public void drawRed(Graphics g)
{
Graphics2D g2=(Grpahics2D) g;
g2.setPaint(Color.RED);
g2.fill(circle1);
g2.setPaint(Color.BLACK);
g2.fill(circle2);
g2.fill(circle3);
}
public void drawGreen(Graphics g)
{
Graphics2D g2= (Graphics2D) g;
g2.setPaint(Color.GREEN);
g2.fill(circle3);
g2.setPaint(Color.BLACK);
g2.fill(circle2);
g2.fill(circle1);
}
public void drawYellow(Graphics g)
{
Graphics2D g2= (Graphics2D) g;
g2.setPaint(Color.YELLOW);
g2.fill(circle2);
g2.setPaint(Color.BLACK);
g2.fill(circle1);
g2.fill(circle3);
}
}
The entire code is missing.
But the general solution is to have the paintComponent method do all.
So have a state red/green/yellow.
public class TrafficLight extends JComponent {
public enum Light { RED, GREEN, YELLOW, BLACK }
private Light light = Light.BLACK;
public void setLight(Light light) {
this.light = light;
repaint(50L); // Repaint almost immediately.
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
...
// Draw red bulb
g2.setPaint(light == Light.RED ? Color.RED : Color.BLACK);
...
// Draw yellow bulb
g2.setPaint(light == Light.YELLOW ? Color.YELLOW : Color.BLACK);
...
// Draw green bulb
g2.setPaint(light == Light.GREEN ? Color.GREEN : Color.BLACK);
Switching the light then alters the state and causes a repaint. There are some repaint methods.
Instead of the enum one might use Color light;, but that is not so clear.
Also consider an enum value BLACK, for semaphores without electricity.
To handle automatic timed switching, one might use a swing Timer.
My rectangle code:
class Rectangle extends JPanel {
int x = 105;
int y= 100;
int width = 50;
int height = 100;
public void paint(Graphics g) {
g.drawRect (x, y, width, height);
g.setColor(Color.WHITE);
}
Rectangle r = new Rectangle();
and I have a button "rotate". When the user presses the button with the mouse, the rectangle must rotate 15 degrees.
This is my action code:
public void actionPerformed(ActionEvent e){
Object source = e.getSource();
if( source == rotate){
AffineTransform transform = new AffineTransform();
transform.rotate(Math.toRadians(15), r.getX() + r.getWidth()/2, r.getY() + height/2);
r.add(transform);
}
}
But the code doesn't work. I don't know why? What do you think?
My edited-action-code part:
public void actionPerformed(ActionEvent e){
Object source = e.getSource();
if( source == rotate){
Paint p = new Paint();
panel1.add(r);
repaint();
}
}
class Paint extends JPanel {
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.WHITE);
g2d.translate(r.getX()+(r.WIDTH/2), r.getY()+(r.HEIGHT/2));
g2d.rotate(Math.toRadians(15));
r.equals(g2d);
repaint();
}
}
Custom painting is done by overriding the paintComponent() method, not paint(). Don't forget the super.paintComponent() at the start.
The paintComponent() method is where the painting is done so that is were you need the rotation code. So you could set a variable to indicate if you need to do the rotation or not. So all the ActionListener does is set the variable and then invoke repaint().
Or, I've never tried applying a rotation directly to the Rectangle (I've always applied it to the Graphics object in the painting method). Maybe you just need to invoke repaint() on the panel in your ActionListener. The panel won't know you've changed the Rectangle, so you need to tell it to repaint itself.
I'm attempting to create a JDialog frame that will have a background image and an interactive JPanel above that. In this context, the JDialog would represent a 'combat' field where units would be able to be selected and moved. The game is space based so there will be an ArrayList of ships and possibly a planet object to defend.
I've been able to override paintComponent to draw a rough circle representing a 'planet' but couldn't get the background JLabel image to show. Then I could get the background JLabel to show but couldn't see the circles. Ideally, I want to replace the circles with actual images for each ship type and unique for planets. I'm open to other methods than using this JDialog/JLayered/Customer JPanel if there is a better way to accomplish this. I've been working on this for more hours than I can count.
I've created a JDialog, added a JLayeredPane and set a JLabel within that for the background. I've written a custom class extending JPanel that would be added to the JLayeredPane above the JLabel, which draws circles for the planet and units.
The reason I chose a JPanel is so I can check for mouse events to determine what the player is selecting (the planet to add resources) or a ship (for movement and attack).
for the custom JPanel I've written this simple extension:
public class SectorPnl extends javax.swing.JPanel implements MouseInputListener, ActionListener {
private int circleY, circleX, circleRadius;
private Sector sector;
private Shape planetShape;
private Shape shipShape;
private Ship ship;
private Planet planet;
private Invasion inv;
private ArrayList<ShipType> shipBuild;
public SectorPnl(Sector sector, Invasion inv)
{
initComponents();
this.sector = sector;
this.inv = inv;
this.planet = sector.getPlanet();
shipBuild = new ArrayList();
Timer update = new Timer(28, this);
update.start();
if ( sector.hasPlanet() )
{
circleRadius = (int) sector.getPlanet().getPlanetRadius();
circleX = (int) sector.getPlanet().getPositionX();
circleY = (int) sector.getPlanet().getPositionY();
planetShape = new Ellipse2D.Double(circleX, circleY, circleRadius,
circleRadius);
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponents(g);
Graphics2D g2 = (Graphics2D) g;
if ( planetShape != null)
{
g2.setColor(Color.red);
g2.fill(planetShape);
g2.draw(planetShape);
}
if ( shipShape != null )
{
g2.setColor(Color.white);
g2.fill(shipShape);
g2.draw(shipShape);
}
}
And this is the lines for adding it to the JDialog:
sectorDlg.setTitle(sector.getName());
sectorDlg.setVisible(true);
sectorDlg.setSize(800,800);
SectorPnl sectorPnl = new SectorPnl(sector, inv);
sectorPnl.addMouseListener(sectorPnl);
sectorPnl.addMouseMotionListener(sectorPnl);
sectorLayer.setLayer(sectorPnl, 100);
sectorLayer.setBounds(0, 0, 800, 800);
It's possible for a JLabel to show both an icon and text, but I think you have to specify the relative position of the icon and the text.
ImageIcon icon = createImageIcon("images/middle.gif");
. . .
label1 = new JLabel("Image and Text",
icon,
JLabel.CENTER);
//Set the position of the text, relative to the icon:
label1.setVerticalTextPosition(JLabel.BOTTOM);
label1.setHorizontalTextPosition(JLabel.CENTER);
This was taken from Oracle's How to use labels.
I don't know what happens if you try to set the background and foreground colors.
I think you would be better off if you used a JPanel as a canvas, and just drew the icons and text where you wanted them. You can process mouse clicks on the JPanel.
I've got the background image displaying now and having the 'ship' and 'planet' painted on top. I'm still trying to determine how to pick a mouse event when an Image is clicked though.
I've added a few new variables:
private Image bgImage;
private Image shipImage;
For correcting the background issue I added these lines after initComponents();
ImageIcon ii = new ImageIcon(this.getClass().getResource("sector_Bg1.png"));
bgImage = ii.getImage();
I've changed my paintComponent method to add ship icons (in .png or .jpg format) and paint them as well.
#Override
public void paintComponent(Graphics g)
{
super.paintComponents(g);
Graphics2D g2 = (Graphics2D) g;
Graphics2D g3 = (Graphics2D) g;
Graphics2D g4 = (Graphics2D) g;
g3.drawImage(bgImage, 0, 0, null);
if ( planetShape != null)
{
g2.setColor(Color.red);
g2.fill(planetShape);
g2.draw(planetShape);
}
for ( Ship s : sector.getShips() )
{
ImageIcon si = new ImageIcon(this.getClass().getResource("isd.png"));
shipImage = si.getImage();
g3.drawImage(shipImage, (circleX + shipImage.getHeight(null)), circleY, null);
}
}
Now....how to pick up mouse events within a drawn Image?