I have 2 Components (which are both derived from jComponent). One is a Map and the other one are Territories within this map as you can see at the picture below.
The Map is drawn on a fixed size of 1200x800 so far. And for each territorys I have a list of points, which will connected be a the shapes of each country.
Drawing everything working quite good so far. But when I click on a country always the last drawn territory gets called. It seems like every territory is an layer over the whole map. How can I make it work, that if I click on a single territory that only this gets clicked. So that if I click on China, the click event of china gets called?
Below is the Code of the 2 Components in case it helps:
Map.java
public class Map extends JComponent {
private Collection<Territory> territories;
public Map(Collection<Territory> territories) {
super();
this.territories = territories;
}
#Override
public void paintComponent(Graphics graphics)
{
graphics.setColor(new Color(8, 114, 200));
graphics.fillRect(0, 0, this.getWidth(), this.getHeight());
for (Territory tmp : this.territories) {
TerritoryComponent territoryComponent = new TerritoryComponent(tmp);
this.add(territoryComponent);
}
this.updateUI();
}
}
TerritoryComponent.java
public class TerritoryComponent extends JComponent implements MouseListener {
private Territory territory;
public TerritoryComponent(Territory tmp) {
super();
this.setSize(this.getMaximumSize());
this.addMouseListener(this);
this.territory = tmp;
}
#Override
public void paintComponent(Graphics graphics)
{
for (Patch patch : this.territory.getPatches()) {
ArrayList<Point> points = patch.getPoints();
int x[] = new int[points.size() + 1];
int y[] = new int[points.size() + 1];
for (int i = 0; i < points.size(); i++) {
x[i] = (int) points.get(i).getX();
y[i] = (int) points.get(i).getY();
}
x[points.size()] = (int) points.get(0).getX();
y[points.size()] = (int) points.get(0).getY();
graphics.setColor(new Color(255, 255, 255));
graphics.fillPolygon(x, y, points.size() + 1);
graphics.setColor(new Color(52, 52, 52));
graphics.drawPolyline(x, y, points.size() + 1);
}
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("clicked " + this.territory.getName());
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
And for each territorys I have a list of points,
Create a Polygon Object to represent each of your territories.
Add each Polygon to an ArrayList
In the paintComponent() method you just iterate through the ArrayList to paint each Polygon
if I click on a single territory that only this gets clicked.
In the mouseClicked(...) event you then get the mouse point and your iterate through the ArrayList containing all your Polygons. You can then use the Polygon.contains(...) method to determine which territory you clicked on.
Also don't use the updateUI() method. The method should only be invoked when the LAF is changed. It should especially not be invoked in a painting method as this will cause the component to potentially repaint itself.
Related
What I'm trying to do is to draw circles and lines.
When the mouse is first pressed, I draw a small circle. Then, I need
to draw a line connecting the original point to the current
position of the mouse. When the mouse is released, the line
remains, but when I click again, everything disappears and I draw a
circle and a line all over again.
This is the code I have so far:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Canvas4 extends JComponent implements MouseListener, MouseMotionListener {
//constructor
public Canvas4() {
super();
addMouseListener(this);
addMouseMotionListener(this);
}
//some variables I may or may not use
int pressedX;
int pressedY;
int currentX;
int currentY;
int startDragX;
int startDragY;
int endDragX;
int endDragY;
int mouseReleasedX;
int mouseReleasedY;
//mouse events
public void mouseClicked(MouseEvent event) { }
public void mouseEntered(MouseEvent event) { }
public void mouseExited(MouseEvent event) { }
public void mousePressed(MouseEvent event) {
pressedX = event.getX();
pressedY = event.getY();
drawCircle();
startDragX = pressedX;
startDragY = pressedY;
}
public void mouseReleased(MouseEvent event) {
mouseReleasedX = event.getX();
mouseReleasedY = event.getY();
//repaint() here maybe???
}
//mouse motion events
public void mouseDragged(MouseEvent event) {
System.out.println("You dragged the mouse.");
endDragX = event.getX();
endDragY = event.getY();
drawLine();
}
public void mouseMoved(MouseEvent event) { }
//draw circle when mouse pressed
//this method works fine
public void drawCircle() {
Graphics g1 = this.getGraphics();
g1.setColor(Color.CYAN);
g1.fillOval(pressedX, pressedY, 10, 10);
}
//draw line when mouse dragged
//this is where I need help
public void drawLine() {
Graphics g2 = this.getGraphics();
g2.setColor(Color.RED);
g2.drawLine(pressedX, pressedY, mouseReleasedX, mouseReleasedY);
}
}
Then of course, there's a main method that creates the class object and adds it to a frame and whatnot.
My two specific questions are:
how do I draw a line as it's dragged? The code I have currently only draws a line to the last point of mouse release.
When do I repaint? If I repaint in the drawCircle() method, the circle blinks in and out instead of disappearing on the next click.
If you want to draw circles and lines then you need to keep an ArrayList of Shapes to draw. You would add an Ellipse2D.Double for the circle and a Line2D.Double for the line.
In the mousePressed event you add the Ellipse2D.Double object to the ArrayList, then you set up a temporary Line2D.Double object to contain your line information.
In the mouseDragged event you update Line2D.Double object with the new end point and then invoke repaint().
In the mouseReleased event you add the Line2D.Double object to the ArrayList and clear the variable referencing the Line2D.Double object.
Then in the paintComponent() method you add logic to:
iterate through the ArrayList to paint each Shape.
draw the Line2D.Double object when not null
Check out the Draw On Component example found in Custom Painting Approaches. This will show you the basic concept of this approach.
In the example the ArrayList only contains information on Rectangles so you will need to make it more generic to hold a Shape object. Both Ellispse2D.Double and Line2D.Double implement the Shape interface.
For drawing lines i have this.When you click mouse left you retain a point next click will retain another point making a line between them , and with mouse right you make the line between first point and last point (you can delete this "if (isClosed)" if you dont want)
Another thing : it's not a good precision because pointlocation return a double and drawline need an integer and the cast loses precision.
public class PolygonOnClick extends JPanel implements MouseListener, MouseMotionListener {
ArrayList<Point> points = new ArrayList<>();
static boolean isClosed = false;
PolygonOnClick() {
JFrame frame = new JFrame("Polygon ON CLICK");
frame.addMouseListener(this);
frame.setLocation(80, 50);
frame.setSize(1000, 700);
frame.add(this);
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.drawString("Click stanga pentru a incepe a desena , click dreapta pentru a inchide poligonul ", 15, 15);
for (int i = 1; i < points.size(); i++) {
graphics.drawLine((int) points.get(i - 1).getX(), (int) points.get(i - 1).getY(), (int) points.get(i).getX(), (int) points.get(i).getY());
}
if (isClosed) {
graphics.drawLine((int) points.get(points.size() - 1).getX(), (int) points.get(points.size() - 1).getY(), (int) points.get(0).getX(), (int) points.get(0).getY());
}
}
#Override
public void mousePressed(MouseEvent e) {
if (!isClosed) {
if (e.getButton() == MouseEvent.BUTTON1) {
points.add(e.getPoint().getLocation());
}
}
if (e.getButton() == MouseEvent.BUTTON3) {
isClosed = true;
}
repaint();
}
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 have an array list that keeps track of the "dots" and am using an array to keep track of the different sizes. The size is changed via a mouseWheelListener and the change is saved in that array. However for some reason ALL of the dots are resized which is not what I want. After running through it a few times everything looks like it should work but I must be missing something simple. My code as follows.
//********************************************************************
// DotsPanel.java
// Represents the primary panel for the Dots program.
//********************************************************************
import java.util.ArrayList;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.*;
public class DotsPanel extends JPanel
{
private int SIZE = 10; // radius of each dot
private int[] dotSizes = new int[10000];
int i = 0;
private ArrayList<Point> pointList;
//-----------------------------------------------------------------
// Constructor: Sets up this panel to listen for mouse events.
//-----------------------------------------------------------------
public DotsPanel()
{
pointList = new ArrayList<Point>();
addMouseListener (new DotsListener());
addMouseMotionListener(new DotsListener());
addMouseWheelListener(new DotsListener());
setBackground(Color.red);
setPreferredSize(new Dimension(300, 200));
}
//-----------------------------------------------------------------
// Draws all of the dots stored in the list.
//-----------------------------------------------------------------
public void paintComponent(Graphics page)
{
super.paintComponent(page);
page.setColor(Color.cyan);
for (Point spot : pointList)
//change size to sizes[n]
page.fillOval(spot.x-dotSizes[pointList.size()-1], spot.y-dotSizes[pointList.size()-1], dotSizes[pointList.size()-1]*2, dotSizes[pointList.size()-1]*2);
page.drawString("Count: " + pointList.size(), 5, 15);
}
//*****************************************************************
// Represents the listener for mouse events.
//*****************************************************************
private class DotsListener implements MouseListener, MouseMotionListener, MouseWheelListener
{
//--------------------------------------------------------------
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is pressed.
//--------------------------------------------------------------
public void mousePressed(MouseEvent event)
{
pointList.add(event.getPoint());
repaint();
}
//--------------------------------------------------------------
// Provide empty definitions for unused event methods.
//--------------------------------------------------------------
public void mouseClicked(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
#Override
public void mouseDragged(MouseEvent event)
{
pointList.add(event.getPoint());
dotSizes[pointList.size()-1] = SIZE;
//store size of dot here
repaint();
}
#Override
public void mouseMoved(MouseEvent event) {}
#Override
public void mouseWheelMoved(MouseWheelEvent event)
{
SIZE -= event.getWheelRotation();
repaint();
}
}
}
In this code:
for (Point spot : pointList)
//change size to sizes[n]
page.fillOval(spot.x-dotSizes[pointList.size()-1], spot.y-dotSizes[pointList.size()-1], dotSizes[pointList.size()-1]*2, dotSizes[pointList.size()-1]*2);
you are using a single dot size for all the dots: the size of the last dot that was added. (There's even a comment there to fix the problem!) You need to iterate with an index so you can index into the dotSizes array as well as the pointList ArrayList:
for (int i = 0; i < pointList.size(); ++i) {
Point spot = pointList.get(i);
int size = dotSizes[i];
page.fillOval(spot.x-size, spot.y-size, size*2, size*2);
}
It would be much better to define your own "point with size" class that encapsulates a Point and a size:
class Spot extends Point {
public int size;
public Spot(int x, int y, int size) {
super(x, y);
this.size = size;
}
}
Then change your pointList to an ArrayList<Spot> and you can go back to iterating over a single list:
for (Spot spot : pointList)
page.fillOval(spot.x-spot.size, spot.y-spot.size, 2*dot.size, 2*dot.size);
Of course, you'll also have to update the code that adds points to the list accordingly.
As an aside, it seems to me that your mousePressed handler has a bug: it does not add a size when it adds a point. Switching to a Spot class would also help avoid that kind of problem.
for (Point spot : pointList)
//change size to sizes[n]
page.fillOval(spot.x-dotSizes[pointList.size()-1], spot.y-dotSizes[pointList.size()-1], dotSizes[pointList.size()-1]*2, dotSizes[pointList.size()-1]*2);
Here is your problem. You aren't changing the size to size[n] but you are changing the size of every one to size[pointList.size()-1] or in other words, the last one. You will probably have to use a regular for loop unless you want to find the index of the current point.
I want to develop a Java Applet which shows visualization of Sorting. So I have written code like this...
public class SortNumbersGUI extends JApplet {
private static final long serialVersionUID = 1L;
List<Integer> randomList;
JLabel numberLabel;
JButton sortButton;
#Override
public void init() {
randomList = MyRandom.myRandom();
setSize(400, 400);
setLayout(new FlowLayout());
sortButton = new JButton("Sort");
sortButton.setBounds(50, 0, 50, 10);
sortButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Collections.sort(randomList);
removeAll();
repaint();
}
});
this.add(sortButton);
super.init();
}
#Override
public void paint(Graphics g) {
int y = 20;
for (Integer i : randomList) {
g.drawString(i.toString(), 0, y + 10);
g.fillRect(20, y, (i * 10) / 10, 5);
y = y + 20;
}
}
}
But, I am facing two problems here..
Sort button, which is JButton is not always visible. When i am placing mouse cursor over that sort button place then only it is appearing.
The results are overriding when I am calling repaint() method. As the bellow image
Result Image (As I don't have reputation I am pasting the image link.)
Please help me to solve this problem.
Thank you in Advance.
#Override
public void paint(Graphics g) {
// ..
Should be:
#Override
public void paint(Graphics g) {
super.paint(g); // paints **BG**, border etc.
// ..
Your problem is that you are overridding paint instead of paintComponent, and also you are not calling super.paint/Component (also a good practice, though possibly not 100% necessary here).
Because of how you've overridden paint(), you button is not getting automatically painted when the JApplet is painted
I want to be able to click the JButton and change the next individual ellipse I make to its respective color, Red or Black, from its default Blue. So far I'm able to change the color of all the ellipses I have made.
public class MainClient {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainClient window = new MainClient();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public MainClient() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final SpriteField panel = new SpriteField();
panel.setBounds(0, 110, 782, 331);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
int tX = e.getX();
int tY = e.getY();
panel.CreateObjectAt(tX,tY);
}
});
frame.getContentPane().setLayout(null);
panel.setBackground(Color.LIGHT_GRAY);
frame.getContentPane().add(panel);
final JButton btnRed = new JButton("black");
btnRed.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
panel.setColor(Color.BLACK);
}
});
btnRed.setBounds(229, 500, 97, 25);
frame.getContentPane().add(btnRed);
JButton btnRed_1 = new JButton("red");
btnRed_1.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
panel.setColor(Color.RED);
}
});
btnRed_1.setBounds(342, 500, 97, 25);
frame.getContentPane().add(btnRed_1);
}
}
I have a SriteField Class
public class SpriteField extends JPanel
{
ArrayList<RoundSprite>mSprites = new ArrayList<RoundSprite>();
private Color color = Color.BLUE;
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
AffineTransform tOldTransform = g2.getTransform();
for(RoundSprite tOne:mSprites)
{
tOne.DrawSprite(g2, color);
}
g2.setTransform(tOldTransform);
}
public void CreateObjectAt(int tX, int tY)
{
//make sprite
RoundSprite mLonely = new RoundSprite();
//set position
mLonely.SetPosition(tX, tY);
//add to array
mSprites.add(mLonely);
repaint();
}
public void setColor(Color color)
{
this.color = color;
repaint();
}
}
I have a RoundSprite Class
public class RoundSprite
{
int mX;
int mY;
private Color color;
void DrawSprite(Graphics2D g2, Color color)
{
AffineTransform tOldTransform = g2.getTransform();
g2.setColor(color);
g2.translate(mX, mY); //got it out from the corner
g2.draw(new Ellipse2D.Double(-15, -22, 30, 50));
g2.setTransform(tOldTransform);
}
public void SetPosition(int tX, int tY) //g2.translate
{
mX = tX;
mY = tY;
}
There are three core issues...
One
In order for the panel_1 MouseListener to change the color of the sprites within panel, the MouseListener requires a reference to the panel.
You could declare the panel as final within the initialize method, but I prefer to make the panel a private instance field of the MainClient.
Two
In order to change the color, the SpriteField needs to provide a means by which interested parties can request a change.
This is going to require SpriteField to provide some kind of method that interested parties can call when they need to, for example, a setColor method...
public class SpriteField extends JPanel {
private Color color = Color.BLACK;
//...
public void setColor(Color color) {
this.color = color;
repaint();
}
Then panel_1's MouseListener can now reference the SpriteField and call a method which can produce a change...
panel_1.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Click");
panel.setColor(Color.BLUE);
}
});
Three
You need some way to tell the sprite what color it should be...
This one depends on what it is you want to achieve...
If you want to paint ALL the sprites the same color, then you need some way to tell the sprites which color they should use, for example, you could change the DrawSprite method to accept a Color parameter...
public class RoundSprite {
//...
void DrawSprite(Graphics2D g2, Color color) {
And within the SpriteField paintComponent, pass the color to the sprite
public class SpriteField extends JPanel {
//...
private Color color = Color.BLACK;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
AffineTransform tOldTransform = g2.getTransform();
for (RoundSprite tOne : mSprites) {
tOne.DrawSprite(g2, color);
}
g2.setTransform(tOldTransform);
}
Now, this will paint ALL the sprites within the field the same color, if you want to change the color of individual sprites, this comes infinitely more complicated...
Lets leave aside the issues of selecting a sprite for the moment...
Basically, you would need to apply the same principle from point Two to the RoundSprite class.
You would need to supply a method that could set/get the desired color. You would need to store this value in an instance field of the RoundSprite class and when DrawSprite is called, apply that Color to the Graphics context.
This would mean that SpriteField won't need to perform any color management, beyond passing the request for change from a caller to the selected sprite, so it would still need a setColor method...
Side Notes...
You might like to have a read through Code Conventions for the Java TM Programming Language, it will make it easier for people to read your code and for you to read others
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
Oh, and you realise that there already is a JButton class that probably should be used instead of a JPanel with a MouseListener....?