I might be doing something really stupid and can't see it but I've been trying to animate the resizing of a rectangle to be used for a bar graph. The code complies but nothing is showing up in my frame. Its completely blank.
package server2;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Bar extends JPanel implements Runnable {
int height = 0;
Color barColor;
Rectangle bar;
Thread bartender;
public Bar(int x, int y, Color barColor) {
this.barColor=barColor;
this.bar = new Rectangle(x, y, 15, this.height);
this.bartender= new Thread(this);
}
public void update() {
bar.setSize(this.bar.width, this.bar.height++);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(this.barColor);
g2d.fill(this.bar);
}
public void callBarTender() {
this.bartender.notify();
}
#Override
public void run() {
for(int i = this.bar.height; i<this.height; i++ ) {
try {
update();
repaint();
Thread.sleep(30);
} catch(Exception e) {
System.out.println(e);
}
}
try {
this.bartender.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Bar bar1 = new Bar(200,200, Color.blue);
bar1.setVisible(true);
frame.add(bar1);
bar1.height = 10;
bar1.bartender.start();
}
Change...
bar.setSize(this.bar.width, this.bar.height++);
to
bar.setSize(this.bar.width, this.bar.height + 1);
or
bar.setSize(this.bar.width, ++this.bar.height);
The assignment of this.bar.height++ is a post assignment which is getting ignored
You should also switch the order of these two lines...
bar1.setVisible(true);
frame.add(bar1);
so setVisible is called AFTER you've prepared the UI.
You should also consider using a Swing Timer which is safer for updating the UI or any data the UI might rely on for painting
Related
I am new to Java and need help. I am making a GUI for an application using images made from photoshop, and want to create a menu using images which highlights when user hover mouse over them. I have tried mouseEntered(); method by getting mouse x, y co-ordinates but it's not working. Here is the code.
public class GUI extends JComponent{
public void paintComponent(Graphics g){
super.paintComponent(g);
ImageIcon exitBtnImg = new ImageIcon("src/images/userInterface/exitBtn.png");
g.drawImage(exitBtnImg.getImage(), 0, 5, this);
mouseHandler handler = new mouseHandler();
addMouseListener(handler);
}
}
public class mouseHandler implements MouseListener{
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
if((e.getX()>100&&e.getX()<300)&&(e.getY()>50&&e.getY()<196)){
repaint();
}
}
#Override
public void mouseExited(MouseEvent e) {
}
}
Don't load resources in paintComponent, your paint method should run as fast as it can
Don't add listeners within the paint methods, this will get called a lot, so you're just repeatedly adding new listeners each time your component is painted
Use a MouseMotionListener instead of a MouseListener, you want the mouseMoved event
You need some way to know where the image is painted, so you can determine if the mouse moved within it's bounds.
Have a look at How to Write a Mouse-Motion Listener and Painting in AWT and Swing for more details.
This example uses a simple Rectangle to define the location that the image is painted within, when the mouse moves within the confines of that Rectangle, a flag is set and the component is repainted, which paints a alpha based highlight effect over the image
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Example {
public static void main(String[] args) {
new Example();
}
public Example() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
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);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage img;
private Rectangle drawRectangle;
private boolean highlight = false;
public TestPane() throws IOException {
img = ImageIO.read(...);
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
highlight = drawRectangle.contains(e.getPoint());
repaint();
}
});
int width = getPreferredSize().width;
int height = getPreferredSize().height;
int x = (width - img.getWidth()) / 2;
int y = (height - img.getHeight()) / 2;
drawRectangle = new Rectangle(x, y, img.getWidth(), img.getHeight());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(img, drawRectangle.x, drawRectangle.y, this);
if (highlight) {
g2d.setColor(Color.RED);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(drawRectangle);
}
g2d.dispose();
}
}
}
Now, having said all that, you might be better off using the rollover capabilities of JButton, which will basically do the same thing.
See How to Use Buttons, Check Boxes, and Radio Buttons for more details
So basically I have some code I was working on a couple of days ago that is kind of like Paint, which allows you to essentially draw on the screen using the mouse. I kind of discovered this property by accident, and I realized that it is really inefficient and i'm wondering if there is a more practical way to do this. There isn't really any reason to give all of my code, but here are the important parts
private static void createAndShowGui() {
SimpleDraw mainPanel = new SimpleDraw();
MenuBar.createMenuBar();
JLabel label = new JLabel();
label.setText("Drawing prototype 0.0.1");
// label.setHorizontalTextPosition(JLabel.NORTH);
label.setFont(new Font("Serif", Font.BOLD, 20));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(),BoxLayout.PAGE_AXIS));
frame.setVisible(true);
frame.setJMenuBar(MenuBar.getMenuBar());
frame.setBackground(Color.WHITE);
frame.add(label);
The code block above sets up the jframe (the window)
#Override
public void mouseDragged(MouseEvent e)
{
// These console outputs are just so that I know what is happening
System.out.println("Event: MOUSE_DRAG");
System.out.println(e.getX());
System.out.println(e.getY());
System.out.println(e.getComponent());
System.out.println(e.getWhen());
System.out.println(e.getButton());
MOUSE_X = e.getX() - 5; //-5 so that the cursor represents the center of the square, not the top left corner.
MOUSE_Y = e.getY() - 5; //^
rect = new Rectangle(MOUSE_X, MOUSE_Y, 10, 10 ); //This doesn't ever come into action.
repaint();
}
The code above pretty much just sets the MOUSE_X and MOUSE_Y variables and the repaint(); method
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
if (rect != null) {
if (!colorChoice.equals("Default"))
{
g2.setColor(Color.BLACK);
}
switch(colorChoice) {
case "GRAY":
g2.setColor(Color.GRAY);
break;
case "CYAN":
g2.setColor(Color.CYAN);
break;
case "BLUE":
g2.setColor(Color.BLUE);
break;
case "RED":
g2.setColor(Color.RED);
break;
case "PINK":
g2.setColor(Color.PINK);
break;
case "YELLOW":
g2.setColor(Color.YELLOW);
break;
case "GREEN":
g2.setColor(Color.GREEN);
break;
case "PURPLE":
g2.setColor(Color.MAGENTA);
break;
case "RESET":
g2.setColor(Color.WHITE);
case "WHITE":
g2.setColor(Color.WHITE);
}
g2.fillRect(MOUSE_X, MOUSE_Y, 15, 15);
if (colorChoice.equals("RESET"))
resetColorOnCursor();
}
}
public static void clearBoard()
{
tempColor = colorChoice;
setColorChoice("RESET");
frame.repaint();
}
public static void resetColorOnCursor()
{
setColorChoice(tempColor);
}
This is the thing I came across accidentally. What I was trying to do when I found this out was basically make a square follow your cursor whenever you moved your mouse. But I forgot to type the code part paintComponent(g);, which turns this program into the thing that I originally intended. The bottom parts of this are essentially how I would clear the board. I'm 100% sure that this isn't the proper way to clear/reset a frame like this, but I couldn't find another way. If anyone has any tips or better methods to use to do this properly I would be very appreciative. Thanks! :D
You're current approach is basically breaking the requirements of the paint chain, by not calling super.paintComponent. The paintComponent method does a set of operations, which you are not taking over and which could result in some very weird paint artifacts which are difficult to replicate consistently.
Graphics is a shared resource, so the Graphics context which was used to paint some other control will be the same which is used to paint your component, unless you are "cleaning" the context before hand, what was previously painted to the context will remain (which is why you code currently "seems" to work).
Instead, you should use a MouseListener to define a anchor point, which represents the point at which the mouse was pressed and then use the MouseMotionListener to define the extent of the selection area, for example...
import java.awt.AlphaComposite;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
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 SelectExample {
public static void main(String[] args) {
new SelectExample();
}
public SelectExample() {
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 class TestPane extends JPanel {
private Rectangle selection;
public TestPane() {
MouseAdapter ma = new MouseAdapter() {
private Point clickPoint;
#Override
public void mousePressed(MouseEvent e) {
clickPoint = e.getPoint();
selection = null;
}
#Override
public void mouseDragged(MouseEvent e) {
Point dragPoint = e.getPoint();
int x = Math.min(clickPoint.x, dragPoint.x);
int y = Math.min(clickPoint.y, dragPoint.y);
int width = Math.max(clickPoint.x, dragPoint.x) - x;
int height = Math.max(clickPoint.y, dragPoint.y) - y;
if (selection == null) {
selection = new Rectangle(x, y, width, height);
} else {
selection.setBounds(x, y, width, height);
}
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
selection = null;
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (selection != null) {
g.setColor(UIManager.getColor("List.selectionBackground"));
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(selection);
g2d.dispose();
g2d = (Graphics2D) g.create();
g2d.draw(selection);
g2d.dispose();
}
}
}
}
Just to highlight the issue you will face if you continue to violate the requirements of the paintComponent method, this is what happens when I don't call super.paintComponent
I simply added two JButton's to the JFrame (so not even directly to the panel). paintComponent does a series of important jobs, which you neglected to perform, which is going to cause more problems and issues.
Free form line example...
A free form line is actually a illusion, it's a series of (small) lines drawn between a series of points, the reason for this is because the MouseListener won't report every mouse position it moves across, depending on the speed the mouse is moved, you might get lots of call backs or a few.
So, instead of drawing to just draw the points, we store the points in a List and draw lines between them, for example...
import java.awt.AlphaComposite;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FreeFormLines {
public static void main(String[] args) {
new FreeFormLines();
}
public FreeFormLines() {
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 class TestPane extends JPanel {
private List<List<Point>> points;
public TestPane() {
points = new ArrayList<>(25);
MouseAdapter ma = new MouseAdapter() {
private List<Point> currentPath;
#Override
public void mousePressed(MouseEvent e) {
currentPath = new ArrayList<>(25);
currentPath.add(e.getPoint());
points.add(currentPath);
}
#Override
public void mouseDragged(MouseEvent e) {
Point dragPoint = e.getPoint();
currentPath.add(dragPoint);
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
currentPath = null;
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (List<Point> path : points) {
Point from = null;
for (Point p : path) {
if (from != null) {
g2d.drawLine(from.x, from.y, p.x, p.y);
}
from = p;
}
}
g2d.dispose();
}
}
}
This is a simple example for a practical paint Application, where you can control and change the size and the Color of your drawing.
public class Main extends Application{
#Override
public void start(Stage stage){
try{
g = can.getGraphicsContext2D();
g.setStroke(Color.BLACK);
g.setLineWidth(1);
c.setValue(Color.BLACK);
c.setOnAction(e->{
g.setStroke(c.getValue());
});
sli.setMin(1);
sli.setMax(100);
sli.setShowTickLabels(true);
sli.setShowTickMarks(true);
sli.valueProperty().addListener(e->{
double val = sli.getValue();
String str = String.format("%.1f", val);
lab.setText(str);
g.setLineWidth(val);
});
gri.addRow(0, c, sli, lab);
gri.setHgap(20);
gri.setAlignement(Pos.TOP_CENTER);
gri.setPadding( new Insets( 20, 0, 0, 0));
scene.setOnMousePressed(e->{.
g.beginPath();
g.lineTo(e.getSceneX(), e.getSceneY());
g.stroke();
});
scene.setOnMoudrDragged(e->{.
g.lineTo(e.getSceneX(), e.getSceneY());
g.stroke();
});
pan.getChildren().addAll(can, gri);
stage.setScene(scene);
stage.show();
}catch(Exception e){
e.printStrackTrace();
}
Canvas can = new Canvas(760, 490);
GraphicsContext g ;
ColorPicker c = new ColorPicker();
Slider sli = new Slider();
Label lab = new Label("1.0");
GridPane gri = new GridPane();
StackPane pan = new StackPane();
Scene scene = new Scene(pan, 760, 490);
public static void main (String [] args){
launch(args);
}
}
Or we can try drawing for only java code , I think it's so easy and powerful.
package drawingbymouse;
import java.awt.*;
import java.awt.event.*;
public class DrawingByMouse extends Frame
implements MouseMotionListener{
DrawingByMouse(){
addMouseMotionListener(this);
setSize(400, 400);
setLayout(null);
setVisible(true);
}
#Override
public void mouseDragged(MouseEvent e){
Graphics g = getGraphics();
g.setColor(Color.BLACK);
g.fillOval(e.getX(), e.getY(), 10, 10);
}
public void mouseMoved(MouseEvent e){
}
public static void main (String[]args){
new DrawingByMouse();
}
}
I'm trying to make a paint program, but I'm having trouble with drawing lines when dragging the mouse. It appears that the paint keeps on refreshing and so it only draws the current position of my mouse.
I a bit new to this, so how would I be able to get all the lines to show on the JPanel when dragging the mouse? Thanks, and here is what I have:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;
public class DrawingPanel extends JPanel{
Point start;
Point end;
static Color c = Color.black;
DrawingPanel(){
addMouseMotionListener(new ml());
addMouseListener(new ml());
}
public class ml implements MouseMotionListener, MouseListener{
public void mouseMoved(MouseEvent ev){}
public void mousePressed(MouseEvent e){
end = e.getPoint();
}
public void mouseDragged(MouseEvent e){
start = end;
end=e.getPoint();
repaint();
}
public void mouseReleased(MouseEvent e){
start=null;
end=null;
}
public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(c);
if(start!=null){
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(5));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawLine(start.x, start.y, end.x, end.y);
}
}
}
There are multiple ways to solve your problem. #MattiasF and #MadProgrammer are correct: Swing is doing what it should be doing. Your paintComponent method is supposed to repaint the entire scene, not add onto the previous one.
The solutions suggested so far result in an application that is doing vector graphics: you are remembering the original drawing operations, and on every paint, you are executing every one of them (Java2D optimises some of it, because it will not really redraw the areas that are not currently visible on the screen, but it also takes time to figure out which areas are visible and which ones not)
The advantages are that you can scale the drawing operations perfectly if you need a bigger or smaller image. The disadvantages are that it may be slower once you have stored many drawing operations, and that you cannot (easily) do bitmap manipulations.
Another approach to this is the bitmap approach. You build up a bitmap of your drawing so far in memory, and in the paintComponent method you draw the bitmap onto the screen.
The advantage is that it's usually faster. It also allows bitmap operations, and it is also often easier to program against this model as you can just draw when you need it, rather than build op a list of drawing operations in memory. The disadvantages are that it uses more memory (until you have many drawing operations) and that you cannot perfectly scale your image up and down anymore.
To make your example work with a bitmap kept in memory, add the fields image and imageGraphics into your class, and replace your mouse listener ml as well as the paintComponent method with the code below:
private BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
private Graphics2D imageGraphics = image.createGraphics();
public class ml extends MouseAdapter implements MouseMotionListener, MouseListener {
public void mousePressed(MouseEvent e) {
end = e.getPoint();
}
public void mouseDragged(MouseEvent e) {
start = end;
end = e.getPoint();
imageGraphics.setColor(c);
imageGraphics.setStroke(new BasicStroke(5));
imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
imageGraphics.drawLine(start.x, start.y, end.x, end.y);
repaint();
}
public void mouseReleased(MouseEvent e) {
start = null;
end = null;
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, null, 0, 0);
}
You will immediately see the problem with scaling. The bitmap buffer is 500x500 pixels, and anything outside it will not be drawn. This is basically the same way that Microsoft paint works: you need to know the canvas size before you start drawing.
You have done almost everything right. The reason for your program only showing the current mouse position is because you don't save the starting position on mousePressed. Try replacing:
public void mousePressed(MouseEvent e){
end = e.getPoint();
}
with:
public void mousePressed(MouseEvent e){
start = e.getPoint();
}
And also:
public void mouseDragged(MouseEvent e){
start = end;
end=e.getPoint();
repaint();
}
with:
public void mouseDragged(MouseEvent e){
end = e.getPoint();
repaint();
}
That will get it working to draw one line. If you want more lines you can just add each finished line to a list in mouseReleased. Try adding this to your DrawingPanel class:
private ArrayList<Point> points = new ArrayList<Point>();
also replace this:
public void mouseReleased(MouseEvent e){
start = null;
end = null;
}
with:
public void mouseReleased(MouseEvent e){
points.add(start);
points.add(end);
start = null;
end = null;
}
And also replace:
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(c);
if(start!=null){
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(5));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawLine(start.x, start.y, end.x, end.y);
}
}
with:
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(c);
g2.setStroke(new BasicStroke(5));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//Draw all previous lines
for (int i = 0; i < points.size(); i+=2) {
g2.drawLine(points.get(i).x, points.get(i).y, points.get(i+1).x, points.get(i+1).y);
}
//Draw the current line if there is one
if(start != null && end != null){
g2.drawLine(start.x, start.y, end.x, end.y);
}
}
Now we save each finished line in the list, each even index is the start of a line and each odd is the end of a line. If you want to be able to draw "curves" that looks exactly like you moved the mouse you have to change this code a bit, but at least this should give you something to work with. Just submit an additional question if you need further help.
It appears that the paint keeps on refreshing
Yes, that's how painting works, take a look at Painting in AWT and Swing for more details
As a possible solution, you could add each new Point a List and simply draw a line between each point within the paintComponent method by iterating over the List.
You could also create a Shape and draw the lines within in and paint this shape within the paintComponent method.
Take a look at 2D Graphics for more details
Example for drawing single lines
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawLine {
public static void main(String[] args) {
new DrawLine();
}
public DrawLine() {
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 class TestPane extends JPanel {
private Point startPoint, endPoint;
private List<Point[]> lines;
public TestPane() {
lines = new ArrayList<>(25);
MouseAdapter ma = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
endPoint = e.getPoint();
Point[] points = new Point[]{startPoint, endPoint};
lines.add(points);
startPoint = null;
endPoint = null;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
endPoint = e.getPoint();
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
for (Point[] p : lines) {
g2d.drawLine(p[0].x, p[0].y, p[1].x, p[1].y);
}
if (startPoint != null && endPoint != null) {
g2d.setColor(Color.RED);
g2d.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
}
g2d.dispose();
}
}
}
Example for drawing multiple, connected lines
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawLine {
public static void main(String[] args) {
new DrawLine();
}
public DrawLine() {
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 class TestPane extends JPanel {
private List<Point> points;
private Point mousePoint;
public TestPane() {
points = new ArrayList<>(25);
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
points.add(e.getPoint());
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
Point startPoint = null;
for (Point p : points) {
if (startPoint == null) {
startPoint = p;
} else {
g2d.drawLine(startPoint.x, startPoint.y, p.x, p.y);
startPoint = p;
}
}
if (startPoint != null) {
g2d.setColor(Color.RED);
g2d.drawLine(startPoint.x, startPoint.y, mousePoint.x, mousePoint.y);
}
g2d.dispose();
}
}
}
I have a Java paint program, and I've got two problems to do with it. Both problems are relatively simple, and just regard how the mouse input is handled and how the image uses colors. Here's a photo of the app:
So here's my first problem:
As you can see, by the look of the app, there's a spray of dots on the paint area. Each of those dots is a mouseclick. The program does not recognize when a user is holding down the mouse button, so you have to click individually.
This is obviously counterproductive, user-unfriendly and unacceptable. Now, how I fix this, I'm not sure. I've tried using a permanent while (true) loop, but that does not work. How do I make it so that instead of having to click every time, each time the mouse is held down it sprays out dots?
The second problem is the color of the dots. As you can see, at the bottom, there are color buttons. These function, but there is a problem: Whenever I change the color, all the dots currently on the screen change color. The color is run by a variable called currentColor which is run by the actionListeners controlled by all the color buttons on the bottom panel. How do I make sure that colors already placed on the screen are not affected anymore?
I believe that all the code that can be fixed for these two problems lies in my custom JPanel which is used for the program to paint on. I'll post the entire class below, and if you have any other questions, please let me know.
int xCord, yCord;
public class PaintPanel extends JPanel implements MouseListener {
// default serial whatever...
private static final long serialVersionUID = -6514297510194472060L;
public PaintPanel() {
addMouseListener(this);
}
ArrayList<Point> points = new ArrayList<Point>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Point point : points) {
g.setColor(currentColor);
g.fillOval(point.x, point.y, 12, 12);
}
repaint();
}
#Override
public void mouseClicked(MouseEvent m) {
}
#Override
public void mouseEntered(MouseEvent m) {
}
#Override
public void mouseExited(MouseEvent m) {
}
#Override
public void mousePressed(MouseEvent m) {
if (paintPanel.contains(m.getPoint())) {
points.add(m.getPoint());
xCord = m.getX();
yCord = m.getY();
System.out.println("x: " + xCord + " y: " + yCord);
}
}
#Override
public void mouseReleased(MouseEvent m) {
}
}
Painting in Swing is destructive.
That is to say, when Swing requests that a repaint occur on a component, the component is expected to clear what ever was previously paint and update itself.
The problem with your color issue is that you only ever have a single color specified.
A possible solution would be to paint to backing buffer (like BufferedImage) instead of relying on paintComponent.
Instead of repainting all the dots each time paintComponent is called, you would simply paint the BufferedImage instead.
As to your issue with the mouse, you need to implement a MouseMotionListener, this will allow you to detect when the mouse is dragged across the surface, painting a trail of dots
Update with very BASIC example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimplePaint04 {
public static void main(String[] args) {
new SimplePaint04();
}
public SimplePaint04() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private PaintPane paintPane;
public TestPane() {
setLayout(new BorderLayout());
add((paintPane = new PaintPane()));
add(new ColorsPane(paintPane), BorderLayout.SOUTH);
}
}
public class ColorsPane extends JPanel {
public ColorsPane(PaintPane paintPane) {
add(new JButton(new ColorAction(paintPane, "Red", Color.RED)));
add(new JButton(new ColorAction(paintPane, "Green", Color.GREEN)));
add(new JButton(new ColorAction(paintPane, "Blue", Color.BLUE)));
}
public class ColorAction extends AbstractAction {
private PaintPane paintPane;
private Color color;
private ColorAction(PaintPane paintPane, String name, Color color) {
putValue(NAME, name);
this.paintPane = paintPane;
this.color = color;
}
#Override
public void actionPerformed(ActionEvent e) {
paintPane.setForeground(color);
}
}
}
public class PaintPane extends JPanel {
private BufferedImage background;
public PaintPane() {
setBackground(Color.WHITE);
setForeground(Color.BLACK);
MouseAdapter handler = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
drawDot(e.getPoint());
}
#Override
public void mouseDragged(MouseEvent e) {
drawDot(e.getPoint());
}
};
addMouseListener(handler);
addMouseMotionListener(handler);
}
protected void drawDot(Point p) {
if (background == null) {
updateBuffer();;
}
if (background != null) {
Graphics2D g2d = background.createGraphics();
g2d.setColor(getForeground());
g2d.fillOval(p.x - 5, p.y - 5, 10, 10);
g2d.dispose();
}
repaint();
}
#Override
public void invalidate() {
super.invalidate();
updateBuffer();
}
protected void updateBuffer() {
if (getWidth() > 0 && getHeight() > 0) {
BufferedImage newBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = newBuffer.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, getWidth(), getHeight());
if (background != null) {
g2d.drawImage(background, 0, 0, this);
}
g2d.dispose();
background = newBuffer;
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (background == null) {
updateBuffer();
}
g2d.drawImage(background, 0, 0, this);
g2d.dispose();
}
}
}
I'm trying to create a simple game using AWT but I want to have some JButtons aswell to exit/reset the game. The problem is, I want the BufferedImage to be drawn inside the visible frame like so in my container I have this at the end:
g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
My problem is, when I add a JButton to that frame, it only detects rollover in space that doesn't take into account the offsetting, but is drawn in a space that does. This is the relevant code (con is the container).
private void addButtons()
{
reset = new JButton("reset");
reset.setBounds(180,460, 75,30);
reset.addActionListener( this );
con.add(reset);
exit = new JButton("exit");
exit.setBounds(290,460, 60,30);
exit.addActionListener( this );
con.add(exit);
con.repaint();
}
The paint method in the Container
public void paint(Graphics g)
{
bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics b = bf.getGraphics();
b.setColor(Color.GRAY);
b.fillRect(0, 0, this.getWidth(), this.getHeight());
b.setColor(Color.BLACK);
b.drawRect(0,0,420,420);
super.paint(b);
g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
}
How can I make the button be drawn and detected in the same spot?
here is a screenshot of the problem
As requested:
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class Draw implements ActionListener{
private SnakeFrame frame;
private SnakeCon con;
JButton reset, exit;
private boolean res;
public Draw()
{
frame = new SnakeFrame("Snake");
frame.setResizable(false);
frame.setLayout(null);
frame.setSize(600,600);
frame.setVisible(true);
con = new SnakeCon();
con.setBounds(0,0,600,600);
frame.add(con);
}
private void addButtons()
{
reset = new JButton("reset");
reset.setBounds(180,460, 75,30);
reset.addActionListener( this );
con.add(reset);
exit = new JButton("exit");
exit.setBounds(290,460, 60,30);
exit.addActionListener( this );
con.add(exit);
con.repaint();
}
public void run()
{
addButtons();
res = false;
boolean dead = false;
while(!dead)
{
if( (res) )
dead = true;
if (!dead)
{
try{
Thread.sleep(100);
}
catch (Exception e)
{}
frame.repaint();
}
}
con.removeAll();
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == reset)
res = true;
else if (e.getSource() == exit)
System.exit(0);
}
}
--
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class SnakeCon extends Container{
private BufferedImage bf;
public SnakeCon()
{
super();
setBounds(0,0,600,600);
}
public void paint(Graphics g)
{
bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics b = bf.getGraphics();
b.setColor(Color.GRAY);
b.fillRect(0, 0, this.getWidth(), this.getHeight());
b.setColor(Color.BLACK);
b.drawRect(0,0,420,420);
super.paint(b);
g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
}
public void update(Graphics g)
{
paint(g);
}
}
--
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class SnakeFrame extends Frame implements WindowListener{
private BufferedImage bf;
public SnakeFrame(String s)
{
super(s);
addWindowListener( this );
}
public void paint(Graphics g)
{
bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics b = bf.getGraphics();
super.paint(b);
g.drawImage(bf,0,0,null);
}
public void update(Graphics g)
{
paint(g);
}
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
public void windowClosed(WindowEvent e) { }
public void windowOpened(WindowEvent e) { }
public void windowIconified(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { }
public void windowActivated(WindowEvent e) { }
public void windowDeactivated(WindowEvent e) { }
}
--
public class Main {
public static void main(String[] args)
{
boolean never = false;
Draw d = new Draw();
while(!never)
{
d.run();
}
System.exit(0);
}
}
Im not sure exactly what is wrong/what you want (why do you have a loop to constantly drawing buttons and invoking removeAll()? etc but i cant shake the feeling it might be implemented in a more readable/efficient way)...
But here are some suggestions that can only help your code get better:
Dont use null/Absolute Layout choose an appropriate LayoutManager.
Do not override JFrame paint(..), rather add JPanel to JFrame and override paintComponent(Graphics g) of JPanel and do drawing there.(Do not forget to have super.paintComponent(..) as 1st call in overriden paintComponent method. See here for more: Performing Custom Painting
Do not set JFrame visible before adding all components to JFrame
Always create and manipulate Swing components on Event Dispatch Thread like so:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//create Swing components
}
});
Do not do long running tasks on Event Dispatch Thread rather use Swing Timer/Swing Worker
Do not call setSize(..) on JFrame rather override getPreferredSize() of JPanel and return Dimensions which fit all components (see here for reasoning), than call pack() on JFrame before setting it visible
Dont extend JFrame unnecessarily or Container!
Adding WindowListener for detecting JFrame exit is not worth the lines rather use:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
to exit Swing application when X is pressed.