I'm writing an application that has a "Drawing area" where the user is to place components. The drawing area contains a grid that the components will snap to, but and it works fine when I resize the window and such
; but when I pan the area, the grid does not redraw itself.
How would I go about drawing the new area and repainting? I create the grid when I override paintComponent(...) by looping through the x and y space in the window and using g.drawLine(...) every 10 units. Based on this example, I pan using a MouseMotionListener in the constructor for my Drawing class which extends JPanel.
public final class Drawing extends JPanel {
private int spacing;
private Point origin = new Point(0,0);
private Point mousePt;
/**
* Default Constructor for Drawing Object. Calls JPanel default constructor.
*/
public Drawing() {
super();
spacing = 10;
setBackground(new Color(255, 255, 255));
setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
GroupLayout workspacePanelLayout = new GroupLayout(this);
setLayout(workspacePanelLayout);
workspacePanelLayout.setHorizontalGroup(workspacePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addGap(0, 343, Short.MAX_VALUE));
workspacePanelLayout.setVerticalGroup(workspacePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addGap(0, 400, Short.MAX_VALUE));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent evt) {
// this stuff is mainly for debugging....
mousePt = evt.getPoint();
System.out.println((mousePt.x - origin.x) + "," + (mousePt.y - origin.
System.out.println(origin.x + ", " + origin.y);
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
//this is what is more important.
#Override
public void mouseDragged(MouseEvent evt) {
if (evt.getButton() == MouseEvent.BUTTON2 || xorlogic.Window.cursorState == 1) {
int dx = evt.getX() - mousePt.x;
int dy = evt.getY() - mousePt.y;
origin.setLocation(origin.x+dx, origin.y+dy);
mousePt = evt.getPoint();
repaint();
}
}
});
}
and the paintComponent(...) which is implemented later:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(0, origin.y, getWidth(), origin.y);
g.drawLine(origin.x, 0, origin.x, getHeight());
// set up grid
int x = 0;
int y = 0;
g.setColor(new Color(220, 220, 220));
while (x < getWidth()) {
g.drawLine(origin.x + x, 0, origin.x + x, getHeight());
x += getSpacing();
}
while (y < getHeight()) {
g.drawLine(0, origin.y + y, getWidth(), origin.y + y);
y += getSpacing();
}
}
I really appreciate your help. Thanks!
The best way to pan is to use Graphics2D.scale that way you can (a) avoid complicated logic in your paintComponent and (b) make panning a reusable feature, as shown here
https://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/drag/Zoomable.java#l58
Related
Problem definition: I want to use graphics in java to print ball and a rectangle ,the ball should move towards the rectangle by arrow keys or mouse moved.
the Problem with the code is that it is printing the ball, again and again, I want to print the ball once only
public class MouseGui extends javax.swing.JFrame implements
MouseMotionListener, MouseListener, KeyListener {
int x = 10, y = 10;
public MouseGui() {
initComponents();
addKeyListener(this);
addMouseListener(this);
}
#Override
public void paint(final Graphics g) {
g.setColor(Color.red);
g.fillOval(x, y, 50, 50);
g.setColor(Color.blue);
g.fillRect(200, 300, 100, 80);
if (x > 180 || y > 280) {
g.drawString("Target hit!!", 80, 20);
}
}
public static void main(final String args[]) {
MouseGui obj = new MouseGui();
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MouseGui().setVisible(true);
}
});
}
#Override
public void mouseMoved(final MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}
#Override
public void keyPressed(final KeyEvent e) {
int a = e.getKeyCode();
if (e.getKeyCode() == VK_UP) {
y = y - 10;
repaint();
} else if (e.getKeyCode() == VK_DOWN) {
y = y + 10;
repaint();
} else if (e.getKeyCode() == VK_RIGHT) {
x = x + 10;
repaint();
} else if (e.getKeyCode() == VK_LEFT) {
x = x - 10;
repaint();
}
}
}
Here is how this code shows output :
The problem is not that the ball gets painted multiple times, the problem is that old drawings are never cleared.
When you override paint methods, it is recommended that you call the super method, so that it does its own stuff first .
Calling super.paint(g) here , will clear the Graphics object :
#Override
public void paint(final Graphics g) {
super.paint(g);
g.setColor(Color.red);
g.fillOval(x, y, 50, 50);
g.setColor(Color.blue);
g.fillRect(200, 300, 100, 80);
if (x > 180 || y > 280) {
g.drawString("Target hit!!", 80, 20);
}
}
Also, as suggested by #JoopEggen, when doing custom painting on Swing components, you usually use a lower-level component like a JPanel and override its paintComponent method, i.e :
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(x, y, 50, 50);
g.setColor(Color.blue);
g.fillRect(200, 300, 100, 80);
if (x > 180 || y > 280) {
g.drawString("Target hit!!", 80, 20);
}
}
See this lesson to get some examples : Performing Custom Painting
Calling super.paintComponent(g) is a must.
When you click in a box, it should create a circle in that box from the designated coordinate. Unless if its already there then its removed. How do I get currentx and currenty coordinates into the fill oval?
public class Grid extends Applet{
boolean click;
public void init()
{
click = false;
addMouseListener(new MyMouseListener());
}
public void paint(Graphics g)
{
super.paint(g);
g.drawRect(100, 100, 400, 400);
//each box
g.drawRect(100, 100, 100, 100);
g.drawRect(200, 100, 100, 100);
g.drawRect(300, 100, 100, 100);
g.drawRect(400, 100, 100, 100);
//2y
g.drawRect(100, 200, 100, 100);
g.drawRect(200, 200, 100, 100);
g.drawRect(300, 200, 100, 100);
g.drawRect(400, 200, 100, 100);
//3y1x
g.drawRect(100, 300, 100, 100);
g.drawRect(200, 300, 100, 100);
g.drawRect(300, 300, 100, 100);
g.drawRect(400, 300, 100, 100);
//4y1x
g.drawRect(100, 400, 100, 100);
g.drawRect(200, 400, 100, 100);
g.drawRect(300, 400, 100, 100);
g.drawRect(400, 400, 100, 100);
if (click)
{
g.fillOval(currentx, currenty, 100, 100); // problem HERE
}
}
private class MyMouseListener implements MouseListener
{
public void mouseClicked(MouseEvent e)
{
int nowx = e.getX();
int nowy = e.getY();
nowx = nowx / 100;
String stringx = Integer.toString(nowx);
stringx = stringx+"00";
int currentx = Integer.parseInt(stringx);
nowy = nowy /100;
String stringy = Integer.toString(nowy);
stringy = stringy+"00";
int currenty = Integer.parseInt(stringy);
click = true;
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
}
Your main problem is, painting in Swing/AWT is destructive, that is, each time your paint method is called, you are expected to repaint the current state of the component.
In that case, what you really need is some way to model the state of the game so when paint is called, you can repaint it in some meaningful way. This a basic concept of a Model-View-Controller paradigm, where you separate the responsibility of the program into separate layers.
The problem then becomes, how do you translate from the view to model?
The basic idea is take the current x/y coordinates of the mouse and divide it by the cell size. You also need to ensure that the results are within the expected ranges, as you could get a result which is beyond the columns/rows of grids
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static final int CELL_COUNT = 3;
private int[][] board;
private int[] cell;
private boolean isX = true;
public TestPane() {
board = new int[CELL_COUNT][CELL_COUNT];
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
int[] cell = getCellAt(e.getPoint());
if (board[cell[0]][cell[1]] == 0) {
board[cell[0]][cell[1]] = isX ? 1 : 2;
isX = !isX;
repaint();
}
}
});
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
cell = getCellAt(e.getPoint());
repaint();
}
});
}
protected int[] getCellAt(Point p) {
Point offset = getOffset();
int cellSize = getCellSize();
int x = p.x - offset.x;
int y = p.y - offset.y;
int gridx = Math.min(Math.max(0, x / cellSize), CELL_COUNT - 1);
int gridy = Math.min(Math.max(0, y / cellSize), CELL_COUNT - 1);
return new int[]{gridx, gridy};
}
protected Point getOffset() {
int cellSize = getCellSize();
int x = (getWidth() - (cellSize * CELL_COUNT)) / 2;
int y = (getHeight() - (cellSize * CELL_COUNT)) / 2;
return new Point(x, y);
}
protected int getCellSize() {
return Math.min(getWidth() / CELL_COUNT, getHeight() / CELL_COUNT) - 10;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Point offset = getOffset();
int cellSize = getCellSize();
if (cell != null) {
g2d.setColor(new Color(0, 0, 255, 128));
g2d.fillRect(
offset.x + (cellSize * cell[0]),
offset.y + (cellSize * cell[1]),
cellSize,
cellSize);
}
g2d.setColor(Color.BLACK);
FontMetrics fm = g2d.getFontMetrics();
for (int col = 0; col < CELL_COUNT; col++) {
for (int row = 0; row < CELL_COUNT; row++) {
int value = board[col][row];
int x = offset.x + (cellSize * col);
int y = offset.y + (cellSize * row);
String text = "";
switch (value) {
case 1:
text = "X";
break;
case 2:
text = "O";
break;
}
x = x + ((cellSize - fm.stringWidth(text)) / 2);
y = y + ((cellSize - fm.getHeight()) / 2) + fm.getAscent();
g2d.drawString(text, x, y);
}
}
int x = offset.x;
int y = offset.y;
for (int col = 1; col < CELL_COUNT; col++) {
x = offset.x + (col * cellSize);
g2d.drawLine(x, y, x, y + (cellSize * CELL_COUNT));
}
x = offset.x;
for (int row = 1; row < CELL_COUNT; row++) {
y = offset.x + (row * cellSize);
g2d.drawLine(x, y, x + (cellSize * CELL_COUNT), y);
}
g2d.dispose();
}
}
}
First off, if you want to truncate a number to the nearest 100, e.g. 142 becomes 100, all you have to do is:
num = (num/100)*100;
this is because when you divide 2 integers it automatically truncates it, and you can just multiply it back to get the number. And what I think you want in this case is to create some field variables and some accessor methods. At the top of your MouseListener class, you will need to add:
private int mouseX=0;
private int mouseY=0;
then to be able to access these variables from outside of the mouselistener class you will need to add accessor methods:
public int getMouseX(){
return mouseX;
}
in your Grid Class, you can add the field:
private MyMouseListener listener;
and then initialize it in your init by doing:
listener = new MyMouseListener();
addMouseListener(listener);
then you can do the same for mouseY, and finally from your paint method you can then call:
int mouseX = listener.getMouseX();
int mouseY = listener.getMouseY();
and from there it is as simple as doing if statements or switch statements to find which box you clicked in!
Hi guys I'm super new to Java; I've looked around and haven't been able to find an answer to this question. Any chance you could help me?
Here is an example of what I'm trying to achieve.
public class FrameWork extends JFrame implements MouseListener {
... //Irrelevant to the question code
public void mouseClicked(MouseEvent e){
int x = e.getX();
int y = e.getY();
if (x==1 && y==1){
// This is where and when I want to draw GFXDice
}
}}
Now the other class, all imports left out for readability.
public class Board extends JPanel{
Image GFXDice1;
public Board() {
ImageIcon Dice1;
Dice1 = new ImageIcon(this.getClass().getResource("GFX/Dice1"));
GFXDice1 = Dice1.getImage();
}
Now the graphics part
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(GFXDice, 100, 100, null);
}
Now for the question - I want to use the method paint from the Class Board in the Class FrameWork - But can't get it to work - any ideas ? I'm offering a bazillion units of good karma to anyone who has an idea.
The general way to do most Swing drawing is via passive graphics. This means:
Do the drawing itself in the paintComponent(Graphics g) method of a JPanel or JComponent.
In your MouseListener change the state of some of the fields of the class. In your mouseClicked method you are setting the state of some local variables, and I recommend that you instead make your x and y fields, not local.
Then when the mouse listener is done making changes, call repaint() on the JPanel.
Then in the paintComponent method, use those fields that were changed in the mouse listener to do your drawing.
Don't forget to call the super's paintComponent method in your paintComponent override.
Don't forget to read tutorials on Swing Graphics to get the fine points.
Edit
For example, please have a look at a small graphics program that I created for an answer to another recent question.
The drawing occurs in the main class, SpaceShip, which extends JPanel. I add an anonymous inner MouseAdapter class for my Mouse Listener, and inside of the MouseAdapter, I call a method called moveIt, passing in the MouseEvent object.
MouseAdapter myMouseAdapter = new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
moveIt(evt);
count = count + 1;
}
#Override
public void mouseDragged(MouseEvent evt) {
moveIt(evt);
}
};
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
All moveIt(MouseEvent evt) does is to change the state of two fields, myX and myY, and then calls repaint() on the current class:
public void moveIt(MouseEvent evt) {
myY = evt.getY() - sprite.getHeight() / 2;
myX = evt.getX() - sprite.getWidth() / 2;
repaint();
}
And then in the class's paintComponent method, I first call the super's paintComponent to allow it to erase any previous old out of date images, then I paint a background image, background, then I draw a sprite that uses the myX and myY variables to tell it where to draw, then I draw some yellow rectangles at locations that are determined by the JPanel's size:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
font1 = new Font("Serif", Font.BOLD, 36);
g.drawImage(background, 0, 0, this);
g.drawImage(sprite, myX, myY, this);
g.setColor(Color.yellow);
int rectCount = 10;
int height = getHeight() / rectCount;
int width = 272;
int x = getWidth() - width;
for (int i = 0; i < rectCount; i++) {
int y = i * height;
g.drawRect(x, y, width, height);
}
g.setFont(font1);
g.drawString(Integer.toString(count), 500, 100);
}
The whole thing looks like this:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.io.IOException;
import java.net.URL;
import java.lang.String;
import java.awt.Font;
#SuppressWarnings("serial")
public class SpaceShip extends JPanel {
private static final String BACKGROUND_PATH = "http://www.thatsreallypossible.com/"
+ "wp-content/uploads/2012/12/Space-Colonialisation.jpg";
private static final String SPRITE_PATH = "http://www.pd4pic.com/"
+ "images250_/ufo-flying-saucer-spacecraft-spaceship-alien.png";
private Font font1;
int myX = 100;
int myY = 400;
int count = 0;
private BufferedImage background;
private BufferedImage sprite;
public SpaceShip() throws IOException {
URL backgroundUrl = new URL(BACKGROUND_PATH);
URL spriteUrl = new URL(SPRITE_PATH);
background = ImageIO.read(backgroundUrl);
sprite = ImageIO.read(spriteUrl);
MouseAdapter myMouseAdapter = new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
moveIt(evt);
count = count + 1;
}
#Override
public void mouseDragged(MouseEvent evt) {
moveIt(evt);
}
};
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
#Override
public Dimension getPreferredSize() {
if (background != null) {
return new Dimension(background.getWidth(), background.getHeight());
}
return super.getPreferredSize();
}
public void moveIt(MouseEvent evt) {
myY = evt.getY() - sprite.getHeight() / 2;
myX = evt.getX() - sprite.getWidth() / 2;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
font1 = new Font("Serif", Font.BOLD, 36);
g.drawImage(background, 0, 0, this);
g.drawImage(sprite, myX, myY, this);
g.setColor(Color.yellow);
int rectCount = 10;
int height = getHeight() / rectCount;
int width = 272;
int x = getWidth() - width;
for (int i = 0; i < rectCount; i++) {
int y = i * height;
g.drawRect(x, y, width, height);
}
g.setFont(font1);
g.drawString(Integer.toString(count), 500, 100);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Basic Game");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
SpaceShip ex;
try {
ex = new SpaceShip();
frame.getContentPane().add(ex);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
ex.requestFocus();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I have java swing chess application. Cursor has custom view - rectangle, sized to fit whole cell. And I need cursor moving only over whole cell. Not in the limits of one cell. Is there some typical solutions for this problem? Or maybe it is possible to set with standard java capabilities step-type cursor moving?
I wouldn't implement some kind of "stepping" cursor. Instead I would hide the cursor completly and highlight the current cell programmatically.
Hiding the cursor check out this question and answer.
Use a MouseMotionListener to get the movements of the mouse (and highlight it in the paintComponent method of your custom component
Full example below that "outputs" this screenshot:
public class StepComponent extends JComponent implements MouseMotionListener {
private Point point = new Point(0, 0);
public StepComponent() {
setCursor(Toolkit.getDefaultToolkit().createCustomCursor(
new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB),
new Point(0, 0), "blank cursor"));
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = 0, y = 0;
while (x < getWidth()) { g.drawLine(x, 0, x, getHeight()); x += 10; }
while (y < getHeight()) { g.drawLine(0, y, getWidth(), y); y += 10; }
if (point != null)
g.fillRect(point.x, point.y, 10, 10);
}
#Override public void mouseDragged(MouseEvent e) { update(e.getPoint()); }
#Override public void mouseMoved(MouseEvent e) { update(e.getPoint()); }
private void update(Point p) {
Point point = new Point(10 * (p.x / 10), 10 * (p.y / 10));
if (!this.point.equals(point)) {
Rectangle changed = new Rectangle(this.point,new Dimension(10,10));
this.point = point;
changed.add(new Rectangle(this.point, new Dimension(10, 10)));
repaint(changed);
}
}
}
And some test code:
public static void main(String[] args) {
JFrame frame = new JFrame("Test");
frame.add(new StepComponent());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
frame.setVisible(true);
}
I have JPanel in a container of a JFrame called Box
public Box(){
add(new Ball());
}
public void paint(Graphics g){
g.setColor(Color.WHITE);
g.fillRect(OFFSET, OFFSET, WIDTH, HEIGHT);
g.setColor(Color.BLACK);
g.drawRect(OFFSET, OFFSET, WIDTH, HEIGHT);
}
Ball extends Component and draws a ball
public class Ball extends Component{
...
public void paint(Graphics g){
g.setColor(Color.BLACK);
g.fillOval(xCoord, yCoord, radius, radius);
}
...
}
When I add a Box with a Ball to the container I can only ever see the Box. If I just add a Ball I can see the Ball.
Does anyone know why the Ball is not visible when added to a Box?
In addition to overriding paintComponent, use a LayoutManager to set bounds automatically. For testing purposes, you can set the LayoutManager of the Box instance to null and use setBounds on the Ball instance.
Do not mix heavyweight and lightweight components. You should be extending JComponent instead.
You should be overriding paintComponent(), not paint().
Does Ball have a size? If you haven't supplied Ball with a Dimension, it won't be visible.
In Swing, you should normally never override the paint method. Use paintComponent instead.
there are three possible mistake
1/ simpliest paint by using JLabel
2/ timing by javax.swing.Timer
3/ paintComponents instead of paint (for AWT Compoents and painting DefaultXxxUI)
and put that together, for example
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AnimationJPanel extends JPanel {
private static final long serialVersionUID = 1L;
private int cx = 0;
private int cy = 150;
private int cw = 20;
private int ch = 20;
private int xinc = 1;
private int yinc = 1;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
AnimationJPanel panel = new AnimationJPanel();
panel.setPreferredSize(new Dimension(400, 300));
panel.animate();
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public AnimationJPanel() {
setLayout(new BorderLayout());
JLabel label = new JLabel("This is an AnimationJPanel");
label.setForeground(Color.RED);
label.setHorizontalAlignment(SwingConstants.CENTER);
add(label);
setBackground(Color.BLACK);
setForeground(Color.RED);
setOpaque(true);
}
public void animate() {
new Timer(15, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Rectangle oldCircle = new Rectangle(cx - 1, cy - 1, cw + 2, ch + 2);
cx += xinc;
cy += yinc;
if (cx >= getWidth() - cw || cx <= 0) {
xinc *= -1;
}
if (cy >= getHeight() - ch || cy <= 0) {
yinc *= -1;
}
repaint(oldCircle);
repaint(cx - 1, cy - 1, cw + 2, ch + 2);
}
}).start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(cx, cy, cw, ch);
}
}