Dragging BufferedImage in JPanel - java

I have JPanel inside JFrame and there are more than one BufferedImages in the JPanel like this.
Now my question is how can i move the BufferedImages inside the JPanel? More specifically How to add MouseEventhandler to the Buffered Images?. Though I can drag JPanel from the code below but can't figure out how to drag Buffered Images inside JPanel. Thanks for the help.
I have three classes like this
MainWindow.Java
public class MainWindow extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainWindow window = new MainWindow();
window.frame.setVisible(true);
} catch (Exception e) {
}
}
});
}
public MainWindow() {
JFrame frame = new JFrame();
.... // Other code of JFrame
JPanel panel_3 = new JPanel();
.... // other code of JPanel
frame.getContentPane().add(panel_3);
panel_3.setLayout(new BorderLayout(0, 0));
Drawing drawingObj = new Drawing();
panel_3.removeAll();
panel_3.add(drawingObj );
drawingObj .revalidate();
drawingObj .repaint();
BasicDragging drag= new BasicDragging();
panel_3.addMouseListener(drag);
panel_3.addMouseMotionListener(drag);
}
}
Drawing.java
public class Drawing extends JPanel {
private BufferedImage Image1 ;
private BufferedImage Image2;
private BufferedImage Image3;
public Drawing() {
try {
Image1 = ImageIO.read(new File("path of file"));
Image2 = ImageIO.read(new File("path of file"));
Image3 = ImageIO.read(new File("path of file"));
}
catch (IOException ex) {
// handle exception...
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(Image1 , 150, 10, null);
g.drawImage(Image2 , 150, 70,null);
// Other code of drawing images
}
}
}
BasicDragging.java
public class BasicDragging extends MouseInputAdapter{
Point location;
MouseEvent pressed;
public void mousePressed(MouseEvent me)
{
pressed = me;
}
public void mouseDragged(MouseEvent me)
{
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
}

Instead of using images and then writing code to determine which image has "been clicked" you can just use a JLabel with an ImageIcon to display the image. Then you can add your MouseListener to the JLabel and drag the label around the panel.
Using this approach the basic code for dragging components would be:
public class DragListener extends MouseInputAdapter
{
Point location;
MouseEvent pressed;
public void mousePressed(MouseEvent me)
{
pressed = me;
}
public void mouseDragged(MouseEvent me)
{
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
}
The code to use this class would be:
DragListener drag = new DragListener();
component.addMouseListener( drag );
component.addMouseMotionListener( drag );
You can also check out the Component Mover which adds some more advanced dragging features.
You will need to use a null layout on the panel and set the size/location of the label. So you may want to use the Drag Layout which simplifies this process for you.

My first suggestion would be a simple one...
As you can see you draw these images by coordinates, x and y.
You could add the MouseListener to JPanel. That's where the magic already happens and it would be rather confusing if you create more objects.
Now you only have to check if the mouse hover above a image and is pressed.
You only need to calculate the size and if the point is inside the image.
If it is inside the image moves in the direction of the mouse.
And you shouldn't reset the position rather move it along.
That's how it is done.
Just set the x and y for the images when the are clicked and pressed on.
I'd create an object DragImage which contains the core BufferedImage + coordinates that represent the x and y and it's size. Additionally i'd add a function that calculates public boolean isHovering(int x, int y).
It should work out. And the x and y coordinates are used in your draw function.
I hope you understand what i was trying to say

Related

JPanel drawing is slowed down after several instances are painted

I'm building a game and I'm painting the road sprites with the JPanel's draw function. The roads (Building class) can be built by dragging the mouse and on each field a new road sprite appeares. But after I've drawn like 20 road sprites, the drawing gets really slow.
I have a frame and there is this JPanel on it.
Here is the code of the JPanel on which my game drawing is:
private class GamePanel extends JPanel implements ActionListener{
Field[][] map = gameEngine.getMap().getFields();
ArrayList<Building> buildings = gameEngine.getBuildings();
Timer timer;
ArrayList<Field> fields = new ArrayList<>();
GameFrame frame; //REFERENCE FOR THE CONTAINER OF THIS PANEL
private int mousePosX;
private int mousePosY;
GamePanel(GameFrame frame){
/*...*/
Mouse mouseListener = new Mouse();
addMouseListener(mouseListener);
addMouseMotionListener(mouseListener);
timer = new Timer(1000/30,this);
timer.start();
/*...*/
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g){
for(Building b : buildings){
int drawPosX = b.getLocation().getPos().x*40;
int drawPosY = b.getLocation().getPos().y*40;
try {
BufferedImage img = ImageIO.read(new File("src/GFX/" + b.getType() + ".png"));
g.drawImage(img, drawPosX, drawPosY, null);
} catch (IOException e) {
}
}
}
#Override
public void actionPerformed(ActionEvent e) {
// I thought this is not needed because I call "repaint()" only at mouse events (like
// building road by dragging)
}
public class Mouse extends MouseAdapter{
#Override
public void mouseDragged(MouseEvent evt){
repaint(); // THIS IS AN EXAMPLE TO WHERE I CALL THE REPAINT()
fieldPosX = evt.getX() - (evt.getX() % 40);
fieldPosY = evt.getY() - (evt.getY() % 40);
gameEngine.placeRoad(new SimpleRoad(new Field(fieldPosX/40,fieldPosY/40)));
}
/*... OTHER MOUSE EVENTS ...*/
}
I thought that calling repaint() only at mouse events will optimise the speed but it really isn't. I attached a GIF on which it can be seen that after 2 line of roads, it gets really slow.
I heard about invokeLater and people advised me to use it but I don't know how to implement that in this project. Why is my game getting slower after several buildings, where am I making a mistake? Would invokeLater solve the problem? How do I place it in my project?
Thanks for helping!
Why is my game getting slower after several buildings,
try
{
BufferedImage img = ImageIO.read(new File("src/GFX/" + b.getType() + ".png"));
g.drawImage(img, drawPosX, drawPosY, null);
}
Don't do I/O in a painting method. As you add more building you are doing more I/O.
The images should be read in the constructor of your class.
You can save them in a HashMap for easy access in the painting method.
Or, the image can be saved as part of the Building class itself.

How to rotate the Image along y-axis?

The code i tried is rotating the image but i want to rotate the image vertically like rotating earth at 360 degrees with 0 inclination
The code i tried is
public class MainClass extends JPanel {
static ImageIcon icon = null;
static RotatedIcon rotate = null;
static JLabel label = null;
public MainClass() {
try {
BufferedImage wPic = ImageIO.read(this.getClass().getResource(
"globe.png"));
icon = new ImageIcon(wPic);
rotate = new RotatedIcon(icon, 180);
label = new JLabel(rotate);
} catch (Exception e) {
System.out.println("raise exception");
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
label.repaint();
}
public static void main(String[] args) throws IOException,
InterruptedException {
MainClass mainClass = new MainClass();
JFrame frame = new JFrame();
mainClass.add(label);
frame.add(mainClass);
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
ActionListener taskPerformer = new ActionListener() {
int degree = 360;
public void actionPerformed(ActionEvent evt) {
rotate.setDegrees(degree);
degree = degree + 90;
label.repaint();
mainClass.repaint();
}
};
Timer timer = new Timer(1000, taskPerformer);
// timer.setRepeats(false);
timer.start();
Thread.sleep(5000);
}
}
https://tips4java.wordpress.com/2009/04/06/rotated-icon/ Reference link of the RotatedIcon class i used.
As Explained am able to rotate image but that is not vertically.
If you only have a flat 2D image of the world then then best you might be able to do is use the Marquee Panel.
The Marquee Panel will allow you to scroll the image and you can set it to wrap when it reaches the end. You won't get a 3D effect buy you will scroll around the world with a flat 2D image.

Repainting JPanel

So i'm trying to clear my drawing Panel and I have looked at multiple examples but none of them seem to be working for me? I have a clear button that clears textfields/errors which I got to work perfectly but the Drawing panel still does not clear arraylists or "repaint".
I'm playing around with changing around the size of the oval so please ignore my drawPoints method.
Here is my code:
public class Panel extends JPanel{
ArrayList<Point> pointArray = new ArrayList<>();
ArrayList<Color> colorArray = new ArrayList<>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
repaint();
//Create the 2D graphics object
Graphics2D myDrawing = (Graphics2D) g;
for (int i = 0; i < pointArray.size(); i++) {
myDrawing.setColor(colorArray.get(i));
myDrawing.fillOval(pointArray.get(i).x,pointArray.get(i).y, 10, 10);
}
}
public void drawPoints(int mouseX, int mouseY, int height, int width){
Point p = new Point(mouseX,mouseY);
pointArray.add(p);
colorArray.add(this.getForeground());
repaint();
}
public void changeColor(){
int red = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
this.setForeground(new Color(red,green,blue));
}
public void mousePressed(MouseEvent event) {
pointArray.clear();
colorArray.clear();
repaint();
}
}
public static void main(String[] args) {
//set the frame
JFrame frame = new JFrame();
frame.setSize(600, 300);
frame.setTitle("Multiple Panels");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//create the panel for GUI
JPanel panelGUI = new JPanel();
panelGUI.setBackground(Color.yellow);
//GUIs
//create textfields
JTextField radiusField1 = new JTextField("10", 10);
JTextField radiusField2 = new JTextField("10", 10);
//create buttons
final JButton clearDrawingButton = new JButton("Clear Screen");
final JButton changeColorButton = new JButton("Change Color");
//labels
final JLabel displayLabel = new JLabel("");
//add all GUIs to the GUI panel
panelGUI.add(radiusField1);
panelGUI.add(radiusField2);
panelGUI.add(changeColorButton);
panelGUI.add(clearDrawingButton);
panelGUI.add(displayLabel);
//create the panel to draw
final Panel drawingPanel = new Panel();
drawingPanel.setBackground(Color.white);
//create the initial color
Color drawingColor = new Color(255,0,0);
//set the initial drawing color of the panel
drawingPanel.setForeground(drawingColor);
//add the grid with two columns and two rows to add the three panels
GridLayout grid = new GridLayout(0,2,10,20);
//add the grid to the frame
frame.setLayout(grid);
//add the panels to the frame
frame.add(panelGUI);
frame.add(drawingPanel);
class MouseClickListener implements MouseListener
{
public void mouseClicked(MouseEvent event)
{
int x = event.getX();
int y = event.getY();
System.out.println(x + " " + y);
try {
String text1 = radiusField1.getText();
String text2 = radiusField2.getText();
int height = Integer.parseInt(text1);
int width = Integer.parseInt(text2);
drawingPanel.drawPoints(x, y, height, width);
} catch (NumberFormatException ex) {
displayLabel.setText("Textfields empty! Please enter number.");}
}
// Do­nothing methods
public void mouseReleased(MouseEvent event) {}
public void mousePressed(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
}
class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
if (event.getSource()== changeColorButton){
drawingPanel.changeColor();
}
if(event.getSource()==clearDrawingButton){
radiusField1.setText("10");
radiusField2.setText("10");
displayLabel.setText("");
}
}
}
MouseListener listener1 = new MouseClickListener();
drawingPanel.addMouseListener(listener1);
ActionListener listener = new ButtonListener();
changeColorButton.addActionListener(listener);
clearDrawingButton.addActionListener(listener);
frame.setVisible(true);
}
}
I have a clear button that clears textfields/errors which I got to work perfectly but the Drawing panel still does not clear arraylists or "repaint".
Well look at the code that clears the text fields:
if(event.getSource()==clearDrawingButton){
radiusField1.setText("10");
radiusField2.setText("10");
displayLabel.setText("");
}
Where is the code that clears the ArrayLists?
Add the code to clear the ArrayLists to the ActionListener.
You can also check out Custom Painting Approaches for working code that draws "rectangles". It supports different colors and a "Clear" button.
Also, instead of using a JTextField for the oval size, you might want to consider using a JSpinner. This will allow the user to easily change the numeric value and you don't have to add any special editing to make sure the value entered is a number.
You have this mousePressed method in your main class:
public void mousePressed(MouseEvent event) {
pointArray.clear();
colorArray.clear();
repaint();
}
But it isn't doing anything because your main class does not implement MouseListener and no one is calling this method.
The rest of your code is not very pretty looking. I assume you are doing this as part of a course or otherwise just trying to learn Java Swing in a non-work environment. If this is true, I would recommend that you start over - at least with your MouseListeners, and instead create Actions for your button responses by subclassing AbstractAction and using it with JButton.setAction(myAction); This may seem painful now, but you'll be glad you did it in the future

How to create a background and foreground image which overlaps?

By using Java swing, what available approach are there to create a foreground image (such as an image of a knight) which is movable on a static background image?
Shall we use JLabel with image icons?
This solution also addresses the issues mentioned in: Images In JFrame are overwriting each other not displaying both images over eachother
If we try to add a background and some foreground images, it can be a little tricky if we intend to let those images overlap each other as many layouts provided by Java may prevent components (such as JLabels) from overlapping each other. Positioning the images to the exact location can be an issue too.
I will suggest a different approach when we want to create a screen similar to those we see in games:
Instead of creating multiple JLabel filled with imageIcon, an alternative will be drawing directly on the panel. This is a customized panel with instances of images we are interested to draw.
class DrawingSpace extends JPanel
{
private BufferedImage bg, hero;
private int bgWidth, bgHeight;
private int heroWidth, heroHeight;
private int scWidth, scHeight;
private int mouseX, mouseY;
public DrawingSpace(){
loadImages();
init();
setPreferredSize(new Dimension(scWidth, scHeight));
addMouseMotionListener(new MouseHandler());
}
private void init(){
mouseX = 0;
mouseY = 0;
heroWidth = hero.getWidth();
heroHeight = hero.getHeight();
bgWidth = bg.getWidth();
bgHeight = bg.getHeight();
scWidth = bgWidth;
scHeight = bgHeight;
}
private void loadImages(){
try{
bg = ImageIO.read(getClass().getResource("Images/background.jpg"));
hero = ImageIO.read(getClass().getResource("Images/knight.png"));
}catch(IOException ioe){System.out.println("Unable to open file");}
}
#Override public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(bg, 0, 0, bgWidth, bgHeight, null);
g.drawImage(hero, mouseX-(heroWidth/2), mouseY-(heroHeight/2), heroWidth, heroHeight, null);
}
private class MouseHandler implements MouseMotionListener
{
#Override public void mouseMoved(MouseEvent e){
mouseX = e.getX();
mouseY = e.getY();
repaint();
}
#Override public void mouseDragged(MouseEvent e){}
}
}
A runner class to drive the codes:
class KnightRunner
{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
JFrame frame = new JFrame("Knight Runner");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingSpace());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Drawing a rectangle over an existing Graphics page

