How to "paint" on JLabels on a JPanel? - java

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

Related

Painting a circle at the center of JButton

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!

draw ellipse relative to panel java

Basically I want to draw an ellipse which its size is relative to JPanel in Java.
for example: Ellipse2D e = new Ellipse2D.Double(0, 0, w, h)
Which w and h is the size of the panel. So by doing this, the ellipse will automatically resize when the panel is changing its size.
I have tried this but actually it doesn't work, I wrote this code for testing only.
public class Help extends JFrame{
public static void main(String [] agrs){
Help h = new Help();
h.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
h.init();
}
public void init(){
this.setLayout(new FlowLayout());
this.setSize(2000, 1000);
JPanel a = new JPanel();
a.setPreferredSize(new Dimension(1000, 500));
a.setBorder(BorderFactory.createLineBorder(Color.yellow, 3));
Help_Option k = new Help_Option(a.getPreferredSize().width/2, a.getPreferredSize().height/4);
k.setPreferredSize(new Dimension(1000, 400));
a.add(k);
this.add(a);
this.validate();
this.setVisible(true);
}
}
class Help_Option extends JComponent implements MouseMotionListener{
private static int x, y;
private Ellipse2D ellipse = new Ellipse2D.Double(0, 0, x, y);
private Color c = Color.MAGENTA;
public Help_Option(int x, int y){
Help_Option.x = x;
Help_Option.y = y;
this.addMouseMotionListener(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
g2d.draw(ellipse);
g2d.setColor(c);
g2d.fill(ellipse);
g2d.setColor(Color.BLACK);
g2d.setFont(new Font("TimesRoman", Font.BOLD, 20));
g2d.drawString("Here I am", 250, 100);
}
public void setColor(Color c){
this.c = c;
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if(ellipse.contains(e.getX(), e.getY())){
setColor(Color.GREEN);
repaint();
}else{
setColor(Color.MAGENTA);
repaint();
}
}
}
Thank you for your code snippet. In order to make it work, you need to initalize the ellipse in the Help_Option-Constructor otherwise it is draw at 0, 0 with the widht and height of 0 and 0.
...
private Ellipse2D ellipse = null; //also works w/o this assignment.
private Color c = Color.MAGENTA;
public Help_Option(int x, int y){
Help_Option.x = x;
Help_Option.y = y;
ellipse = new Ellipse2D.Double(0, 0, x, y);
this.addMouseMotionListener(this);
}
...
If I change the code accordingly, the ellipse stays at its size even though I resize the window.
Please give me a hint (screenshot) what you mean.
I want to draw an ellipse which its size is relative to JPanel in Java.
Then you need to create the Ellipse object in the paintComponent() method. You can use the getWidth() and getHeight() methods to get the current size of the panel.

How to double buffer rectangles

It always works with images but rectangles and ovals never buffer right. I have a basic game loop in my gamepanel class that draws the player repeatedly. It doesn't remove the rectangle, just leaves a trace. I want to use a rectangle instead of an image for learning purposes. I tried using repaint in the game loop, but it flickered like crazy and still didn't work. I looked at another tutorial on this in this website but they used opengl witch is foreign to me and I don't want to take the time to figure it out.
JFrame:
import javax.swing.JFrame;
public class Game {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setTitle("OMG I MADE A GAME");
f.setResizable(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(new Panel());
f.pack();
f.setVisible(true);
}
}
JPanel Class:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.event.*;
import javax.swing.JPanel;
import com.game.entity.Player;
public class Panel extends JPanel implements Runnable, KeyListener{
private static final long serialVersionUID = -5122190028751177848L;
// dimensions
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
// game thread
private Thread thread;
private boolean running;
// image
private BufferedImage image;
private Graphics2D g;
private Player p;
public Panel() {
super();
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
// DRAWS PANEL TO FRAME
public void addNotify() {
super.addNotify();
if(thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init() {
image = new BufferedImage(
WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB );
g = (Graphics2D) image.getGraphics();
p = new Player(100, 100);
running = true;
}
public void run() {
init();
// game loop
while(running) {
update();
draw();
drawToScreen();
System.out.println("ELAPSED :" + System.nanoTime()/ 1000000 + " Seconds");
try {
Thread.sleep(10);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
private void update() {
p.update();
}
private void draw(){
// NAME (remember it loops)
String name = "2014 Jay H.";
g.setFont(new Font("Name", 0, 12));
g.setColor(Color.WHITE);
g.drawString(name, 0, 10);
g.setColor(Color.BLUE);
g.fillRect( 0, 10, 65, 5);
//TITLE looks sexy :D
g.setColor(Color.GREEN);
g.setFont(new Font("Title", 0, WIDTH/ 10));
g.drawString("JAY'S GAME", WIDTH/ 5, 100);
//DRAW PLAYER
p.draw(g);
}
// SCREEN IMAGE (dont have to use. Just use this^)
private void drawToScreen() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0,
WIDTH * SCALE, HEIGHT * SCALE,null);
g2.dispose();
}
public void keyTyped(KeyEvent key) {}
// PUBLIC KEYRELEASES
public void keyPressed(KeyEvent key) {
int KeyCode = key.getKeyCode();
//EXIT SYSTEM
if(KeyCode == KeyEvent.VK_Q) {System.exit(0);
} //UP
if(KeyCode == KeyEvent.VK_W){p.setDY(-2);}
}
// PUBLIC KEYRELEASES
public void keyReleased(KeyEvent key) {
int KeyCode = key.getKeyCode();
//UP
if(KeyCode == KeyEvent.VK_W) {p.setDY(0);}
}
}
Player Class:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
//FOR NOW THE PLAYER IS A RECTANGLE
public class Player {
// PLAYER CORDINATES AND VELOCITY
int x,y,dx,dy;
public Player(int x, int y) {
//NEEDED TO USE THE X AND Y
this.x =x;
this.y = y;
}
public void update() {
x += dx;
y += dy;
}
// DRAW TO PANEL CLASS
public void draw(Graphics2D g) {
//BODY
g.setColor(Color.PINK);
g.fillRect(x, y, 20, 20);
//EYES
g.setColor(Color.BLACK);
g.fillRect(x+3, y+2, 5, 10);
g.fillRect(x+ 12, y+2, 5, 10);
//EYERIS
g.setColor(Color.WHITE);
g.fillRect(x+3, y+2, 2, 10);
g.fillRect(x+15, y+2, 2, 10);
//NOSE
g.setColor(Color.MAGENTA);
g.fillRect(x+5, y+13, 10, 5);
//NOSTRILLS
g.setColor(Color.red);
g.fillRect(x+6, y+15, 2, 2);
g.fillRect(x+12, y+15, 2, 2);
}
//GET METHODS FOR CORDINATES AND VELOCITY (Unused for now... i think)
public int getX() {return x;}
public int getY() {return y;}
public int getDX() {return dx;}
public int getDY() {return dy;}
//SET METHODS TO CHANGE
public void setX(int x) {this.x = x;}
public void setY(int y) {this.y = y;}
public void setDX(int dx) {this.dx = dx;}
public void setDY(int dy) {this.dy = dy;}
}
You need to "reset" the background of the buffer before you paint to it.
Remember, painting is accumilitive, that is, what ever you painted previously, will remain. You will need to rebuild each frame from scratch each time you paint to it
Flickering will occur for two reasons...
You are using AWT based components, which aren't double buffered
You are overriding paint of a top level container like JFrame, which isn't double buffered.
You should either use a BufferStrategy or override the paintComponent method of a Swing based component, like JPanel which are double buffered by default

How can I get this Paintcomponent to act as I intended?

I was given the assignment to make a simple paint program in java that utilizes a GUI and has basic I/O capabilities. That was all I was told to do by my professor. However, I've only made one GUI program before, so jumping straight into this paint program has been a headache. Now I'm nearly done, but the program isn't really behaving as I expected. When new objects are drawn on the Panel, they draw invisible white rectangles on the objects underneath them that erases those objects. I think this is the result of the repaint(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); method in DrawShapes, but can't think of a way to fix it.
On the other hand, the objects are also not saving properly. I can get it to export a jpg as I intended, however, it will only export the last image drawn and not everything on the paintComponent canvas.
Lastly, the clear method in DrawShapes is working in a very similar way. When the clear method is activated, it will clear everything but the last image drawn.
Is there anyone more familiar than me with these tools that can see a way to fix these? This is only the first program I've utilized draw on, and I/O.
Here is the class for the panel that the shapes are supposed to drawn on:
/**
* #author me
*/
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class DrawShapes extends JPanel{
Point startPoint = null;
Point endPoint = null;
public int drawType = 1;
BufferedImage image;
Graphics2D g2d;
public DrawShapes(){
setBackground(Color.WHITE);
MyMouseListener m1 = new MyMouseListener();
addMouseListener(m1);
addMouseMotionListener(m1);
}//end constructor
//sets draw type, which is the decider of what is being drawn.
public void setType(int type){
if(type == 1)
{
drawType = 1;
}
else if(type == 2)
{
drawType = 2;
}
else if(type == 3)
{
drawType = 3;
}
}//end setType
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (image == null)
{
createEmptyImage();
}
g.drawImage(image, 0, 0, null);
if (startPoint != null && endPoint != null)
{
int x = Math.min(startPoint.x, endPoint.x);
int y = Math.min(startPoint.y, endPoint.y);
int width = Math.abs(startPoint.x - endPoint.x);
int height = Math.abs(startPoint.y - endPoint.y);
switch (drawType)
{
case 1:
g.drawRect(x, y, width, height);
break;
case 2:
g.drawOval(x, y, width, height);
break;
case 3:
g.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
break;
}
}
}//end paintComponent
public void save()
{
BufferedImage bi = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_INT_RGB);
Graphics g = bi.createGraphics();
this.paint(g);
g.dispose();
try{ImageIO.write(bi, "png",new File("test.png"));
}catch (Exception e){}
}
private void createEmptyImage()
{
image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
g2d = (Graphics2D)image.getGraphics();
g2d.setColor(Color.BLACK);
g2d.drawString("Add a shape by clicking and dragging.", 40, 15);
}
public void addRect(int x, int y, int width, int height, Color color)
{
g2d.setColor( color );
g2d.drawRect(x, y, width, height);
repaint();
}
public void addOval(int x, int y, int width, int height, Color color)
{
g2d.setColor( color );
g2d.drawOval(x, y, width, height);
repaint();
}
public void addLine(int x1, int y1, int x2, int y2, Color color)
{
g2d.setColor(color);
g2d.drawLine(x1, y1, x2, y2);
repaint();
}
public void clear()
{
createEmptyImage();
repaint();
}
class MyMouseListener extends MouseInputAdapter
{
private int xMin;
private int xMax;
private int yMin;
private int yMax;
public void mousePressed(MouseEvent e)
{
startPoint = e.getPoint();
endPoint = startPoint;
xMin = startPoint.x;
xMax = startPoint.x;
yMin = startPoint.y;
yMax = startPoint.y;
}
public void mouseDragged(MouseEvent e)
{
//This is code I found that should make it so the only area affected by the dragging is repainted.
endPoint = e.getPoint();
xMin = Math.min(xMin, endPoint.x);
xMax = Math.max(xMax, endPoint.x);
yMin = Math.min(yMin, endPoint.y);
yMax = Math.max(yMax, endPoint.y);
repaint(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
}
public void mouseRelease(MouseEvent e)
{
//This code paints the shapes on the Buffered Image created as a canvas
int x = Math.min(startPoint.x, endPoint.x);
int y = Math.min(startPoint.y, endPoint.y);
int width = Math.abs(startPoint.x - endPoint.x);
int height = Math.abs(startPoint.y - endPoint.y);
if (width != 0 || height != 0)
{
g2d.setColor( e.getComponent().getForeground() );
// g2d.drawRect(x, y, width, height);
switch (drawType)
{
case 1:
addRect(x, y, width, height, e.getComponent().getForeground());
break;
case 2:
addOval(x, y, width, height, e.getComponent().getForeground());
break;
case 3:
addLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, e.getComponent().getForeground());
break;
}//end switch statement.
}
startPoint = null;
// repaint();
}
}
}//end class
And here is the code for the UI:
/*#author Me*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyDrawUI extends JFrame
{
private DrawShapes draw = new DrawShapes();
private JPanel ButtonPanel = new JPanel();
private JFrame window = new JFrame("Draw!");
//constructor
MyDrawUI(){
buildUI();
}
void buildUI()
{
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLayout(new GridLayout(2,2));
window.add(draw);
window.add(ButtonPanel, BorderLayout.SOUTH);
ButtonPanel.setBackground(Color.LIGHT_GRAY);
draw.setBackground(Color.WHITE);
//define buttons
JButton rectangle = new JButton("Rectangle");
JButton oval = new JButton("Oval");
JButton line = new JButton("Line");
JButton exit = new JButton("Exit");
JButton save = new JButton("Save");
JButton clear = new JButton("Clear");
//add buttons
ButtonPanel.add(rectangle, BorderLayout.SOUTH);
ButtonPanel.add(oval, BorderLayout.SOUTH);
ButtonPanel.add(line, BorderLayout.SOUTH);
ButtonPanel.add(clear, BorderLayout.SOUTH);
ButtonPanel.add(save, BorderLayout.SOUTH);
ButtonPanel.add(exit, BorderLayout.SOUTH);
ButtonPanel.setSize(100, 100);
save.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
draw.save();
}
});
clear.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
draw.clear();
}
});
rectangle.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
draw.setType(1);
}
});
oval.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
draw.setType(2);
}
});
line.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
draw.setType(3);
}
});
exit.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
});
window.setVisible(true);
window.setSize(1024, 800);
}
}
There are a few issues I can see, the main one is the fact that you "think" you've overridden a method in the MouseAdaptor class, but haven't
mouseRelease is not method that will cause any events to trigger it. The method you're after is mouseReleased.
When overriding methods, make use the #Override annotation, it will cause a compiler error if the method you "think" you're overriding doesn't exist in any of the parent classes.
#Override
public void mouseReleased(MouseEvent e) {
Several other things pop out.
You're MyDrawUI classes extends from JFrame, but you create a instance of another JFrame called window, onto which you create your UI. In this case, drop the extends JFrame from the MyDrawUI class, as it just adds confusion...
Maintaining a reference to a Graphics context, even one you created, is ill advised in this context. On some systems, until you call dispose it's possible that nothing will be committed to the underlying implementation. Instead, simply use image.getGraphics when you need it and call g2d.dispose when you're done with it.

Java Swing - Valid approach for dragging rectangles onto a JPanel?

I have some code to draw rectangles. It's used to draw rectangles on a JPanel, to mark boundaries of widgets. Here the code first, after that I'll explain my problem cq. question.
First off, I have a class (WidgetDrawingPanel) which extends JPanel.
public WidgetDrawingPanel(int width, int height) {
/*To make things visible at least*/
widgets.add(new Widget(10,10,100,100, WidgetType.TextField));
widgets.add(new Widget(50,50,100,200, WidgetType.TextField));
this.width = width;
this.height = height;
this.setBackground(Color.BLUE);
addListener(); //adds both MouseMotionListener and MouseListener
}
Below you'll see me reference ch a lot. This is a CoordinateHolder, which holds start and current coordinates of my mouse movement.
private void addListener() {
this.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent arg0) {
ch.currentX = arg0.getX();
ch.currentY = arg0.getY();
System.out.println("dragging " + ch.currentX + ","+ch.currentY);
WidgetDrawingPanel.this.repaint();
}
});
this.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent event) {
ch.endX = event.getX();
ch.endY = event.getY();
try {
checkCoords();
} catch (OutsidePanelException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "drawn Outside Panel");
}
}
#Override
public void mousePressed(MouseEvent event) {
ch = new CoordinateHolder(event.getX(), event.getY());
}
});
}
and, finally, the paintComponent(Grapics) method. There's loop through Widgets, which are actually just already drawn Rects (x, y, w, h attributes), but which a little more information, which is not useful in the drawing part of the application. Everytime you release the mouse, the CoordinateHolder is converted into a Widget, and added to widgets.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Paint");
g.setColor(Color.BLUE);
g.fillRect(0, 0, width, height); //making the whole panel blue
g.setColor(Color.RED);
Graphics2D g2 = (Graphics2D)g;
g2.setStroke(new BasicStroke(3));
for (Widget w : widgets) {
g.drawRect(w.getX(), w.getY(), w.getW(), w.getH());
}
if (ch != null)
g.drawRect(ch.startX, ch.startY, ch.currentX - ch.startX, ch.currentY - ch.startY);
}
This code is working, but I suspect this is highly inefficient and inperformant, as above code continually refreshes the JPanel on mouse drag, which is, say, once every 10ms? I suppose it'll get slow really soon, especially when the user creates a heck of a lot rectangles (which are also continally redrawn, as seen in painComponent(Graphics)).
Question cq. Problem
Is there a better, less resource consuming method, where the user can drag rectangles smoothly?
I read an answer to this Drag rectangle on JFrame in Java, but the author of that answer seems to do it the same as me. But again, that's way inperformant, right? Or should computers be easily able to redraw the component continually, and is this actually a valid approach?
To show lots of non-changing background shapes, draw them to a BufferedImage and then show that BufferedImage in the paintComponent(...) method. So while a shape is being drawn, draw it in paintComponent(...) but once the shape is done being drawn, perhaps on mouseRelease, then draw it in the background BufferedImage.
Note that what will slow your current drawing code the most may be your debugging SOP statements, but I assume that these will be removed from the finished code.
For example:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawingPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private static final Color DRAWING_COLOR = new Color(255, 100, 200);
private static final Color FINAL_DRAWING_COLOR = Color.red;
private BufferedImage backgroundImg;
private Point startPt = null;
private Point endPt = null;
private Point currentPt = null;
public DrawingPanel() {
backgroundImg = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
Graphics g = backgroundImg.getGraphics();
g.setColor(Color.blue);
g.fillRect(0, 0, PREF_W, PREF_H);
g.dispose();
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseMotionListener(myMouseAdapter);
addMouseListener(myMouseAdapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (backgroundImg != null) {
g.drawImage(backgroundImg, 0, 0, this);
}
if (startPt != null && currentPt != null) {
g.setColor(DRAWING_COLOR);
int x = Math.min(startPt.x, currentPt.x);
int y = Math.min(startPt.y, currentPt.y);
int width = Math.abs(startPt.x - currentPt.x);
int height = Math.abs(startPt.y - currentPt.y);
g.drawRect(x, y, width, height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void drawToBackground() {
Graphics g = backgroundImg.getGraphics();
g.setColor(FINAL_DRAWING_COLOR);
int x = Math.min(startPt.x, endPt.x);
int y = Math.min(startPt.y, endPt.y);
int width = Math.abs(startPt.x - endPt.x);
int height = Math.abs(startPt.y - endPt.y);
g.drawRect(x, y, width, height);
g.dispose();
startPt = null;
repaint();
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mouseDragged(MouseEvent mEvt) {
currentPt = mEvt.getPoint();
DrawingPanel.this.repaint();
}
#Override
public void mouseReleased(MouseEvent mEvt) {
endPt = mEvt.getPoint();
currentPt = null;
drawToBackground();
}
#Override
public void mousePressed(MouseEvent mEvt) {
startPt = mEvt.getPoint();
}
}
private static void createAndShowGui() {
DrawingPanel mainPanel = new DrawingPanel();
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_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();
}
});
}
}

Categories