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
Related
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;
}
}
}
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);
}
}
I have a JFrame, comprised of three JPanel containers, each panel has a filled-in circle (for example, red, white blue).
What I'd like to be able to do is update the color of the filled-in circle of that panel (make it darker with Color.RED.darker, for example) when that particular panel is clicked
I can't use an ActionListener, since panels aren't components but containers. I started out using MouseListener, but have now updated this to MouseAdapter. I am able to determine which JPanel was clicked - for testing purposes I'm printing out which panel was clicked to the console (for simplicity purposes I added a name to each panel).
EDIT: I got this mostly working - I can now repaint() the Jpanel that was clicked, making that cricle color darker, using the suggestion below of creating a setCircleColor (color) method, which calls repaint().This redraws the circle in that panel, using a darker color.
However, what I really also need to do is make the other two (non-clicked) cirlces on the other panels to repaint() with lighter colors.
But I can't see an easy way to handle this - how can I manipulate the other Jpanels that I didn't click on?
Here's my code:
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class TrafficLight {
// sets up the frame, calls the circle panels and adds them to the frame
public static void setUpGui() {
JFrame frame = new JFrame("Traffic Lights");
frame.setSize(300, 900);
frame.setLayout(new GridLayout(3, 0));
DrawCirclePanel redCircle = new DrawCirclePanel(Color.RED);
DrawCirclePanel yellowCircle = new DrawCirclePanel(Color.YELLOW);
DrawCirclePanel greenCircle = new DrawCirclePanel(Color.GREEN);
redCircle.setName("redCircle");
yellowCircle.setName("yellowCircle");
greenCircle.setName("greenCircle");
CircleListener cl = new CircleListener();
redCircle.addMouseListener(cl);
yellowCircle.addMouseListener(cl);
greenCircle.addMouseListener(cl);
frame.add(redCircle);
frame.add(yellowCircle);
frame.add(greenCircle);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
}
public static void main(String[] args) {
setUpGui();
}
}
// the DrawCirclePanel class creates a panel and
// draws a filled-in circle on the panel
class DrawCirclePanel extends JPanel {
Color c;
Border blackline = BorderFactory.createLineBorder(Color.black);
// constructor for panel, takes Color as argument
// so we know what color circle to make
public DrawCirclePanel(Color color) {
this.c = color;
this.setLayout(new GridLayout(1, 0));
this.setBorder(blackline);
}
// draws the circle in the panel
public void paintComponent(Graphics g) {
super.paintComponent(g);
int xWidth = this.getParent().getWidth();
g.setColor(c);
g.fillOval(1, 1, xWidth-1, xWidth-1);
}
public void setCircleColor(Color color) {
this.c = color;
this.getGraphics().setColor(c);
this.repaint();
}
}
class CircleListener extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
DrawCirclePanel cPanel = (DrawCirclePanel) e.getSource();
System.out.println(cPanel);
String name = cPanel.getName();
if (name == "redCircle") {
cPanel.setCircleColor(Color.red.darker());
}
else if (name == "yellowCircle") {
cPanel.setCircleColor(Color.yellow.darker());
}
else {
cPanel.setCircleColor(Color.green.darker());
}
}
}
So the answer turned out to be fairly simple.
Declare the panels with the circles as static variables so that the MouseListener class can access all three of the panels.
As Andrew Thompson noted, set up a setCircleColor method in DrawCirclePanel that repaints the circle with a different color as needed.
In the MouseListener class, determine with panel was called, then repaint all three of the circles, passing in the correct color - darker() or brighter() - as needed.
Here's the final code
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.Color;
import java.awt.event.*;
import java.awt.event.MouseEvent;
public class TrafficLight {
static DrawCirclePanel redCircle;
static DrawCirclePanel yellowCircle;
static DrawCirclePanel greenCircle;
// sets up the frame, calls the circle panels and adds them to the frame
public static void setUpGui() {
JFrame frame = new JFrame("Traffic Lights");
frame.setSize(300, 900);
frame.setLayout(new GridLayout(3, 0));
redCircle = new DrawCirclePanel(Color.RED.brighter().brighter());
yellowCircle = new DrawCirclePanel(Color.YELLOW.darker().darker());
greenCircle = new DrawCirclePanel(Color.GREEN.darker().darker());
redCircle.setName("redCircle");
yellowCircle.setName("yellowCircle");
greenCircle.setName("greenCircle");
CircleListener cl = new CircleListener();
redCircle.addMouseListener(cl);
yellowCircle.addMouseListener(cl);
greenCircle.addMouseListener(cl);
frame.add(redCircle);
frame.add(yellowCircle);
frame.add(greenCircle);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
}
public static void main(String[] args) {
setUpGui();
}
}
// the DrawCirclePanel class creates a panel and
// draws a filled-in circle on the panel
class DrawCirclePanel extends JPanel {
Color c;
Border blackLine = BorderFactory.createLineBorder(Color.BLACK);
// constructor for panel, takes Color as argument
// so we know what color circle to make
public DrawCirclePanel(Color color) {
this.c = color;
this.setLayout(new GridLayout(1, 0));
this.setBorder(blackLine);
}
// draws the circle in the panel
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int xWidth = this.getParent().getWidth();
g.setColor(c);
g.fillOval(0, 0, xWidth, xWidth);
}
// changes the color and calls repaint after a mouseClicked event
public void setCircleColor(Color color) {
this.c = color;
this.getGraphics().setColor(c);
this.repaint();
}
}
//abstract adapter class for receiving mouse events
class CircleListener extends MouseAdapter {
// determine which panel was clicked; redraws as brighter circle
// redraws other circles with dimmer (darker) color
public void mouseClicked(MouseEvent e) {
DrawCirclePanel cPanel = (DrawCirclePanel) e.getSource();
String name = cPanel.getName();
if (name.equals("redCircle")) {
TrafficLight.redCircle.setCircleColor(Color.RED.brighter().brighter());
TrafficLight.yellowCircle.setCircleColor(Color.YELLOW.darker().darker());
TrafficLight.greenCircle.setCircleColor(Color.GREEN.darker().darker());
} else if (name.equals("yellowCircle")) {
TrafficLight.redCircle.setCircleColor(Color.RED.darker().darker());
TrafficLight.yellowCircle.setCircleColor(Color.YELLOW.brighter().brighter());
TrafficLight.greenCircle.setCircleColor(Color.GREEN.darker().darker());
} else {
TrafficLight.redCircle.setCircleColor(Color.RED.darker().darker());
TrafficLight.yellowCircle.setCircleColor(Color.YELLOW.darker().darker());
TrafficLight.greenCircle.setCircleColor(Color.GREEN.brighter().brighter());
}
}
}
I am attempting to have a circle appear on the screen and follow the mouse around. (Eventually I'm going to turn it into a game with ray casting) I am using a MouseMotionListener and am trying to use the mouseMoved method to get accurate mouse location within my JPanel. The problem is though that the farther I move my mouse down the screen, the less accurate it becomes. By the time my mouse gets to the bottom, it is drawing circles about 20 pixels above. It is not a lagging thing because it never catches up, it is always a few pixels above where it should be.
I've tried using different methods that call from MouseEvents and have tried using MousePointerInfo but none work correctly. It does seem to work when I have the JFrame set to undecorated, but obviously that does not look good for a program and thus I want to avoid that.
public class Driver {
public static void main(String[] args) {
JFrame frame = new JFrame("Moonlight");
frame.setSize(700, 700);
frame.setLocation(350, 50);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new MoonlightPanel());
frame.setVisible(true);
}
}
public class Panel extends JPanel {
private BufferedImage myImage;
private Graphics myBuffer;
private Timer t;
public Panel () {
myImage = new BufferedImage(700, 700, BufferedImage.TYPE_INT_RGB);
myBuffer = myImage.getGraphics();
t = new Timer(0, new Listener());
t.start();
addMouseMotionListener(new Mouse());
}
private class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
drawBackground();
/*try {
Point pos = getMousePosition();
myBuffer.setColor(Color.WHITE);
myBuffer.fillOval(pos.x - 10, pos.y - 10, 20, 20);
}
catch(NullPointerException en) {}*/
repaint();
}
}
private class Mouse implements MouseMotionListener {
public void mouseMoved(MouseEvent e) {
Point pos = new Point(e.getX(), e.getY());
System.out.println(pos);
myBuffer.setColor(Color.BLUE);
myBuffer.fillOval(pos.x - 10, pos.y - 10, 20, 20);
}
public void mouseDragged(MouseEvent e) {}
}
public void drawBackground() {
setBackground(Color.BLACK);
}
public void paintComponent(Graphics g) {
g.drawImage(myImage, 0, 0, getWidth(), getHeight(), null);
}
}
Your code is much more complex than it needs to be. The Panel class members are unnecessary. All you need to do is save the mouse location in the mouseMoved() method - in a class member variable - and reference it in the paintComponent() method to draw your blue circle. The below code is a stripped-down version which displays a blue circle that follows the mouse pointer around on the screen.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MoonLite extends JPanel implements MouseMotionListener {
private Point pt;
public MoonLite() {
setBackground(Color.BLACK);
setForeground(Color.BLUE);
addMouseMotionListener(this);
}
public void mouseMoved(MouseEvent e) {
pt = e.getPoint();
repaint();
}
public void mouseDragged(MouseEvent e) {
// Do nothing.
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (pt != null) {
g.fillOval(pt.x - 10, pt.y - 10, 20, 20);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Moonlight");
frame.setSize(700, 700);
frame.setLocation(350, 50);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new MoonLite());
frame.setVisible(true);
}
});
}
}
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.