How do I connect the dots to get a polygon in JAVA? - java

I'm making an application for creating convex
polygon.
I imagined it to be so that I first set the vertices of the polygon and then create it.
I was able to make the addition of points (vertices). Now I need help connecting the dots with a line.
This is what it looks like:
It looks like this
And I would like when I click the Draw Polygon button that these points connect and it looks like a polygon, like this:
and it should look like this when I click the button
Here's the code so you can run it yourself:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
public class MyPaint{
public static void main(String[] args){
final PadDraw drawPad = new PadDraw();
JFrame frame = new JFrame("Draw Polygon");
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(100, 100, 450, 300);
Container contentPane = frame.getContentPane();
((JComponent) contentPane).setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
frame.setContentPane(contentPane);
JPanel buttonPanel = new JPanel();
buttonPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
contentPane.add(buttonPanel, BorderLayout.SOUTH);
JButton buttonDrawPolygon = new JButton("Draw Polygon");
buttonPanel.add(buttonDrawPolygon);
JButton buttonReset = new JButton("Reset");
buttonPanel.add(buttonReset);
contentPane.add(drawPad, BorderLayout.CENTER);
}
}
class PadDraw extends JComponent{
private Image image;
private Graphics2D graphics2D;
private int currentX , currentY , oldX , oldY ;
public PadDraw(){
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
oldX = e.getX();
oldY = e.getY();
}
});
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
currentX = e.getX();
currentY = e.getY();
if(graphics2D != null) {
graphics2D.drawLine(oldX, oldY, currentX, currentY);
repaint();
oldX = currentX;
oldY = currentY;
}
System.out.println(oldX + " " + oldY);
}
});
}
public void paintComponent(Graphics g){
if(image == null){
image = createImage(getSize().width, getSize().height);
graphics2D = (Graphics2D)image.getGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.setStroke(new BasicStroke(5));
}
g.drawImage(image, 0, 0, null);
}
}

Introduction
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.
Here's your revised GUI
Here's the GUI with four points
Here's the GUI with a polygon
I'm not showing it, but the Reset button clears the drawing area.
Explanation
When I create a Swing GUI, I use the model-view-controller pattern. This pattern allows me to separate my concerns and focus on one part of the Swing application at a time.
A Swing application model consists of one or more plain Java getter/setter classes.
A Swing view consists of a JFrame and one or more JPanels.
Swing controllers are the listeners that are attached to JButtons and drawing JPanels.
Model
For this application, I created a PolygonModel class. The class holds a boolean that tells me whether or not to draw the polygon and a java.util.List of java.awt.Point instances. The Point class holds an X and Y int value.
View
All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I broke up your main method into a couple of methods. I separate the creation of the JFrame from the creation of the JPanels. This allows me to separate my concerns and focus on one part of the GUI at a time.
The JFrame methods must be called in a specific order. This is the order I use for most of my Swing applications.
I changed your PadDraw class to extend a JPanel. I moved the MouseAdapter code to its own class. Your drawing panel should draw. Period. Nothing else.
The paintComponent method always starts with a call to the super.paintComponent method. This maintains the Swing paint chain and helps to eliminate unwanted drawing artifacts.
The drawing JPanel is cleared before every repaint. Therefore, you have to completely redraw your image each time. That's why we store the List of Point instances in the model.
Controller
I created three controller classes.
The PointListener class extends MouseAdapter. Notice how simple the mousePressed method becomes with an application model.
The two JButtons each have their own ActionListener. Since they are so simple, I made each of them lambdas.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could make them public and post the code as one block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
public class PolygonImage implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new PolygonImage());
}
private final PolygonModel model;
private final PadDraw drawPad;
public PolygonImage() {
this.model = new PolygonModel();
this.drawPad = new PadDraw(this, model);
}
#Override
public void run() {
JFrame frame = new JFrame("Draw Polygon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawPad, BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel buttonPanel = new JPanel();
buttonPanel
.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
JButton buttonDrawPolygon = new JButton("Draw Polygon");
buttonDrawPolygon.addActionListener(event -> {
model.setConnectPoints(true);
repaint();
});
buttonPanel.add(buttonDrawPolygon);
JButton buttonReset = new JButton("Reset");
buttonReset.addActionListener(event -> {
model.setConnectPoints(false);
model.clearList();
repaint();
});
buttonPanel.add(buttonReset);
return buttonPanel;
}
public void repaint() {
drawPad.repaint();
}
public class PadDraw extends JPanel {
private static final long serialVersionUID = 1L;
private final PolygonModel model;
public PadDraw(PolygonImage view, PolygonModel model) {
this.model = model;
this.addMouseListener(new PointListener(view, model));
this.setPreferredSize(new Dimension(450, 300));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.black);
// Draw points
for (Point p : model.getPoints()) {
int radius = 6;
int diameter = radius + radius;
g2d.fillOval(p.x - radius, p.y - radius, diameter, diameter);
}
// Draw polygon
if (model.isConnectPoints()) {
g2d.setStroke(new BasicStroke(5));
List<Point> points = model.getPoints();
if (points.size() >= 1) {
Point old = points.get(0);
for (int index = 1; index < points.size(); index++) {
Point p = points.get(index);
g2d.drawLine(old.x, old.y, p.x, p.y);
old = p;
}
Point p = points.get(0);
g2d.drawLine(p.x, p.y, old.x, old.y);
}
}
}
}
public class PointListener extends MouseAdapter {
private final PolygonImage view;
private final PolygonModel model;
public PointListener(PolygonImage view, PolygonModel model) {
this.view = view;
this.model = model;
}
#Override
public void mousePressed(MouseEvent event) {
model.addPoint(event.getPoint());
view.repaint();
}
}
public class PolygonModel {
private boolean connectPoints;
private final List<Point> points;
public PolygonModel() {
this.points = new ArrayList<>();
this.connectPoints = false;
}
public void setConnectPoints(boolean connectPoints) {
this.connectPoints = connectPoints;
}
public boolean isConnectPoints() {
return connectPoints;
}
public void clearList() {
this.points.clear();
}
public void addPoint(Point point) {
this.points.add(point);
}
public List<Point> getPoints() {
return points;
}
}
}

