Recently I started making a Map Editor for my game and I came across a couple of issues that I've kind of have fixed. My current problem right now is that I have a JFrame (main interface). Within that JFrame I have a JScrollPane which holds my Client(a JPanel). Since I can't post picture I will post a link of the image.
Image of GUI: http://i.imgur.com/yPNAlYS.png
This is where I add my client(JPanel) to the JScrollPane.
//======== mainScrollPane ========
{
client.setPreferredSize(client.getSize());
mainScrollPane.setPreferredSize(client.getSize());
mainScrollPane.setViewportView(client);
mainScrollPane.add(client);
This is my Client.java|The MapEditor class is a little large to post here but feel free to ask for snippets or anymore information.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;
public class Client extends JPanel implements Runnable, MouseListener, MouseMotionListener {
private static final long serialVersionUID = 1L;
private Thread animationThread;
protected Map map;
private Tile tile;
public int mX, mY;
public Client() {
init();
setVisible(true);
setFocusable(true);
animationThread = new Thread(this);
animationThread.start();
}
public void init() {
addMouseListener(this);
addMouseMotionListener(this);
ImageHandler.loadImages();
map = new Map();
setSize(1000, 500);
}
public void run() {
while (animationThread != null) {
repaint();
try {
Thread.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void paint(Graphics g) {
Graphics2D gg = (Graphics2D) g;
map.drawCurrentMap(gg);
}
#Override
public void mouseClicked(final MouseEvent e) {
//final Point p = e.getPoint();
//final int x = p.x / 25;
//final int y = p.y / 25;
//map.getTileAt(x, y).setGraphicId(MapEditor.id);
//map.getTileAt(x, y).setBlocked(true);
map.getTiles().set(map.getTileIndex(mX, mY), new Tile(mX, mY, true, MapEditor.id));
System.out.println(map.getTiles().get(map.getTileIndex(mX, mY)).toString());
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {;
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
mX = e.getX()/25;
mY = e.getY()/25;
}
}
This has nothing to do with scroll pane, but with how you've done your custom painting...
Basically, this...
public void paint(Graphics g) {
Graphics2D gg = (Graphics2D) g;
map.drawCurrentMap(gg);
}
Is breaking the paint chain requirements. A Graphics context is a shared resource, meaning that everything painting within a given paint cycle shares the same Graphics contents.
Painting is also a complex chain of methods which is, as you've discovered, really easy to break.
Instead, you should override paintComponent and perform your custom painting there, making sure you call super.paintComponent first
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details
Related
I am a beginner. I am trying to add filled rectangle or any other graphic on JFrame using multiple inner classes. I am getting debugging errors. What are the problems here?. If this is a wrong way. Please tell me how to do the same using JFrame and JPanel only.
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.io.*;
public class RainBow{
JFrame frame;
public static void main(String[] args){
RainBow bow = new RainBow();
bow.go();
}
public class Paint extends JPanel{
public void paintComponent(Graphics g){
g.setColor(Color.red);
g.fillRect(100, 100, 100, 100);
}
}
public void go(){
frame.addMouseListener(new ListenMouse());
frame.setSize(400, 400);
frame.setVisible(true);
}
public class ListenMouse implements MouseListener{
public void mosueClicked(MouseEvent a){
Paint p = new Paint();
frame.getContentPane().add(p);
frame.setVisible(true);
}
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
}
The code posted has multiple problems. See this working example with explanations in comments.
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
public class RainBow {
JFrame frame;
boolean paintRectangle = false;
public static void main(String[] args) {
RainBow bow = new RainBow();
bow.go();
}
public class Paint extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g); // should always be done
if (paintRectangle) {
g.setColor(Color.red);
g.fillRect(100, 100, 100, 100);
}
}
}
public void go() {
frame = new JFrame(); // otherwise NPE
Paint paint = new Paint();
paint.addMouseListener(new ListenMouse()); // add listner to paint
frame.add(paint); // add paint at start-up
frame.setSize(400, 400);
frame.setVisible(true);
// ensures JVM shuts down when frame is closed.
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
public class ListenMouse extends MouseAdapter {
// this method is incorrectly spelled!
public void mosueClicked(MouseEvent a) {
}
#Override
public void mouseClicked(MouseEvent arg0) {
/* requires special handling to add components on the fly */
//Paint p = new Paint();
paintRectangle = true;
frame.repaint(); // forces the Paint to be painted as well.
}
}
}
So, here is the thing. My lecturer taught us how to get mouse coordinates by clicking on an applet. Now, i was thinking that may be i can try drawing dynamic lines using mousemotionlistener class using the mousedrag method and the mouse coordinates. I tried it but couldn't really make it work. Can anyone please suggest me where i made the error. Thanks a lot.
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.applet.*;
public class drawing extends Applet
implements MouseMotionListener {
int mx,my;
Graphics2D g2;
public void init()
{
this.addMouseMotionListener(this);
}
public void paint(Graphics g)
{
this.setSize(800,600);
g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(10));
g2.drawLine(40,50,200,150); // I was experimenting to draw static lines using the drawLine() method.
}
#Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
mx = e.getX();
my = e.getY();
System.out.println(" "+ mx + " "+ my);
g2.setStroke(new BasicStroke(10));
g2.drawLine(mx,my,mx,my);
}
#Override
public void mouseMoved(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
In order to draw a line, you need to know the start point (which would be the point the user clicked) and the end point (the point where the user dragged to), from there is a simple matter to simple use Graphics#drawLine.
Start by taking a look at How to Write a Mouse Listener, 2D Graphics and Painting in AWT and Swing for more details
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
public class Drawing extends Applet
implements MouseMotionListener, MouseListener {
private Point startPoint, endPoint;
public void init() {
this.addMouseMotionListener(this);
addMouseListener(this);
}
#Override
public void paint(Graphics g) {
super.paint(g);
if (startPoint != null && endPoint != null) {
g.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
}
}
#Override
public void mouseDragged(MouseEvent e) {
endPoint = e.getPoint();
repaint();
}
#Override
public void mouseMoved(MouseEvent arg0) {
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
startPoint = e.getPoint();
endPoint = e.getPoint();
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
Now, my advice, don't use Applet or AWT directly. Instead, start with a JPanel and override it's paintComponent method and use it perform your custom painting. Then add this panel to a JFrame to display it on the screen.
AWT is more then 15 years out of date and applets have way to many gotchas which you just don't need to deal with right now.
Start by having a look at Creating a GUI With JFC/Swing or even do some research into JavaFX
I'm currently trying to make a game with a GUI that needs to paint new things on the screen on button clicks. For example:
public class GUI() extends JPanel {
public void paintComponent() {
/*
*Basic initial set up here
*/
// ***** Call method here on mouse click *****
}
public void setUpGUI() {
JFrame mainFrame = new JFrame();
GUI paintGUI = new GUI();
clickDetector click = new clickDetector();
mainFrame.addMouseListener(click);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(500, 500);
mainFrame.getContentPane().add(paintGUI);
mainFrame.setVisible(true);
}
public static void main(String args[]) {
GUI gui = new GUI();
gui.setUpGUI();
}
}
I need to implement a method in the // ***** Call method here on mouse click ***** that will paint on the new additions to the frame (in my case these are circles that represent pieces on the board) but I'm unsure of how to do this on a button click. How can I repaint the frame each time the mouse is clicked so that I can modify my game board?
----EDIT----
Here is my paintComponent code, along with the listener being used to repaint.
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Method called");
g.setColor(Color.red);
for(int y = 0; y < 6; y++) {
for(int x = 0; x < 7; x++) {
g.fillOval(x*70 + 10, y*70 + 10, 50, 50);
}
}
g.setColor(Color.BLACK);
g.fillRect(0, 430, 500, 50);
g.setColor(Color.white);
g.drawString("CONNECT FOUR", 250, 450);
g.setColor(Color.LIGHT_GRAY);
click.paintPiece(g);
}
public void mouseClicked(MouseEvent e) {
this.repaint();
}
Here is the method that paintComponent should be calling, but is not
public void paintPiece(Graphics g) {
int x = getMouseX() + 10;
int y = mover.getRow() + 10;
g.fillOval(x, y, 50, 50);
}
Just create a mouse listener:
MouseListener listen = new MouseListener()
{
void mouseClicked(MouseEvent e){}
void mouseReleased(MouseEvent e){}
void mousePressed(MouseEvent e){paintGUI.repaint();}
void mouseExited(MouseEvent e){}
void mouseEntered(MouseEvent e){}
};
paintGUI.addMouseListener(listen);
Every time you click inside of the JPanel, you should now see it repaint. Likewise, if you want to update when a JButton is pressed, just use ActionListener instead:
ActionListener listen = new ActionListener()
{
public void actionPerformed(ActionEvent e){paintGUI.repaint();}
}
button.addActionListener(listen);
This should be placed in your setUpGUI() method.
Add a Action Event listener to the button.
Oracle Docs Action Event Listener
In the actionPerformed method add whatever needs to be added then call
repaint();
This changes will let you paint a rectangle each time you click on the frame
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GUI extends JPanel implements MouseListener{
private Rectangle rect;
private int width = 100;
private int height = 100;
public GUI(int x, int y, int width, int height)
{
rect = new Rectangle(x, y, width, height);
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.draw(rect);
}
public void mouseClicked(MouseEvent e) {
rect = new Rectangle(e.getX(), e.getY(), width, height);
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
}
public void setUpGUI() {
JFrame mainFrame = new JFrame();
mainFrame.addMouseListener(this);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(500, 500);
mainFrame.getContentPane().add(this);
mainFrame.setVisible(true);
}
public static void main(String args[]) {
GUI gui = new GUI(0,0,100,100);
gui.setUpGUI();
}
}
Notice the MouseListener was implemented by the GUI class and when you are trying to initialize the MouseListener for the frame, you are just putting this as parameter, which will refer to the GUI class and therefore to your JPanel
Here is the code.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Robot;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;
public class Paint extends JPanel implements MouseMotionListener, MouseListener {
public Paint() {
setBackground(Color.RED);
addMouseMotionListener(this);
addMouseListener(this);
}
private boolean clicked = false;
public void paintComponent (Graphics g) {
super.paintComponent(g);
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
int x = (int) b.getX() - 3;
int y = (int) b.getY() - 23;
if (clicked) {
g.drawLine(x, y-5000, x,y+5000);
g.drawLine(x+5000,y,x-5000,y);
g.setColor(Color.white);
}
g.drawLine(x, y-5000, x,y+5000);
g.drawLine(x+5000,y,x-5000,y);
g.setColor(Color.black);
// . . .
}
#Override
public void mouseDragged(MouseEvent e) {
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
repaint();
}
#Override
public void mouseClicked(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
}
#Override
public void mousePressed(MouseEvent e) {
clicked = true;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
clicked = false;
repaint();
}
}
public class Frame extends JFrame {
public Frame() {
this.setSize(500,500);
this.setTitle("Test painting");
this.setResizable(false);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
Paint panel = new Paint();
this.add(panel);
this.setVisible(true);
}
public static void main (String[] args) {
new Frame();
}
}
When I run the program, it looks like it works fine when the application is in the default position, but once I move the applet to where I like on the screen, the lines don't maintain the current mouse position on screen.
Can someone show me where I've gone wrong or how to fix this?
Painting is done from the context of the component. The Graphics context for any given component is translated so that the top, left corner is 0x0.
MouseInfo.getPointerInfo().getLocation() is returning the location of the mouse on the screen, not the position relative to the component.
While there is a way to fix it, a better solution would be to simply use a MouseMotionListener instead. The MouseEvent sent to this method has already been translated to the component coordinate space...
public void mouseMoved(MouseEvent me) {
myPoint = me.getPoint();
}
Then in you paintComponent method, simple refer to myPoint instead of trying to use MouseInfo
I think that you will be better off to change your class's state in the MouseAdapter, call repaint() and then have your paintComponent(...) method use this state to help it draw. This seems cleaner and safer to me than using MouseInfo in paintComponent(...).
Other than that, if you need more specific help, consider
telling us more about what it is exactly you're trying to do and how your program isn't working.
creating and posting an sscce, a small compilable and runnable program that demonstrates your problem for us.
I'm trying to make an applet which I can simply drag an image. And I want image object to listen events. So here is the applet code which simple run in a thread:
import java.awt.*;
import java.net.URL;
import javax.swing.JApplet;
public class Client extends JApplet implements Runnable {
private static final long serialVersionUID = 1L;
MediaTracker mediaTracker;
Image [] imgArray;
Tas t1;
public void init()
{
mediaTracker = new MediaTracker(this);
imgArray = new Image[1];
URL base = getCodeBase();
imgArray[0] = getImage(base,"okey.png");
mediaTracker.addImage(imgArray[0],1);
try {
mediaTracker.waitForAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
t1 = new Tas(this, new Rectangle(0, 0, imgArray[0].getWidth(this), imgArray[0].getHeight(this)), imgArray[0]);
Thread t = new Thread(this);
t.start();
}
public void paint(Graphics g)
{
t1.paint(g);
}
#Override
public void run() {
while(true){
//System.out.println("run");
repaint();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
And the class of object which holds image is:
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Movable extends JPanel implements MouseListener {
public Client mainObj;
public Rectangle rect;
public Image image;
public Movable(Client mainObj, Rectangle rect, Image image) {
this.mainObj = mainObj;
this.rect = rect;
this.image = image;
addMouseListener(this);
}
public void paint(Graphics g) {
g.drawImage(image, rect.x, rect.y, rect.width, rect.height, this);
}
#Override
public void mouseClicked(MouseEvent arg0) {
System.out.println("clicked");
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
System.out.println("pressed");
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
#SuppressWarnings("serial")
class Tas extends Movable{
public String name = "";
public Tas(Client mainObj, Rectangle rect, Image image) {
super(mainObj, rect, image);
}
}
I can see the image in my applet but nothing happens when I click in or out of the image. So what's wrong with this code.
Assuming that Tas in code #1 is Moveable in code #2...
You don't actually use the Moveable as a Component, but instead ask it to paint itself onto the Applet's graphics context, here:
public void paint(Graphics g)
{
t1.paint(g);
}
Instead you should add an instance of Moveable onto the Applet's container, wherein painting will become automatic, and it will start to receive mouse events. You can also remove that paint() method then too.
First of all you should never override the paint method of a top level container (JApplet, JFrame, JDialog).
Then to do custom painting on other Swing components you override the paintComponent() method of the component, NOT the paint() method. Read the Swing tutorial on Custom Painting. So first fix those problems.
I'm not sure what the point of the Thread is but remove it from your code until you solve your other problems. If you are trying to do animation, then you should be using a Swing Timer, not a Thread.
If you want to see some code for dragging components you can take a look at Moving Windows for some generic code.
This is a working solution. It is not an applet but you can easily convert that. Hope it helps :
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class ImagePanel extends JPanel {
Image image;
Point2D axis = new Point2D.Double();
boolean drag = false;
Point2D dragPoint = new Point2D.Double();
public ImagePanel(Image image) {
this.image = image;
setPreferredSize(new Dimension(300,300));
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
drag = true;
dragPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
drag = false;
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (drag) {
axis.setLocation(axis.getX()
+ (e.getPoint().x - dragPoint.getX()), axis.getY()
+ (e.getPoint().y - dragPoint.getY()));
dragPoint = e.getPoint();
repaint();
}
}
});
}
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(image, (int) axis.getX(), (int) axis.getY(), null);
}
public static void main(String[] args) {
try {
JFrame f = new JFrame();
f.getContentPane().add(
new ImagePanel(ImageIO.read(new File("image.jpg"))));
f.pack();
f.setVisible(true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
The simple answer is - you don't have code to do anything in mousePressed() or mouseReleased().
There are lots of other problems in the code though...
Simplest solution I could come up with -
public class Client extends JApplet {
private MouseInputAdapter myMouseListener = new MyMouseListener();
public void init() {
// usually a very bad idea, but needed here
// since you want to move things around manually
setLayout(null);
// assuming this will get used often, so making it a method.
addLabelForImage(getImage(getCodeBase(), "okay.png"));
}
private void addLabelForImage(Image image) {
ImageIcon icon = new ImageIcon(image);
JLabel l = new JLabel(icon);
add(l);
l.setSize(l.getPreferredSize());
// you'll probably want some way to calculate initial position
// of each label based on number of images, size of images,
// size of applet, etc. - just defaulting to 100,100 now.
l.setLocation(100, 100);
l.addMouseListener(myMouseListener);
l.addMouseMotionListener(myMouseListener);
}
// Made this a MouseInputAdapter because I assume you may want to handle
// other types of mouse events later...
private static class MyMouseListener extends MouseInputAdapter {
#Override
public void mouseDragged(MouseEvent e) {
// when the mouse is dragged over a the component this listener is
// attached to (ie - one of the labels) convert the point of the mouse
// event from the internal component coordinates (0,0 is upper right
// corner of each label), to it's parent's coordinates (0,0 is upper
// right corner of the applet), and set the components location to
// that point.
Component theLabel = e.getComponent();
Container theApplet = theLabel.getParent();
Point labelPoint = e.getPoint();
Point appletPoint = SwingUtilities.convertPoint(
theLabel, labelPoint, theApplet );
theLabel.setLocation(appletPoint);
}
}
}