I have a Java application which draws a drawing. I want to give the user the possibility to mark an area with the mouse (in order to, for example, zoom into it).
For that I use the MouseMotionListener class, and when the mouse is (clicked and then) moved, I save the location of the currently selected (it isn't final since the user haven't released the mouse) rectangle, and use the repaint() function. I wish to display that rectangle over the original drawing, making it similar to the Selection tool in MSPaint.
The problem is that when I call the repaint() function, the method paintComponent (Graphics page) is invoked, in which I use the method super.paintComponent(page) which erases my drawing. However, if I don't use that method when I know the user is selecting a rectangle, I get that all the selected rectangles are "packed" one above the other, and this is an undesirable result - I wish to display the currently selected rectangle only.
I thought I should be able to save a copy of the Graphics page of the drawing and somehow restore it every time the user moves the mouse, but I could not find any documentation for helpful methods.
Thank you very much,
Ron.
Edit: Here are the relevant pieces of my code:
public class DrawingPanel extends JPanel
{
public FractalPanel()
{
addMouseListener (new MyListener());
addMouseMotionListener (new MyListener());
setBackground (Color.black);
setPreferredSize (new Dimension(200,200));
setFocusable(true);
}
public void paintComponent (Graphics page)
{
super.paintComponent(page);
//that's where the drawing takes place: page.setColor(Color.red), page.drawOval(..) etc
}
private class MyListener implements MouseListener, MouseMotionListener
{
...
public void mouseDragged (MouseEvent event)
{
//saving the location of the rectangle
isHoldingRectangle = true;
repaint();
}
}
}
I'm betting that you are getting your Graphics object via a getGraphics() call on a component, and are disatisfied since this obtains a Graphics object which does not persist. It is for this reason that you shouldn't do this but instead just do your drawing inside of the JPanel's paintComponent. If you do this all will be happy.
As an aside -- we'll be able to help you better if you tell us more of the pertinent details of your problem such as how you're getting your Graphics object and how you're trying to draw with it, key issues here. Otherwise we're limited to taking wild guesses about what you're trying to do.
e.g.,
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MandelDraw extends JPanel {
private static final String IMAGE_ADDR = "http://upload.wikimedia.org/" +
"wikipedia/commons/thumb/b/b3/Mandel_zoom_07_satellite.jpg/" +
"800px-Mandel_zoom_07_satellite.jpg";
private static final Color DRAWING_RECT_COLOR = new Color(200, 200, 255);
private static final Color DRAWN_RECT_COLOR = Color.blue;
private BufferedImage image;
private Rectangle rect = null;
private boolean drawing = false;
public MandelDraw() {
try {
image = ImageIO.read(new URL(IMAGE_ADDR));
MyMouseAdapter mouseAdapter = new MyMouseAdapter();
addMouseListener(mouseAdapter);
addMouseMotionListener(mouseAdapter);
} catch (MalformedURLException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
#Override
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(), image.getHeight());
}
return super.getPreferredSize();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
if (image != null) {
g.drawImage(image, 0, 0, null);
}
if (rect == null) {
return;
} else if (drawing) {
g2.setColor(DRAWING_RECT_COLOR);
g2.draw(rect);
} else {
g2.setColor(DRAWN_RECT_COLOR);
g2.draw(rect);
}
}
private class MyMouseAdapter extends MouseAdapter {
private Point mousePress = null;
#Override
public void mousePressed(MouseEvent e) {
mousePress = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
drawing = true;
int x = Math.min(mousePress.x, e.getPoint().x);
int y = Math.min(mousePress.y, e.getPoint().y);
int width = Math.abs(mousePress.x - e.getPoint().x);
int height = Math.abs(mousePress.y - e.getPoint().y);
rect = new Rectangle(x, y, width, height);
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
drawing = false;
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MandelDraw");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MandelDraw());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You need to repaint on every mouse movement:
public void mouseDragged(MouseEvent e){
int x = e.getX();
int y = e.getY();
//Update the rectangle holder object with that point coordinates
repaint();
}
You'll probably have a holder rectangle object to hold the initial and final rectangle points. The initials are set on mouse click, the final are modified on mouse dragged and on mouse released.
In paint method, clear the graphics and draw a rectangle with the coordinates in the holder. This is the basic idea.
UPDATE: How to draw a new shape on top of the existing image:
I'm thinking of two options:
If you are only drawing shapes (such as lines, rectangles and other Java2D stuff) you could have a Collection holding these shapes coordinates, and draw all of them on each paint. Pros: good when there are few shapes, allows undoing. Cons: When the number of shapes increase, the paint method will take more and more time in each pass.
Have a "background image". On each paint call, draw first the image and then the currently active shape on top. when an active shape is made persistent (onMouseReleased), it is saved to the background image. Pros: efficient, constant time. Cons: drawing a big background image on every mouse movement could be "expensive".

Categories

Resources