Related

output not being displayed on GUI properly

I'm trying to display 4 tiles in a row. However, only the last tile seems to be displayed on the GUI and I'm not sure why. I've looked around the internet and I cant find any solution I can comprehend.
here is my code:
import java.awt.*;
import javax.swing.*;
// creates a GUI and displays the game tiles made in gameTile.java
public class testGameTileClass {
gameTile tile1;
gameTile tile2;
gameTile tile3;
gameTile tile4;
public testGameTileClass() {
// sets up GUI
JFrame frame = new JFrame("Game Tiles");
JPanel panel = new JPanel();
frame.setSize(1280, 720);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
testConstructor1(frame);
frame.setVisible(true);
}
public void testConstructor1(JFrame frame) {
tile1 = new gameTile(10, 10, 64, Color.RED.darker());
frame.add(tile1);
tile2 = new gameTile(84, 10, 64, Color.GREEN);
frame.add(tile2);
tile3 = new gameTile(148, 10, 64, Color.BLUE);
frame.add(tile3);
tile4 = new gameTile(212, 10, 64, Color.ORANGE);
frame.add(tile4);
}
public static void main(String[] args) {
new testGameTileClass();
}
}
and here is the gameTile class code:
import javax.swing.*;
import java.awt.*;
public class gameTile extends JPanel {
private Color color; //color of gameTile
private int x, y, size; // x and y coords, and size of gameTile
public gameTile (int x_, int y_, int size_, Color color_) {
x = x_;
y = y_;
size = size_;
color = color_;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
g.setColor(color);
g.fillRect(x, y, size, size);
g2.setColor(Color.BLACK);
g2.drawRect(x, y, size, size);
}
}
This is my result. Is there any way to fix this?
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay close attention to the Performing Custom Painting section.
I went ahead and created the GUI you were trying to create.
When I create a Swing application, I use the model-view-controller (MVC) pattern. This pattern allows me to separate my concerns and focus on one small part of the application at a time.
I used your game tile idea and created a plain Java getter/setter class to hold a tile. In this simple example, all I needed was the color and rectangle information. In a more complex example, you could create a BufferedImage with a more detailed image than just one color.
I collected four GameTile instances in a separate model class. The model class would hold all the information for the application in another separate plain Java getter/setter class.
Writing the view was straightforward after creating the model. You have one drawing JPanel where you draw all the game tiles.
In this simple example, there's no controller. The controller is made up of one or more Action or ActionListener classes.
Here's the complete runnable code. I made all the additional classes inner classes so I could post the code as one block. You should separate the classes into different files within a package. For most of my Swing projects, I create separate model, view, and controller packages.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestGameTileExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new TestGameTileExample());
}
private final GameModel model;
public TestGameTileExample() {
this.model = new GameModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Game Tiles");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(new DrawingPanel(model), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final GameModel model;
public DrawingPanel(GameModel model) {
this.model = model;
this.setPreferredSize(new Dimension(1200, 720));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (GameTile tile : model.getTiles()) {
g.setColor(Color.BLACK);
Rectangle r = tile.getTile();
g.fillRect(r.x, r.y, r.height, r.width);
g.setColor(tile.getColor());
g.fillRect(r.x + 2, r.y + 2, r.height - 4, r.width - 4);
}
}
}
public class GameModel {
private final GameTile[] tiles;
public GameModel() {
this.tiles = new GameTile[4];
tiles[0] = new GameTile(10, 10, 64, 64, Color.RED.darker());
tiles[1] = new GameTile(84, 10, 64, 64, Color.GREEN);
tiles[2] = new GameTile(148, 10, 64, 64, Color.BLUE);
tiles[3] = new GameTile(212, 10, 64, 64, Color.ORANGE);
}
public GameTile[] getTiles() {
return tiles;
}
}
public class GameTile {
private final Color color;
private final Rectangle tile;
public GameTile(int x, int y, int width, int height, Color color) {
this.tile = new Rectangle(x, y, width, height);
this.color = color;
}
public Color getColor() {
return color;
}
public Rectangle getTile() {
return tile;
}
}
}

