I have 2 Java classes, Game and BaseComponent.
I wanted to draw multiple components in my JFrame, but that doesn't work, since it will only display the last added component.
I think the solution is to add an JPanel, but that still doesn't quite work for me, since no object are being drawn, not even one.
Game:
public class Game
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(300, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel jpanel = new JPanel();
JComponent component = new BaseComponent(0,0);
JComponent component2 = new BaseComponent(1,1);
jpanel.add(component);
jpanel.add(component2);
frame.add(jpanel);
frame.setVisible(true);
}
}
BaseComponent:
public class BaseComponent extends JComponent
{
private int x, y;
BaseComponent(int i, int y) {
this.x = i;
this.y = y;
}
#Override
public Dimension getPreferredSize() {
return new Dimension((x * 50) + 50, (y * 50) + 50);
}
#Override
protected void paintComponent(Graphics g)
{
drawBaseComponent(g, x, y);
}
void drawBaseComponent(Graphics g, int x, int y)
{
g.setColor(Color.GREEN);
g.fillRect(x*50, y*50, 50, 50);
}
}
As you can see, this code adds one component to the the panel, which is being added to the JFrame, but it's completely empty;
EDIT:
When using prefered size, the application draws two green boxes but the position is not right, I expected them in the place of the two red boxes.
JPanel uses a FlowLayout by default. This will attempt to size any components to the preferredSize, which is, by default 0x0, which is why, your component "appears" empty.
You will need to supply sizing hints, via getPreferredSize, in order to allow the layout management API to determine the best way to layout your component, for example
public class BaseComponent extends JComponent
{
private int x, y;
BaseComponent(int i, int i0) {
this.x = i;
this.y = i0;
}
#Override
public Dimension getPreferredSize() {
return new Dimension((x * 50) + 50, (y * 50) + 50);
}
#Override
protected void paintComponent(Graphics g)
{
drawBaseComponent(g, x, y);
}
void drawBaseComponent(Graphics g, int xLeft, int yTop)
{
g.setColor(Color.GREEN);
g.fillRect(x*50, y*50, 50, 50);
}
}
Related
I'm trying to draw a rectangle to the screen based on mouse listeners. I'm having a few problems:
To start with the shape, it has an offset by a few pixels relative to the
release location of the mouse.
Secondly I can get only the shape to display if I set the screen to
visible each time after adding a JPanel component.
Could anyone tell me what I'm doing wrong or point me in the direction of some documentation? (I’ve read the Oracle documentation but I didn’t really understand it)
I know this isnt the greatest code but i just want to get it working for the moment.
This is my first time asking a question on stackoverflow so if I'm doing something wrong pls tell me.
This is my code
public class Frame {
private JFrame frame;
public Frame() {
this.frame = frameSetup("Graph");
this.frame.addMouseListener(new Mouse());
}
private JFrame frameSetup(String heading) {
JFrame f = new JFrame(heading);
f.setBackground(Color.WHITE);
f.setSize(800, 800);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setVisible(true);
return f;
}
public void draw(int xIn, int yIn, int widthIn, int heightIn) {
int x = xIn;
int y = yIn;
int width = widthIn;
int height = heightIn;
this.frame.add(new Panel(x, y, width, height));
this.frame.pack();
//this.frame.setVisible(true);
}
private class Mouse implements MouseListener {
int x = 0;
int y = 0;
int height = 0;
int width = 0;
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == 1) {
x = e.getX();
y = e.getY();
System.out.println(x + ": " + y);
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == 1) {
width = (e.getX() - x);
height = (e.getY() - y);
System.out.println(width + ": " + height);
draw(x - 7, y -30, width, height);
}
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
}
and
public class Panel extends JPanel {
private int x;
private int y;
private int width;
private int height;
public Panel(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(x, y, width, height);
}
}
Welcome to the Site!
The offset is something I've had plenty of trouble with in the past aswell. It's caused by adding the MouseListener to the JFrame rather than it's contentPane. Basically, the JFrame is the thin border and the bar with the name, the icons and the buttons at the top. The content pane sits inside that, here it's the white surface. When you add the MouseListener to the JFrame, the coords are relative to this outline (the top left corner of the frame), rather than the white panel. This is fixed as follows:
// in the Frame constructor
this.frame.getContentPane().addMouseListener(new Mouse());
// in Mouse.mouseReleased(), remove the offset you added.
draw(x, y, width, height);
The Panel problem is a different story. I don't know how the code is doing what it does or how it works at all in the first place, but I can still give you some general tips.
Don't pack() the frame in the drawing method. It's used to calculate the window size while respecting the layout and the content of it. For me, it made the window as small as the OS allowed it to be.
Don't use JPanels like that! You'll run into weird problems like this, since they're made to be used in GUIs. For this reason, they're also way to heavy-weight to be used as a graphic. #user16320675 commented a tutorial on custom painting, I suggest you check that out.
Lastly, I suggest reworking the rectangle drawing. Right now, it only works when dragging from the top left to the bottom right.
I'm trying to make the game snake and have run into an issue. What I have is a class called Segment which is used to create the objects that hold the x- and y-positions and also the direction of the snake. The class extends JPanel and overrides the method paintComponent(). I then add an object of type Segment to a JFrame in a different class. The methods I have for moving/changing directions of the snake (actually just a square at the moment) work perfectly but my problem is this:
When the square gets to about half of the width of the frame or half of the height of the frame it stops being drawn. I have made the background of the JPanel light grey so I know that the square hasn't reached the end of the JPanel when it stops. Below is my simple paintComponent() method and the section in the class that extends JFrame where I add the object.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.LIGHT_GRAY);
g.setColor(Color.black);
g.fillRect(xpos, ypos, width, height);
}
public Snake() {
setLayout(new BorderLayout());
addKeyListener(l);
segment = new Segment(100, 100, Segment.Dir.RIGHT);
segment.setPreferredSize(new Dimension(500,500));
add(segment, BorderLayout.CENTER);
timer.start();
setVisible(true);
pack();
setTitle("Snake");
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
I also know that the objects position doesn't stop being updated so it's just the drawing of the square in the entire panel/frame that's the issue. Appreciate any help!
Here's an MCVE:
import java.awt.*;
import javax.swing.*;
public class Segment extends JPanel {
private int width = 10;
private int height = 10;
private int xpos, ypos;
private Dir dir;
public enum Dir {
LEFT, RIGHT, UP, DOWN;
}
public Segment() {
xpos = 0;
ypos = 0;
dir = Dir.RIGHT;
}
public Segment(int x, int y, Dir d) {
xpos = x;
ypos = y;
dir = d;
}
public Dir getDir() {
return dir;
}
public void setDir(Dir d) {
dir = d;
}
public void setX(int x) {
xpos = x;
repaint();
}
public void setY(int y) {
ypos = y;
repaint();
}
public int getX() {
return xpos;
}
public int getY() {
return ypos;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.LIGHT_GRAY);
g.setColor(Color.black);
g.fillRect(xpos, ypos, width, height);
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Snake extends JFrame implements ActionListener {
Segment segment;
Timer timer = new Timer(50, this);
public Snake() {
setLayout(new BorderLayout());
setSize(500,500);
segment = new Segment(100, 100, Segment.Dir.RIGHT);
segment.setPreferredSize(new Dimension(getWidth(),getHeight()));
add(segment, BorderLayout.CENTER);
timer.start();
setVisible(true);
setTitle("Snake");
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == timer) {
segment.setX((segment.getX() + 4 + getWidth())%getWidth());
}
}
public static void main(String[] arg) {
new Snake();
}
}
Don't override getX() and getY() of the Segment class.
Those are methods of all Swing components. They get the current location of the component within the parent container.
Use different method names to control the location of the painting of your snake. Since your variable names are xPos and yPos maybe use getXPos() and getYPos().
So what happens is that the snake is drawn at xPos/yPos relative to the Segment panel and the Segment panel is also drawn at xPos/yPos relative to its parent container.
I want to paint a circle at the middle of JButton. Here is what I tried:
JButton jButton = new JButton(new CircleIcon());
public class CircleIcon implements Icon{
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.drawOval(10, 10, 20, 20);
}
#Override
public int getIconWidth() {
return 10;
}
#Override
public int getIconHeight() {
return 10;
}
}
I got this:
But I need something like this:
My question is what is the quare in the middle of the button on the first picture? And how to make it as in the second one?
The Swing tutorial on How to Use Icons should help: Creating a Custom Icon Implementation
import java.awt.*;
import javax.swing.*;
public class CircleIconTest {
public JComponent makeUI() {
JPanel p = new JPanel();
p.add(new JButton(new CircleIcon()));
return p;
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new CircleIconTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class CircleIcon implements Icon {
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
//g.drawOval(10, 10, 20, 20);
Graphics2D g2 = (Graphics2D) g.create();
//Draw the icon at the specified x, y location:
g2.drawOval(x, y, getIconWidth() - 1, getIconHeight() - 1);
//or
//g2.translate(x, y);
//g2.drawOval(0, 0, getIconWidth() - 1, getIconHeight() - 1);
g2.dispose();
}
#Override
public int getIconWidth() {
return 20;
}
#Override
public int getIconHeight() {
return 20;
}
}
what is the quare in the middle of the button on the first picture?
You have probably painted a rectangle over your codes. You should just look for drawRectangle( code line on your code block.
how to make it as in the second one?
There are 2 possible solution for it.
1 - You can set some size for the button. Because it seems need to get bigger to be seen like the latter picture. Try this
jButton.setPreferredSize(new Dimension(40, 40));
2 - You are using static values to draw a circle. I would use dynamic values for it. just like this.
JButton JButton = new JButton() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int nGap = 10;
int nXPosition = nGap;
int nYPosition = nGap;
int nWidth = getWidth() - nGap * 2;
int nHeight = getHeight() - nGap * 2;
g.setColor(Color.RED);
g.drawOval(nXPosition, nYPosition, nWidth, nHeight);
g.fillOval(nXPosition, nYPosition, nWidth, nHeight);
}
};
JButton.setHorizontalAlignment(JLabel.CENTER);
JButton.setVerticalAlignment(JLabel.CENTER);
This is the button display at different sizes.
jButton.setFocusPainted(false); // This will prevent the square highlight on focus!
I have created a label with overriding its paintComponent (to paint it in a different format with an arc) and located them on a panel. Everything worked fine, however the labels which are located in same location (by .setlocation) are creating problems.
Lets say I have 3 labels at the same location with different shapes. A-B-C
A is first created, then B is created and lastly c is created.
When I click on A, my function paints A and displays "youve clicked A"
But when I click on B or C, it acts like i have clicked on A.
(I'm adding labels in a panel stacked in scroll pane )
Here is the code where i make labels
for(int i=0;i<rowcount;i++){
arcstart = Rawazimuth(azimuth[i]);
//custom JLabel to paint it like an arc
MyLabel FLabel = new MyLabel(100, 50, (int)arcstart,cellid[i],lon[i],lat[i]);
FLabel.setOpaque(true);
Dimension LabelSize = new Dimension( 100,50);
FLabel.setSize(LabelSize);
//locate the JLabel to labels location. it might be same or not
FLabel.setLocation(lon[i], lat[i]);
MyPanel.add(FLabel);
}
this is my custom jlabel class
public MyLabel(int W, int H, int start,int outcellid,int lonn, int latt) {
addMouseListener(this);
arcsizeW = W;
arcsizeH = H;
arcstart = start;
cellid = outcellid;
clicked = false;
lon = lonn;
lat = latt;
}
#Override
public void paintComponent(Graphics g){
// true if button is clicked, so paint acordingly
if(clicked ==true){
g.setColor(dummyg.getColor());
}else{
g.setColor(Color.blue);
}
// draw the arc
g.fillArc(0, 0,arcsizeW , arcsizeH, arcstart, 60);
}
//if cell is clicked, change its color to red and print its cellid
#Override
public void mouseClicked(MouseEvent e) {
System.out.println(cellid);
dummyg = this.getGraphics();
dummyg.setColor(Color.red);
this.paintComponent(dummyg);
clicked = true;
}// other listener stuff}
so how do i prevent this?
i think i can use jlayerpane, but i will need at least 4 layers ( dunno if its ok ).
Make sure you are calling super.paintComponent. This is important to ensure that the component is painting correcting.
Mouse events are like rain. They will hit the first component registered to receive mouse events and stop (like rain hitting an umbrella).
If you have components that overlap, where they overlap, the mouse events go to the first component registered to handle them (in this case A).
Here's a quick example to show what I mean...
As you hover over each circle, it will highlight, try hovering over the middle...
public class OverlappingMouseTest {
public static void main(String[] args) {
new OverlappingMouseTest();
}
public OverlappingMouseTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BackgroundPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected class BackgroundPane extends JLayeredPane {
private CirclePane redPane;
private CirclePane greenPane;
private CirclePane bluePane;
public BackgroundPane() {
redPane = new CirclePane(Color.RED);
greenPane = new CirclePane(Color.GREEN);
bluePane = new CirclePane(Color.BLUE);
add(redPane);
add(greenPane);
add(bluePane);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void doLayout() {
Dimension size = new Dimension(100, 100);
int width = getWidth() - 1;
int height = getHeight() - 1;
Point p = new Point();
p.x = (width / 2) - 50;
p.y = (height / 2) - 75;
redPane.setBounds(new Rectangle(p, size));
p.x = (width / 2) - 75;
p.y = (height / 2) - 25;
bluePane.setBounds(new Rectangle(p, size));
p.x = (width / 2) - 25;
p.y = (height / 2) - 25;
greenPane.setBounds(new Rectangle(p, size));
}
}
protected class CirclePane extends JPanel {
private boolean mouseHover = false;
public CirclePane(Color background) {
setOpaque(false);
setBackground(background);
addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent me) {
mouseHover = false;
repaint();
}
#Override
public void mouseEntered(MouseEvent me) {
mouseHover = true;
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs.create();
if (!mouseHover) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
}
g2d.setColor(getBackground());
g2d.fill(new Ellipse2D.Float(0, 0, getWidth() - 1, getHeight() - 1));
g2d.dispose();
}
}
}
Everything worked fine, however the JLabel's which are located in same
location ( by .setlocation ) are creating problems.
Lets say i have 3 Jlabels at the same location with different shapes.
A-B-C A is first created, then B is created and lastly c is created.
there are tree ways, to use
JLayer (Java7), based on JXLayer (Java6)
OverlayLayout
JLayeredPane
I have solved my problem.
The real problem that i found was, Java is interpreting each component with their x,y location and width & height. So even if your label does not overlap ( graphically ) they can be interpreted as same component.
To solve my problem,
I found the angle between mouse click point and component center; then calculated the closest component and reached it.
Layers and such doesn't work in this situation, the only other way around is possibly overriding the contains method for the actualy shape.
I have a set of JLabels, each containing a letter (via seText()), opaque and background set to white, on a JPanel with a GridLayout so the labels are forming a table.
I am doing a simple animation of highlighting certain rows and columns then there intersection. I can use the setBackground() of labels for this purpose, but thought I'd have more "choices" if a was able to use a Graphics object (maybe drawing a circle around intersection, then clearing it).
I tried to extend JLabel, or drawing on the JPanel directly(using getGraphics() in a method) but it didn't work, I think the drawing is behind the labels in this case. I can't figure out where should the "painting" code be placed in either case, nothing appeared on the screen.
in short, a method like the following, can be used to draw on top of labels?
should it be a JLabel or a JPanel method?
public void drawsomething() {
Graphics2D g2d = (Graphics2D) getGraphics();
g2d.fillRect(100, 100, 100, 100);
}
What if you override paintChildren() ?
protected void paintChildren(Graphics g) {
super.paintChildren(g);
//paint your lines here
}
You might want to try a JLayeredPane to paint your specific drawings on top of the existing JComponents
see example here http://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html
I really don't know much about drawing stuff yet, but just created one small sample code for you to look at, hopefully you can get some information out of it. In order to paint on the JLabel you can use it's paintComponent(Graphics g) method.
A Sample Code :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DrawingOnJLabel extends JFrame
{
private CustomLabel label;
private int flag = 1;
private JPanel contentPane;
public DrawingOnJLabel()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
contentPane = new JPanel();
contentPane.setBackground(Color.WHITE);
label = new CustomLabel(200, 200);
label.setLabelText("A");
label.setValues(50, 50, 100, 100, 240, 60);
final JButton button = new JButton("CLEAR");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
if (flag == 1)
{
label.setFlag(flag);
flag = 0;
button.setText("REPAINT");
contentPane.revalidate();
contentPane.repaint();
}
else if (flag == 0)
{
label.setFlag(flag);
flag = 1;
button.setText("CLEAR");
contentPane.revalidate();
contentPane.repaint();
}
}
});
}
});
contentPane.add(label);
add(contentPane, BorderLayout.CENTER);
add(button, BorderLayout.PAGE_END);
setSize(300, 300);
setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new DrawingOnJLabel();
}
});
}
}
class CustomLabel extends JLabel
{
private int sizeX;
private int sizeY;
private int x, y, width, height, startAngle, arcAngle;
private int flag = 0;
private String text;
public CustomLabel(int sX, int sY)
{
sizeX = sX;
sizeY = sY;
}
// Simply call this or any set method to paint on JLabel.
public void setValues(int x, int y, int width, int height, int startAngle, int arcAngle)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.startAngle = startAngle;
this.arcAngle = arcAngle;
repaint();
}
public void setFlag(int value)
{
flag = value;
repaint();
}
public Dimension getPreferredSize()
{
return (new Dimension(sizeX, sizeY));
}
public void setLabelText(String text)
{
super.setText(text);
this.text = text;
flag = 0;
repaint();
}
public void paintComponent(Graphics g)
{
if (flag == 0)
{
g.setColor(Color.RED);
g.drawString(text, 20, 20);
g.setColor(Color.BLUE);
g.drawOval(x, y, width, height);
g.fillOval(x + 20, y + 20, 15, 15);
g.fillOval(x + 65, y + 20, 15, 15);
g.fillRect(x + 40, y + 40, 5, 20);
g.drawArc(x + 20, y + 30, 55, 55, startAngle, arcAngle);
}
else if (flag == 1)
{
g.clearRect(x, y, width, height);
}
}
}
Use paintComponent(Graphics g) instead of paint(Graphics g). That will paint over the GUI