How to move around an object drawn on a panel with button click

I am trying to build a bounce game in Java. My project has three classes ie the Main class which creates a new window(frame) where the game buttons and bounce objects are drawn.
The GameInterface class which represents the properties of the frame being drawn and the RightPanel class which I created so that I could override the paint(Graphics) method to draw my bounce object. So far this is what I have managed to draw with the code.
You can see that I have two JPanels, one that holds my buttons and the other one that accepts the drawing of a round ball on it ie RightPanel
I need help with the Button Event listeners to move the ball up and down and when user holds the button down, it needs to keep moving down until reaches the down order, sam for the up button.
The code am using is provided below.
GameInterface class
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
public class GameInterface extends JFrame {
//we need this panel declaration in the class level for reference from other methods
RightPanel rightpanel;
//define the physical properties of the window
public GameInterface(){
setSize(new Dimension(600, 600));
setResizable(false);
setTitle("Bounce Game");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(Color.black);
//define a new JSplitPane and use it to add two JPanels
JPanel leftpanel= new JPanel();
//add buttons to the left panel programatically
JButton up= new JButton("Move up");
//set the event listeners for the buttons
up.addActionListener(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
//move my ball up
//clear and redraw the ball while in a new position, use a timer or
something
}
});
JButton down = new JButton("Move down");
down.addActionListener(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
//move my ball down
// rightpanel.getGraphics.fillColor(Color.RED);
}
});
leftpanel.add(up);
leftpanel.add(down);
//add a new RightPanel with a drawn red object
rightpanel= new RightPanel();
JSplitPane splitpane= new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,leftpanel,rightpanel);
this.add(splitpane);
setVisible(true);
}
}
RightPanel class
import javax.swing.*;
import java.awt.*;
public class RightPanel extends JPanel {
//define the position where the circle will be drawn
private int positionX=150;
private int positionY=150;
//I had an idea where we need a timer and then on delay we
//decrement positionX by 3 for move down but can't figure out how to clear RightPanel
private int radius=100;//as the shape is a circle
//override the paint method to draw the bounce ball on the second panel
#Override
public void paint(Graphics g) {
g.setColor(Color.RED);
g.fillOval(positionX,positionY,radius,radius);
}
}
Main class
public class Main
{
public static void main(String args[]){
new GameInterface();
}
}
How do I add logic to my code to make it move the circle up an down, Thank You.
I tried using a timer object to clear the panel and then redraw the ball in the new position of the ball but it draws a vertical bar, not clearing the original ball drawn.
Never call getGraphics() on a component.
Override paintComponent not paint
Call the super.paintComponent(g) in your override.
Give the RightPanel class setter methods that allow you to change the positionX and positionY locations for drawing,
In the button listener, call an appropriate setter method, and then call repaint() on on the RightPanel instance after changing the positions.
For example:
The key code below is here in the ActionListener where you update the position values and call repaint:
moveRightBtn.addActionListener(e -> {
// get and update the x position
int x = drawOval.getPositionX();
x += DELTA;
// call the setter method
drawOval.setPositionX(x);
// request that Java repaint the JPanel
drawOval.repaint();
});
and in the drawing JPanel's paintComponent method where you call the super's method and draw the oval:
#Override
protected void paintComponent(Graphics g) {
// this is needed to do house-keeping painting, to clear "dirty" pixels
super.paintComponent(g);
// this is needed to draw smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(OVAL_COLOR);
g2.fillOval(positionX, positionY, RADIUS, RADIUS);
}
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class MoveCircle extends JPanel {
private static final int DELTA = 5;
private DrawOval drawOval = new DrawOval();
public MoveCircle() {
JButton moveRightBtn = new JButton("Move Right");
moveRightBtn.addActionListener(e -> {
// get and update the x position
int x = drawOval.getPositionX();
x += DELTA;
// call the setter method
drawOval.setPositionX(x);
// request that Java repaint the JPanel
drawOval.repaint();
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(moveRightBtn);
setLayout(new BorderLayout());
add(drawOval);
add(buttonPanel, BorderLayout.LINE_START);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
MoveCircle mainPanel = new MoveCircle();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
#SuppressWarnings("serial")
class DrawOval extends JPanel {
private static final int RADIUS = 100;
private static final int PANEL_WIDTH = 600;
private static final int PANEL_HEIGHT = 450;
private static final Color OVAL_COLOR = Color.RED;
private int positionX = 0;
private int positionY = 0;
public DrawOval() {
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
}
public int getPositionX() {
return positionX;
}
public void setPositionX(int positionX) {
this.positionX = positionX;
}
public int getPositionY() {
return positionY;
}
public void setPositionY(int positionY) {
this.positionY = positionY;
}
#Override
protected void paintComponent(Graphics g) {
// this is needed to do house-keeping painting, to clear "dirty" pixels
super.paintComponent(g);
// this is needed to draw smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(OVAL_COLOR);
g2.fillOval(positionX, positionY, RADIUS, RADIUS);
}
}

Jcomponent paintcomponent not appearing on the panel

i am making a simple GUI in which small boxes should appear on the Jpanel according to their x,y coordinates. So i in my structure i have got three classes:
1: MyFrame which contains the main JFrame
2: MyPanel extends JPanel
3: Icon extends JComponent
In my MyFrame i want to have a MenuBar through which i can open a file of X,Y coordinates and below the menu bar i want to have the MyPanel which will have all the Icons for each X,Y coordinates. The first problem i have is that the Icon do not appear on MyPanel when i add the Icons.
My code can be seen below:
public class MyFrame {
private JFrame frame;
private MyPanel panel;
public MyFrame(){
panel = new MyPanel();
}
/*
*HERE GOES THE `FILE OPEN` LISTENER
* it creates `Coordinate` for each line
* passes the coordinate to panel.makeIcons()
*/
public void createGui(){
frame = new JFrame("Graph Editor");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(true);
//create, get and set the Jframe menu bar
//createMenuBar() returns a JMenuBar
frame.setJMenuBar(createMenuBar());
Container frame_pane = frame.getContentPane();
panel.setBounds(0, 0, frame.getWidth(), frame.getHeight());
frame_pane.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]){
MyFrame window = new MyFrame();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
window.createGui();
}
});
}
}
Code For the panel for holding icons:
public class MyPanel extends JPanel{
private Set<Icon> points;
public MyPanel(){
setLayout(null);
setPreferredSize(new Dimension(600, 600));
setBackground(Color.YELLOW);
}
//gets called by `FILE OPEN` listener for each coordinate
public void makeIcons(Coordinate obj){
Icon temp = new Icon(obj);
points.add(temp);
//add the temp JComponent to this JPanel
this.add(temp);
}
}
Code for Icon which needs to be shown on the above panel:
public Icon extends JComponent{
private Coordinate location;
public Icon(Coordinate obj){
location = obj;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(location.getX(), location.getY(), 20, 20);
g.setColor(Color.BLACK);
g.drawRect(location.getX(), location.getY(), 20, 20);
}
}
First Problem: The Icons do not show up in the panel with the above code.
Second Problem: When i change the makeIcon method in MyPanel.class to below. It shows the Icons By the MenuBar erases them when the MenuBar appears on any of the icons:
public void makeIcons(Coordinate obj){
Icon temp = new Icon(obj);
points.add(temp);
//add the temp JComponent to this JPanel
this.add(temp);
temp.paintComponent(this.getGraphics());
revalidate();
}
Any help is appreciated. Thanks
Don't call paintComponent (or any paint) method yourself, ever. This is not how painting works.
The core reasons why a component won't be painted are because:
it's size is 0x0
it's invisible
it's not added to a container that is (indirectly) added to a native peer.
Based on my brief observation, I would say you're suffering from point number 1, in part due to the use of a null layout
Also, remember, when painting a component, the component's Graphics context has already been translated so that 0x0 is the top/left corner of the component. This means, based on your code, you'd most likely be painting beyond the visible bounds of the component any way.
So, you basically have two choices. Implement your own layout manager, which uses the Coordinate information to place the Icons with in the container (which would then need to override getPreferredSize in order to provide sizing hints) or, paint it yourself
Which might look something like this...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Graph Editor");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.add(new MyPanel());
frame.pack();
frame.setVisible(true);
}
});
}
public class Coordinate {
private int x;
private int y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
public class Icon {
private Coordinate coordinate;
public Icon(Coordinate coordinate) {
this.coordinate = coordinate;
}
public Coordinate getCoordinate() {
return coordinate;
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, 20, 20);
g2d.setColor(Color.BLACK);
g2d.drawRect(0, 0, 20, 20);
}
}
public class MyPanel extends JPanel {
private Set<Icon> points;
public MyPanel() {
points = new HashSet<>();
setLayout(null);
setPreferredSize(new Dimension(600, 600));
setBackground(Color.YELLOW);
}
//gets called by `FILE OPEN` listener for each coordinate
public void makeIcons(Coordinate obj) {
points.add(new Icon(obj));
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Icon icon : points) {
Graphics2D g2d = (Graphics2D) g.create();
Coordinate coordinate = icon.getCoordinate();
// I'd have a size component associated with the Icon
// which I'd then use to offset the context by half its
// value, so the icon is paint around the center of the point
g2d.translate(coordinate.getX() - 10, coordinate.getY() - 10);
icon.paint(g2d);
g2d.dispose();
}
}
}
}
I've not seeded any values, I'm lazy, but that's the basic idea

Problems in mixing two JPanels in 1 JFrame

I was mixing two JPanels in one frame and it gives me this output!
Here's my code in which I add the two JPanels:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
import java.util.*;
public class Board extends JFrame{
private int width=500;
private int height=450;
Obstacles asd= new Obstacles();
Human human;
private Dimension mindim= new Dimension(width+10,height+10);
Board(){
human = new Human();
this.setTitle("Athwart");
//setLayout(null);
human.add(asd); //!!!we see here, I add asd (which is inherited from a JPanel)
// to another existing JPanel
this.setMinimumSize(mindim); //
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(human);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //
this.setLocationRelativeTo(null); //
this.setResizable(true); //
pack(); //
setVisible(true);
human.requestFocus(); //
}
}
This is how my Obstacles class looks like.
import javax.swing.*;
import java.awt.*;
public class Obstacles extends JPanel {
private int width=500;
private int height=450;
private Dimension mindim= new Dimension(width+10,height+10);
Obstacles()
{
this.setBackground(Color.white);
// this.addKeyListener(this);
// this.setFocusable(true);
// this.setRequestFocusEnabled(true);
setSize(mindim);
this.setVisible(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g); //
g.fillRect(0, 0, 60, 30);
g.setColor(Color.black);
g.draw3DRect(0, 0, 60, 30, true);
g.setColor(Color.black);
}
}
So as you can see the height of the component is 30 and the width is 60 but the Image above shows not even a half of it!
Is there anything I can do to make that two JPanels mixed together? By the way,
I tried using BoxLayout earlier but it didn't worked. Is there something wrong or it's just my IDE is not working properly? Cheers and thanks for an awesome reply. I'm just a starting gui programmer and I really don't know how to handle things. And yeah, if you would ask the complete code, I'll edit this if it would matter. :)
Finally, you put in a requirement:
I was just trying to put an image of a rectangle inside a jframe together with another Jpanel of an image of a circle. to see if how i would be that far in mixing two Jpanels together without overlapping.
Yes, this can be done, say by using a JLayeredPane, you could layer one JPanel over another, but you need to make sure that the upper JPanel is not opaque (setOpaque(false)).
But having said that, I still stand by my comment, that you look to be going about this wrong. You should not create a JPanel for drawing one thing and try to combine multiple JPanels because this can lead to an unholy mess. Instead you should consider creatomg one drawing JPanel, and give it logical objects, say non-GUI Obstacle objects, put them in a collection such as an ArrayList, and then in the drawing JPanel, iterate through all the Obstacles in the drawing JPanel's paintComponent method, drawing each Obstacle as it directs.
Edit
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class ObstacleDrawer extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private List<Obstacle> obstacleList = new ArrayList<>();
public ObstacleDrawer() {
}
public void addObstacle(Obstacle obstacle) {
obstacleList.add(obstacle);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// smooth out the drawing
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the obstacle list, drawing each obstacle
for (Obstacle obstacle : obstacleList) {
obstacle.draw(g2);
}
}
private static void createAndShowGui() {
ObstacleDrawer mainPanel = new ObstacleDrawer();
mainPanel.addObstacle(new CircleObstacle(new Point(200, 200), 100, Color.red));
mainPanel.addObstacle(new CircleObstacle(new Point(400, 300), 150, Color.blue));
JFrame frame = new JFrame("ObstacleDrawer");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
interface Obstacle {
public Point getCenter();
public void setCenter(Point center);
public int getWidth();
public void setWidth(int width);
public Color getColor();
public void setColor(Color color);
public void draw(Graphics2D g2);
}
class CircleObstacle implements Obstacle {
private Point center;
private int width;
private Color color;
public CircleObstacle(Point center, int width, Color color) {
this.center = center;
this.width = width;
this.color = color;
}
#Override
public Point getCenter() {
return center;
}
#Override
public void setCenter(Point center) {
this.center = center;
}
#Override
public int getWidth() {
return width;
}
#Override
public void setWidth(int width) {
this.width = width;
}
#Override
public Color getColor() {
return color;
}
#Override
public void setColor(Color color) {
this.color = color;
}
#Override
public void draw(Graphics2D g2) {
Color oldColor = g2.getColor();
g2.setColor(color);
int x = center.x - width / 2;
int y = center.y - width / 2;
int height = width;
g2.fillOval(x, y, width, height);
g2.setColor(oldColor);
}
}
When you want to mix JPanels, the best way to do it is to nest them inside another JPanel. For nesting, the best layouts (in my experience) are BoxLayout and GridLayout. The example below tries to replicate the drawing in your JFrame.
JPanel outsidePanel = new JPanel();
Obstacle obstacle1 = new Obstacle();
Human human1 = new Human();
outsidePanel.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
outsidePanel.add(human1);
outsidePanel.add(obstacle1);
Furthermore, I recommend you to actually use BorderLayout in your JFrame, and then add this Panel to the CENTER location of the BorderLayout. Following your example:
this.setLayout(new BorderLayout());
this.add(outsidePanel(), BorderLayout.CENTER);
Usually this will give you the best results. More about nesting panels in the BoxLayout documentation (http://docs.oracle.com/javase/7/docs/api/javax/swing/BoxLayout.html). Hope this helps.

How to add MouseListener to item on Java Swing Canvas

I'd like to make a Java panel that creates objects where the user clicks. Since my actual application uses a MVC approach I'd like also for these objects to be able to repaint themselves when a model is changed, and provide menus to change their properties.
I think that the best way to control their x and y locations would be to take a canvas based approach whereby the JPanel calls a draw method on these objects from the paintComponent method. This however will only draw the shape on the canvas and does not add the object itself loosing all abilities to control object properties. I'd be very grateful if someone could tell me the best approach for what I want to do.
I've created some sample code which can be seen below. When clicked I'd like the circle to change colour, which is implemented using a MouseListener (it basically represents changing the models properties in this small example). Also I'd just like to make sure that zooming in/out still works with any sample code/advice can provide so I've added buttons to zoom the objects in and out as a quick test.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.geom.Ellipse2D;
public class Main {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ExamplePanel panel = new ExamplePanel();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
});
}
//I could not get this to with when it extended JLayeredPane
private static class ExamplePanel extends JPanel {
private static final int maxX = 500;
private static final int maxY = 500;
private static double zoom = 1;
private static final Circle circle = new Circle(100, 100);
public ExamplePanel() {
this.setPreferredSize(new Dimension(maxX, maxY));
this.setFocusable(true);
Button zoomIn = new Button("Zoom In");
zoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
zoom += 0.1;
repaint();
}
});
add(zoomIn);
Button zoomOut = new Button("Zoom Out");
zoomOut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
zoom -= 0.1;
repaint();
}
});
add(zoomOut);
// add(circle); // Comment back in if using JLayeredPane
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.scale(zoom, zoom);
super.paintComponent(g);
circle.paint(g); // Comment out if using JLayeredPane
}
}
static class Circle extends JPanel {
private Color color = Color.RED;
private final int x;
private final int y;
private static final int DIMENSION = 100;
public Circle(int x, int y) {
// setBounds(x, y, DIMENSION, DIMENSION);
this.x = x;
this.y = y;
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
color = Color.BLUE;
}
#Override
public void mouseReleased(MouseEvent e) {
}
});
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(color);
g2.fillOval(x, y, DIMENSION, DIMENSION);
}
// I had some trouble getting this to work with JLayeredPane even when setting the bounds
// In the constructor
// #Override
// public void paintComponent(Graphics g) {
// Graphics2D g2 = (Graphics2D) g;
// g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// g2.setPaint(color);
// g2.fillOval(x, y, DIMENSION, DIMENSION);
// }
#Override
public Dimension getPreferredSize(){
return new Dimension(DIMENSION, DIMENSION);
}
}
}
As an aside I did try using a JLayeredPane(useful because I'd also like to layer my objects) but could not get my objects to even render. I know it has no default layout manager so tried calling setBounds in the circle in the constructor, but sadly it did not work. I know it's better to use a layout manager but can't seem to find one suitable for my needs!
Thanks in advance.
Don't override paint components, use paintComponent and don't forget to call super.paintComponent
A component already has a concept of "location", so when painting, the top left position of your component is actually 0x0
What you are doing is actually painting beyond the boundaries of you component
For example, if you place your Circle at 100x100 and then did...
g2.fillOval(x, y, DIMENSION, DIMENSION);
You would actually start painting at 200x200 (100 for the actual location of the component and 100 for you additional positioning).
Instead use
g2.fillOval(x, y, DIMENSION, DIMENSION);
And go back and try using JLayeredPane.
You could actually write your own layout manager that takes the location of the component and it's preferred size and updates the components bounds and then apply this to a JLayeredPane. This gives you the "benefits" of an absolute layout, but keeps you within how Swing works to update its components when things change.
You should also be careful with doing anything like...
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
The Graphics context is a shared resource. That means, anything you apply to, will still be in effect when the next component is painted. This may produce some strange results.
Instead try using...
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//...
g2.dispose();
Updated
For zooming I would take a closer look at JXLayer (or JLayer in Java 7)
The JXLayer (and excellent PBar extensions) have gone quite on the net, so you can grab a copy from here
(I tried finding a better example, but this is the best I could do with the limited time I have available)
Updated with working zooming example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;
public class TestJLayerZoom {
public static void main(String[] args) {
new TestJLayerZoom();
}
public TestJLayerZoom() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JXLayer<JComponent> layer;
private DefaultTransformModel transformModel;
private JPanel content;
public TestPane() {
content = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
JLabel label = new JLabel("Hello");
JTextField field = new JTextField("World", 20);
content.add(label, gbc);
content.add(field, gbc);
gbc.gridy++;
gbc.gridwidth = GridBagConstraints.REMAINDER;
final JSlider slider = new JSlider(50, 200);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int value = slider.getValue();
double scale = value / 100d;
transformModel.setScale(scale);
}
});
content.add(slider, gbc);
transformModel = new DefaultTransformModel();
transformModel.setScaleToPreferredSize(true);
Map<RenderingHints.Key, Object> hints = new HashMap<>();
//hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
//hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
layer = TransformUtils.createTransformJXLayer(content, transformModel, hints);
setLayout(new BorderLayout());
add(layer);
}
}
}
I've left the rendering hints in to demonstrate their use, but I found that they screwed with the positing of the cursor within the text field, but you might like to have a play
I'd just like to add that I fixed the zooming issue not in the way suggested by the answer, but just by keeping the line that applied a scaled transform call in the ExamplePanel paintComponent method:
g2.scale(zoom, zoom);
I thought that this was the nicest implementation since none of the components require any knowledge about zooming and it seemed far simpler than JLayer since I only required basic zooming functionalities.

Categories

Resources