How to avoid flickering when calling repaint() in Java? - java

I have looked at many answers to questions similar to this, but they all seem to say to draw onto a JPanel, which I am doing, so I don't understand how to make my rectangle not flicker.
I am making a Screenshot maker, like the CMD+SHIFT+4 feature on macs. I have everything working, i.e. it takes pictures when you drag a rectangle, but the rectangle that you select flickers the whole time.
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.BufferStrategy;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.File;
import java.io.PrintWriter;
import javax.swing.*;
import javax.swing.event.*;
import javax.imageio.ImageIO;
import java.text.SimpleDateFormat;
import java.util.*;
import java.awt.geom.Area;
public class ScreenShotter extends JPanel implements MouseListener {
static JPanel contentPane;
private static JPanel picture1;
static int startX, startY, endX, endY;
static int width;
static int height;
int x, y;
int radius = 2;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd kk.mm.ss");
static File f = new File("ScreenShotter.txt");
static String filePath = f.getPath();
static String screenshotPath = "";
JFileChooser chooser;
String chooserTitle = "Select the folder to store your screenshots";
static Point currentPoint;
static Point startPoint;
static boolean clicked = false;
static BufferStrategy bs;
public ScreenShotter() {
setLayout(new BorderLayout());
contentPane = new JPanel(new BorderLayout());
contentPane.requestFocus();
picture1 = new JPanel();
picture1.addMouseListener(this);
picture1.setPreferredSize(new Dimension(width, height));
contentPane.add(picture1);
}
public ScreenShotter(boolean bool){
final String[] labels = {"Screen width: ", "Screen Height: "};
int labelsLength = labels.length;
final JTextField[] textField = new JTextField[labelsLength];
final JPanel p = new JPanel(new SpringLayout());
for(int i = 0; i < labelsLength; i++){
JLabel l = new JLabel(labels[i], JLabel.TRAILING);
p.add(l);
textField[i] = new JTextField(10);
l.setLabelFor(textField[i]);
p.add(textField[i]);
}
final JButton button = new JButton("Submit");
p.add(new JLabel());
p.add(button);
SpringUtilities.makeCompactGrid(p,
labelsLength + 1, 2,
7, 7,
7, 7);
final JFrame frame = new JFrame("File Maker");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//for(int i = 0; i < labels.length; i++){
//System.out.println(labels[i]+"->"+textField[i].getText());
//screenshotPath = textField[0].getText();
width = Integer.parseInt(textField[0].getText());
height = Integer.parseInt(textField[1].getText());
//}
chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
chooser.setDialogTitle(chooserTitle);
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
if(chooser.showOpenDialog(button) == JFileChooser.APPROVE_OPTION){
System.out.println("getCurrentDirectory(): " + chooser.getCurrentDirectory());
System.out.println("getSelectedFile(): " + chooser.getSelectedFile());
screenshotPath = slashConverter(chooser.getSelectedFile().toString());
} else {
System.out.println("No selection.");
}
frame.dispose();
createFile();
}
});
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
p.setOpaque(true);
frame.setContentPane(p);
frame.pack();
frame.setSize(210, 123);
frame.setResizable(false);
frame.setVisible(true);
}
#Override
public Dimension getPreferredSize(){
return new Dimension(210, 123);
}
public static void main(String[] args){
try {UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName());}
catch (UnsupportedLookAndFeelException e) {}
catch (ClassNotFoundException e) {}
catch (InstantiationException e) {}
catch (IllegalAccessException e) {}
if(f.exists()){
width = getWidthFromFile(filePath);
height = getHeightFromFile(filePath);
startScreenShotter();
}
if(!f.exists()){
JFrame fileMaker = new JFrame("File Maker");
fileMaker.add(new ScreenShotter(true));
}
}
public static void startScreenShotter(){
JFrame frame = new JFrame("ScreenShotter");
frame.add(new ScreenShotter());
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.setOpacity(0.33f);
frame.setContentPane(contentPane);
frame.setVisible(true);
frame.addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent ke) {
if(ke.getKeyCode() == ke.VK_ESCAPE){
System.exit(0);
}
}
public void keyReleased(KeyEvent ke) {}
public void keyTyped(KeyEvent ke) {}
});
while(true){
currentPoint = MouseInfo.getPointerInfo().getLocation();
if(currentPoint != startPoint && clicked){
drawRectangle(startPoint, currentPoint, picture1);
}
//System.out.println(currentPoint);
//delay(1);
}
}
public static void drawRectangle(Point start, Point current, Object source){
if(source instanceof JPanel){
picture1.repaint();
//delay(1);
Rectangle r = new Rectangle(rectChecker(start.x, start.y, current.x, current.y));
Graphics g = ((JComponent) source).getGraphics();
g.fillRect((int)r.getX(), (int)r.getY(), (int)r.getWidth(), (int)r.getHeight());
g.setColor(new Color(255, 255, 255, 50));
//delay(1);
}
}
public static void delay(int time){
try{
Thread.sleep(time);
} catch(InterruptedException e){
System.out.println("wot");
}
}
public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();
//drawCircle(e.getX()-(radius/2), e.getY()-(radius/2), e.getSource(), true);
//repaint();
startX = x;
startY = y;
startPoint = new Point(startX, startY);
clicked = true;
//System.out.println("(" + startX + ", " + startY + ")");
}
public void mouseReleased(MouseEvent e) {
x = e.getX();
y = e.getY();
//drawCircle(e.getX()-(radius/2), e.getY()-(radius/2), e.getSource(), true);
//repaint();
endX = x;
endY = y;
//System.out.println("(" + endX + ", " + endY + ")");
Frame[] f = Frame.getFrames();
for(int i = 0; i < f.length; i++){
f[i].dispose();
}
try {
robo(rectChecker(startX, startY, endX, endY));
System.exit(0);
} catch(Exception ex){
System.exit(1);
}
}/*
public void drawCircle(int x, int y, Object source, boolean fill) {
if(source instanceof JPanel) {
Graphics g = ((JComponent) source).getGraphics();
g.drawOval(x - radius, y - radius, 2 * radius, 2 * radius);
g.setColor(Color.RED);
if (fill) {
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
} // else ignore
}*/
public void robo(Rectangle r) throws Exception{
Calendar now = Calendar.getInstance();
Robot robot = new Robot();
BufferedImage screenshot = robot.createScreenCapture(r);
ImageIO.write(screenshot, "PNG", new File(getPath(filePath) + "Screenshot "+formatter.format(now.getTime())+".png"));
}
public static String getPath(String file){
if(f.exists()){
try(BufferedReader br = new BufferedReader(new FileReader(file))){
String sCurrentLine;
while((sCurrentLine = br.readLine()) != null){
return sCurrentLine;
}
} catch(IOException e){
e.printStackTrace();
}
return null;
}
return null;
}
public static int getWidthFromFile(String file){
if(f.exists()){
try(BufferedReader br = new BufferedReader(new FileReader(file))){
br.readLine();
int width = Integer.parseInt(br.readLine());
return width;
} catch(IOException e){
e.printStackTrace();
}
}
return 0;
}
public static int getHeightFromFile(String file){
if(f.exists()){
try(BufferedReader br = new BufferedReader(new FileReader(file))){
br.readLine();
br.readLine();
int height = Integer.parseInt(br.readLine());
return height;
} catch(IOException e){
e.printStackTrace();
}
}
return 0;
}
public static Rectangle rectChecker(int x0, int y0, int x1, int y1){
if(x0 > x1 && y0 < y1){
int x = x1;
int y = y0;
int width = x0 - x1;
int height = y1 - y0;
Rectangle r = new Rectangle(x, y, width, height);
return r;
}
if(x0 < x1 && y0 > y1){
int x = x0;
int y = y1;
int width = x1 - x0;
int height = y0 - y1;
Rectangle r = new Rectangle(x, y, width, height);
return r;
}
if(x0 > x1 && y0 > y1){
int x = x1;
int y = y1;
int width = x0 - x1;
int height = y0 - y1;
Rectangle r = new Rectangle(x, y, width, height);
return r;
}
int x = x0;
int y = y0;
int width = x1 - x0;
int height = y1 - y0;
Rectangle r = new Rectangle(x, y, width, height);
return r;
}
public static void createFile(){
System.out.println(width + " " + height + " " + filePath);
try(PrintWriter writer = new PrintWriter(filePath, "UTF-8")){
writer.println(screenshotPath);
writer.println(width);
writer.println(height);
writer.close();
} catch(IOException ioe){
System.out.println("Call a doctor!");
}
startScreenShotter();
}
public static String slashConverter(String str){
if(str.contains("\\")){
str = str.replace("\\", "/");
if(str.charAt(str.length()-1) != '/'){
str = str + "/";
}
return str;
}
return str;
}
//public void mousePressed(MouseEvent e) {}
//public void mouseReleased(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}

Painting in Swing is typically done from within the context of the paintComponent method inherited from JComponent. This ensures that when a paint cycle occurs, what is painted is double buffered, which will prevent flickering during the paint process.
Painting in Swing is control by the RepaintManager, it is it's responsiblility to determine when and what should be painted. You can make requests to the RepaintManager to perform a paint update by calling one of the repaint methods and it may schedule an update for some time in the future
Using...
Graphics g = ((JComponent) source).getGraphics();
Is a really bad idea. It has the ability to return null and is nothing more then a snapshot of the Graphics context and will wiped clean when a proper paint cycle occurs.
Take a look at Performing custom painting and Painting in AWT and Swing for more details
This...
while (true) {
currentPoint = MouseInfo.getPointerInfo().getLocation();
if (currentPoint != startPoint && clicked) {
drawRectangle(startPoint, currentPoint, picture1);
}
//System.out.println(currentPoint);
//delay(1);
}
scares me, for two reason, first, it's not the recommended way to monitor mouse updates and two, are calling it within the same context you create your UI, which gives it the potential to "hang" your program
I'd also recommend that you take a look at the key bindings API over the use of KeyListeners, it will solve focus related issues...
You may also find Concurrency in Swing of assistance...

Related

How to prevent flushing when draw a rectangle in Java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;
class DrawningBoard extends Canvas implements MouseMotionListener{
private List<Point> points = new ArrayList<>();
private List<List<Point>> curves = new ArrayList<>();
private boolean isRectangle = false;
private int xr, yr, widthr, heightr;
public void setIsRectangle(boolean trueOrFalse){
isRectangle = trueOrFalse;
}
public boolean getIsRectangle(){
return isRectangle;
}
public DrawningBoard(){
setBackground(Color.MAGENTA);
addMouseMotionListener(this);
addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent e){
curves.add(points);
points = new ArrayList<>();
}
public void mousePressed(MouseEvent e){
points.add(new Point(e.getX(), e.getY()));
}
});
}
public void paint(Graphics g){
g.setColor(Color.CYAN);
if(isRectangle){
g.drawRect(xr, yr, widthr, heightr);
}
for(List<Point> curve : curves){
for(int i = 0; i < curve.size() - 1; i++){
g.drawLine((int)curve.get(i).getX(), (int)curve.get(i).getY(), (int)curve.get(i + 1).getX(), (int)curve.get(i + 1).getY());
}
}
}
public void mouseDragged(MouseEvent e){
Graphics g = getGraphics();
g.setColor(Color.CYAN);
int xx = e.getX();
int yy = e.getY();
int x = (int)points.get(points.size() - 1).getX();
int y = (int)points.get(points.size() - 1).getY();
int dx = xx-x;
int dy = yy-y;
if(isRectangle){
if(dx >= 0 && dy >= 0){
xr = x;
yr = y;
widthr = dx;
heightr = dy;
} else if(dx < 0 && dy < 0){
xr = xx;
yr = yy;
widthr = -dx;
heightr = -dy;
} else if(dx >= 0 && dy < 0){
xr = x;
yr = yy;
widthr = dx;
heightr = -dy;
} else if(dx < 0 && dy >= 0){
xr = xx;
yr = y;
widthr = -dx;
heightr = dy;
}
repaint();
}
else {
g.drawLine(xx, yy, (int)points.get(points.size() - 1).getX(), (int)points.get(points.size() - 1).getY());
points.add(new Point(xx, yy));
}
}
public void mouseMoved(MouseEvent e) { }
}
class GUI extends JFrame implements ActionListener{
private JPanel[] panel;
private JButton[] button;
private DrawningBoard board;
public GUI(String title){
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(416, 400);
board = new DrawningBoard();
board.setBounds(0,0, 400, 400);
panel = new JPanel[3];
panel[0] = new JPanel();
panel[1] = new JPanel();
panel[2] = new JPanel();
panel[0].setLayout(null);
panel[1].setLayout(null);
panel[2].setLayout(null);
panel[0].setBounds(0, 0, 400, 20);
panel[1].setBounds(0, 20, 400, 400);
panel[2].add(panel[0]);
panel[2].add(panel[1]);
panel[0].setBackground(Color.red);
button = new JButton[5];
button[0] = new JButton("Rectangle");
button[1] = new JButton("b1");
button[2] = new JButton("b2");
button[3] = new JButton("b3");
button[4] = new JButton("b4");
button[0].addActionListener(this);
button[3].addActionListener(this);
button[0].setBounds(0, 0, 100, 20);
button[1].setBounds(100, 0, 100, 20);
button[2].setBounds(200, 0, 100, 20);
button[3].setBounds(300, 0, 100, 20);
button[4].setBounds(0, 0, 100, 20);
panel[0].add(button[0]);
panel[0].add(button[1]);
panel[0].add(button[2]);
panel[0].add(button[3]);
panel[0].add(button[4]);
panel[1].add(board);
add(panel[2]);
show();
}
public void actionPerformed(ActionEvent e){
String command = e.getActionCommand();
if(command.equals("Rectangle"))
board.setIsRectangle(!board.getIsRectangle());
}
}
public class Paint{
public static void main(String []str){
new GUI("Paint");
}
}
I want to create a Paint application. If I draw multiple curves on the board and then I want to draw a rectangle (using drawRect(x, y, width, height)), those curves are repainted and results some flushing as result of use repaint() method. How can I avoid that flushing?
I tried to use update() method, but many rectangles are drawn when mouse dragging.
Swing is double buffered by default.
public void paint(Graphics g){
Don't override paint(). You have done this incorrectly and have lost the benefit of double buffering.
Instead, custom painting should be done by overriding the paintComponent(...) method. Read the section from the Swing tutorial on Custom Painting for more information and working examples.
You can also check out the DrawOnComponent example from Custom Painting Approaches for a more complete example that does what you want.

How to paint 2Dgraphics and images separately in the same class to the same JPanel

I have a method (drawImages) that draws an arraylist of bufferedimages to the extended JPanel. However, it is only being called in the paintComponent method when another method (drawPerfectRect) is called.
This method (drawPerfectRect) is responsible for using co-ordinates passed to it to draw a rectangle using points clicked on by the user.
Essentially, the two problems I am having are:
It does not paint the arraylist of bufferedimages until the user clicks and drags to create a rectangle (want the images to be painted at after selectDirectory (JButton) is clicked).
It also paints the bufferedimages again for every subsequent rectangle that is painted.
It seems that the bufferedimages are only painted when the rectangles are painted.
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.*;
import com.sun.tools.internal.ws.wsdl.document.Import;
import net.coobird.thumbnailator.*;
import com.mortennobel.imagescaling.*;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.geom.RoundRectangle2D;
import java.io.IOException;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.Buffer;
import java.util.ArrayList;
import java.util.Timer;
public class ImportWorkspace extends JPanel{
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int x, y, x2, y2;
ArrayList<BufferedImage> thumbnailList = new ArrayList<BufferedImage>();
int ContentPanelWidth;
private boolean addThumbnails = false;
public ImportWorkspace(){
x = y = x2 = y2 = 0;
setLayout(null);
setBackground(Color.decode("#2a2e37"));
setBounds(85, 0, screenSize.width-85, screenSize.height);
//System.out.println("W: " + super.getWidth() + ", H: " + super.getHeight());
ContentPanelWidth = getWidth();
System.out.println(ContentPanelWidth);
JLabel dataIcon = new JLabel(new ImageIcon(new ImageIcon ("folder.png").getImage().getScaledInstance(256,256, Image.SCALE_DEFAULT)));
dataIcon.setBounds((getWidth()/2)-128, (getHeight()/2)-200, 256, 256);
add(dataIcon);
JLabel askImport = new JLabel("No Data Files have been selected: To begin importing data please select a directory.");
askImport.setFont(new Font("Helvetica", Font.PLAIN, 20));
askImport.setForeground(Color.white);
askImport.setBounds((getWidth()/2)-375, (getHeight()/2)+50, 750, 100);
add(askImport);
JButton selectDirectory = new JButton("Select Directory");
selectDirectory.setBounds((getWidth()/2)-75, (getHeight()/2)+150, 150, 50); //+half of width or height
add(selectDirectory);
selectDirectory.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
removeAll();
revalidate();
repaint();
setLayout(new FlowLayout(FlowLayout.LEFT));
ImportingImages getImages = new ImportingImages();
getImages.importFiles();
File curDir = new File("");
File[] files = curDir.listFiles();
long noFiles = curDir.length();
for (File f : files) {
String fileName = f.getName();
String hiddenFile = ".DS_Store";
if (fileName.equals(hiddenFile)){
//System.out.println("Do nothing");
} else {
String thumbnailPath = curDir + "/" + f.getName();
try {
BufferedImage thumbnailIcon = ImageIO.read(new File(thumbnailPath));
thumbnailList.add(thumbnailIcon);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
MyMouseListener listener = new MyMouseListener();
addMouseListener(listener);
addMouseMotionListener(listener);
//DisplayImages(thumbnailList, ContentPanelWidth);
}
});
}
public void setStartPoint(int x, int y) {
this.x = x;
this.y = y;
}
public void setEndPoint(int x, int y) {
x2 = (x);
y2 = (y);
}
public void drawPerfectRect(Graphics g, int x, int y, int x2, int y2) {
int px = Math.min(x,x2);
int py = Math.min(y,y2);
int pw = Math.abs(x-x2);
int ph = Math.abs(y-y2);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int alpha = 100; // 127 50% transparent
Color cyanTransparent = new Color(0,206,209, alpha);
g2.setColor(cyanTransparent);
g2.fillRoundRect(px, py, pw, ph, 5, 5);
g2.setColor(Color.cyan);
g2.setStroke(new BasicStroke(1));
g2.drawRoundRect(px, py, pw, ph, 5, 5);
}
public void drawImages(Graphics g){
int xSpacing = 10;
int ySpacing = 20;
int noImages = 0;
for (BufferedImage thumbnail : thumbnailList){
g.drawImage(thumbnail, xSpacing, ySpacing,null );
if ((xSpacing+100) > (ContentPanelWidth-100)){
ySpacing = ySpacing + 77;
xSpacing = 10;
} else{
xSpacing = xSpacing + 110;
}
noImages = noImages + 1;
//System.out.println(noImages);
}
}
class MyMouseListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
setStartPoint(e.getX(), e.getY());
}
public void mouseDragged(MouseEvent e) {
setEndPoint(e.getX(), e.getY());
revalidate();
repaint();
}
public void mouseReleased(MouseEvent e) {
setEndPoint(e.getX(), e.getY());
revalidate();
repaint();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
System.out.println(addThumbnails);
drawImages(g2);
drawPerfectRect(g2, x, y, x2, y2);
}
}
Improve the action listener added to the selectDirectory button, as follows:
Remove the repaint() call inside the action listener (will fix it later on).
Clear the list of thumbnails each time the button is pressed. That is, thumbnailList.clear() inside the action listener. This way, only the last imported images will be displayed for each directory selection.
Call repaint() inside the button's action listener after the for loop which adds the thumbnails in the list. This way, the panel will be repainted (with the new images) after the images are actually loaded into the list.
As a result, the ImportWorkspace constructor can be written as follows:
public ImportWorkspace(){
x = y = x2 = y2 = 0;
setLayout(null);
setBackground(Color.decode("#2a2e37"));
setBounds(85, 0, screenSize.width-85, screenSize.height);
//System.out.println("W: " + super.getWidth() + ", H: " + super.getHeight());
ContentPanelWidth = getWidth();
System.out.println(ContentPanelWidth);
JLabel dataIcon = new JLabel(new ImageIcon(new ImageIcon ("folder.png").getImage().getScaledInstance(256,256, Image.SCALE_DEFAULT)));
dataIcon.setBounds((getWidth()/2)-128, (getHeight()/2)-200, 256, 256);
add(dataIcon);
JLabel askImport = new JLabel("No Data Files have been selected: To begin importing data please select a directory.");
askImport.setFont(new Font("Helvetica", Font.PLAIN, 20));
askImport.setForeground(Color.white);
askImport.setBounds((getWidth()/2)-375, (getHeight()/2)+50, 750, 100);
add(askImport);
JButton selectDirectory = new JButton("Select Directory");
selectDirectory.setBounds((getWidth()/2)-75, (getHeight()/2)+150, 150, 50); //+half of width or height
add(selectDirectory);
selectDirectory.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
removeAll();
revalidate();
//repaint(); //Removed this line. It is not necessary in this place. Will be re-added AFTER the `for` loop below...
thumbnailList.clear(); //Added this line.
setLayout(new FlowLayout(FlowLayout.LEFT));
ImportingImages getImages = new ImportingImages();
getImages.importFiles();
File curDir = new File("");
File[] files = curDir.listFiles();
long noFiles = curDir.length();
for (File f : files) {
String fileName = f.getName();
String hiddenFile = ".DS_Store";
if (fileName.equals(hiddenFile)){
//System.out.println("Do nothing");
} else {
String thumbnailPath = curDir + "/" + f.getName();
try {
BufferedImage thumbnailIcon = ImageIO.read(new File(thumbnailPath));
thumbnailList.add(thumbnailIcon);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
repaint(); //Added this line! Now the newly loaded images shall be painted.
MyMouseListener listener = new MyMouseListener();
addMouseListener(listener);
addMouseMotionListener(listener);
//DisplayImages(thumbnailList, ContentPanelWidth);
}
});
}

Moving point along lines

I draw a shape, which looks like letter "Y". Then I draw a small point at the bottom of it.
Let's say the shape is 3 lines, like so:
I want to move this point along line 1, then line 2, then back along line 2, then line 3, and finally back to the start at the bottom of line 1.
It moves with speed specified by slider.
Here's my code so far:
public class yyyyy extends JFrame{
private Komponent komponent;
private Timer timer;
private int tick = 0;
private int speed = 4;
private int x1 = 225, y1 = 300, y2 = 225, x3 = 150, y3 = 150, x4 = 300;
private int x = x1, y = y1;
class Komponent extends JComponent{
/**
*
*/
private static final long serialVersionUID = -4028514932033769012L;
#Override
protected void paintComponent(Graphics arg0) {
if(tick< y1-y2){
x = x1;
y = y1-tick;
}
else if(tick>y1-y2 && tick < 2*(y1-y2)){
x = x1-tick + (y1-y2);
y = y2-tick + (y1-y2);
}
else if(tick>2*(y1-y2) && tick < 3*(y1-y2)){
x = x3 + tick - 2*(y1-y2);
y = y3 + tick - 2*(y1-y2);
}
else if(tick>3*(y1-y2)&& tick < 4*(y1-y2)){
x = x1 + tick - 3*(y1-y2);
y = y2 - tick + 3*(y1-y2);
}
else if(tick>4*(y1-y2)&& tick < 5*(y1-y2)){
x = x4 - tick + 4*(y1-y2);
y = y3 + tick - 4*(y1-y2);
}
else{
x = x1;
y = y2 + tick - 5*(y1-y2);
}
arg0.setColor(Color.BLUE);
arg0.drawLine(x1, y1, x1, y2);
arg0.drawLine(x1,y2,x3,y3);
arg0.drawLine(x1, y2, x4, y3);
arg0.setColor(Color.RED);
arg0.fillOval(x-5, y-5, 10, 10);
super.paintComponent(arg0);
}
}
public yyyyy (String string){
super(string);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBounds(100,100,550,550);
add(komponent=new Komponent());
JPanel panel = new JPanel();
add(panel, BorderLayout.SOUTH);
final JCheckBox cb = new JCheckBox("Animacja");
cb.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if(cb.isSelected()){
timer.start();
}
else{
timer.stop();
}
}
});
panel.add(cb);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
tick+=speed;
if(tick > 6*(y1 -y2)){
tick -= 6*(y1-y2);
}
if(tick < 0){
tick = 6*(y2-y1);
}
komponent.repaint();
}
});
final JSlider speedSlider = new JSlider(-30,30,speed);
panel.add(speedSlider);
speedSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
speed = speedSlider.getValue();
komponent.repaint();
}
});
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new yyyyy("wat");
}
});
}
}
Now I have 2 problems:
1. Is the resetting of tick defined properly? I feel like it should go to 0 after being 2 * the sum of distances of all lines.
2. The part of the code I marked with comment. How can I check what the current x and y of the point should be? I tried doing that from line equation, but not much success (point was always just shooting outside of the window).
EDIT: Code updated.
Although the question was already answered in the comment (and although it was mainly a debugging hint, and although this is not really an answer...) I'd like to recommend you to generalize this.
You should probably to model each and every segment manually, with a fixed set of coordinates. Instead, you could create a general "path", that should be followed. The position on the path can be defined as a value between 0.0 and 1.0.
Then, the timing and interpolation are separated, and the timing itself can be handled separately. You can then even add nifty animation effects, for example, add some Math.sin somewhere and obtain interesting ease-in/ease-out effects.
However, here is an MCVE showing an example of how such a generic path follower could be implemented. It may changed from following an Y to following an X just by changing the creation of the PathFollower instance in the main method.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class PathFollowerTest
{
public static void main(String[] args)
{
final PathFollower pathFollower = createPathFollowerY();
//final PathFollower pathFollower = createPathFollowerX();
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI(pathFollower);
}
});
}
private static PathFollower createPathFollowerY()
{
Point2D p0 = new Point2D.Double(225, 300);
Point2D p1 = new Point2D.Double(225, 225);
Point2D p2 = new Point2D.Double(150, 150);
Point2D p3 = new Point2D.Double(300, 150);
PathFollower pathFollower = new PathFollower();
pathFollower.addPoint(p0);
pathFollower.addPoint(p1);
pathFollower.addPoint(p2);
pathFollower.addPoint(p1);
pathFollower.addPoint(p3);
pathFollower.addPoint(p1);
pathFollower.addPoint(p0);
return pathFollower;
}
private static PathFollower createPathFollowerX()
{
Point2D p0 = new Point2D.Double(150, 300);
Point2D p1 = new Point2D.Double(225, 225);
Point2D p2 = new Point2D.Double(150, 150);
Point2D p3 = new Point2D.Double(300, 300);
Point2D p4 = new Point2D.Double(300, 150);
PathFollower pathFollower = new PathFollower();
pathFollower.addPoint(p0);
pathFollower.addPoint(p1);
pathFollower.addPoint(p2);
pathFollower.addPoint(p1);
pathFollower.addPoint(p4);
pathFollower.addPoint(p1);
pathFollower.addPoint(p3);
pathFollower.addPoint(p1);
pathFollower.addPoint(p0);
return pathFollower;
}
private static void createAndShowGUI(final PathFollower pathFollower)
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setBounds(100, 100, 550, 550);
f.getContentPane().setLayout(new BorderLayout());
PathFollowerPanel pathFollowerPanel =
new PathFollowerPanel(pathFollower);
f.getContentPane().add(pathFollowerPanel, BorderLayout.CENTER);
final PathFollowerController pathFollowerController =
new PathFollowerController(
pathFollower, pathFollowerPanel);
JPanel panel = new JPanel();
f.getContentPane().add(panel, BorderLayout.SOUTH);
final JCheckBox cb = new JCheckBox("Animacja");
cb.addChangeListener(new ChangeListener()
{
#Override
public void stateChanged(ChangeEvent e)
{
pathFollowerController.setRunning(cb.isSelected());
}
});
panel.add(cb);
final JSlider speedSlider = new JSlider(-30, 30, 0);
panel.add(speedSlider);
speedSlider.addChangeListener(new ChangeListener()
{
#Override
public void stateChanged(ChangeEvent e)
{
pathFollowerController.setSpeed(speedSlider.getValue());
}
});
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class PathFollowerController
{
private int speed = 0;
private PathFollower pathFollower;
private PathFollowerPanel pathFollowerPanel;
private final Timer timer = new Timer(50, new ActionListener()
{
private double alpha = 0;
#Override
public void actionPerformed(ActionEvent e)
{
alpha += speed / 500.0;
alpha %= 1.0;
while (alpha < -1.0)
{
alpha += 1.0;
}
pathFollower.setAlpha(alpha < 0 ? -alpha : alpha);
pathFollowerPanel.repaint();
}
});
PathFollowerController(PathFollower pathFollower,
PathFollowerPanel pathFollowerPanel)
{
this.pathFollower = pathFollower;
this.pathFollowerPanel = pathFollowerPanel;
}
void setRunning(boolean running)
{
if (running)
{
timer.start();
}
else
{
timer.stop();
}
}
public void setSpeed(int speed)
{
this.speed = speed;
}
}
class PathFollower
{
private final List<Point2D> points;
private Shape path;
private double pathLength = -1;
private double alpha = 0;
PathFollower()
{
points = new ArrayList<Point2D>();
}
void addPoint(Point2D p)
{
points.add(new Point2D.Double(p.getX(), p.getY()));
path = null;
pathLength = -1;
}
void setAlpha(double alpha)
{
this.alpha = alpha;
}
Point2D getCurrentPoint()
{
return computePoint(alpha);
}
Shape getPath()
{
if (path == null)
{
path = createPath();
}
return path;
}
private Shape createPath()
{
Path2D path = new Path2D.Double();
for (int i = 0; i < points.size(); i++)
{
Point2D p = points.get(i);
double x = p.getX();
double y = p.getY();
if (i == 0)
{
path.moveTo(x, y);
}
else
{
path.lineTo(x, y);
}
}
return path;
}
private double computePathLength()
{
double pathLength = 0;
for (int i = 0; i < points.size() - 1; i++)
{
Point2D p0 = points.get(i);
Point2D p1 = points.get(i + 1);
pathLength += p0.distance(p1);
}
return pathLength;
}
private Point2D computePoint(double alpha)
{
if (pathLength < 0)
{
pathLength = computePathLength();
}
double alphaPosition = alpha * pathLength;
double accumulatedLength = 0;
for (int i = 0; i < points.size() - 1; i++)
{
Point2D p0 = points.get(i);
Point2D p1 = points.get(i + 1);
double distance = p0.distance(p1);
double nextLength = accumulatedLength + distance;
if (nextLength >= alphaPosition)
{
double localAlpha =
(alphaPosition - accumulatedLength) / distance;
double x = p0.getX() + localAlpha * (p1.getX() - p0.getX());
double y = p0.getY() + localAlpha * (p1.getY() - p0.getY());
return new Point2D.Double(x, y);
}
accumulatedLength = nextLength;
}
Point2D p = points.get(points.size() - 1);
return new Point2D.Double(p.getX(), p.getY());
}
}
class PathFollowerPanel extends JPanel
{
private final PathFollower pathFollower;
PathFollowerPanel(PathFollower pathFollower)
{
this.pathFollower = pathFollower;
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLUE);
g.setStroke(new BasicStroke(3.0f));
g.draw(pathFollower.getPath());
g.setColor(Color.RED);
Point2D p = pathFollower.getCurrentPoint();
double r = 5;
g.fill(new Ellipse2D.Double(
p.getX() - r, p.getY() - r, r + r, r + r));
}
}

dynamically update JPanel background does't work

After reading image from JFilechooser, I am trying to read pixel of an image one by one and display it to JPanel after some delay in sequential manner. can't update the background of JPanel.
public class ImageMain extends JFrame implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 2916361361443483318L;
private JFileChooser fc = null;
private JMenuItem item1, item2;
private BufferedImage image = null;
private JPanel panel = null;
private int width = 0;
private int height = 0;
private BorderLayout card;
private Container contentPane;
//private int loopcount = 0;
//private int counter = 0;
public ImageMain() {
JFrame frame = new JFrame("Image Extraction Tool");
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = frame.getContentPane();
panel = new JPanel();
card = new BorderLayout();
panel.setLayout(card);
panel.setBackground(Color.white);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Menu");
menuBar.add(menu);
item1 = new JMenuItem("Browse an image");
item2 = new JMenuItem("Exit");
item1.addActionListener(this);
item2.addActionListener(this);
menu.add(item1);
menu.add(item2);
frame.setJMenuBar(menuBar);
contentPane.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
ImageMain img = new ImageMain();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == item1) {
if (fc == null)
fc = new JFileChooser();
int retVal = fc.showOpenDialog(null);
if (retVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
try {
image = ImageIO.read(file);
height = image.getHeight();
width = image.getWidth();
// final int[][] pixelData = new int[height * width][3];
// int[] rgb;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
System.out.println(i + " " + j);
Color c = new Color(image.getRGB(j, i));
panel.setBackground(c);
panel.invalidate();
panel.validate();
panel.repaint();
}
}
} catch (IOException e1) {
System.out.println("IO::" + e1.getMessage());
} catch (Exception e1) {
System.out.println("Exception::" + e1.getMessage());
}
}
}
if (e.getSource() == item2) {
System.exit(0);
}
}}
Inside ActionPerformed, I got Color object by reading RGB values and then I am stuck at displaying them to JApplet. Suggestion are welcome if there is a better way to achieve this.
Thanks in advance.
The main problem is your performing a long running task within the context of the Event Dispatching Thread, which is responsible for, amongst other things, processing repaint requests.
#Override
public void actionPerformed(ActionEvent e) {
//...
// Nothing will be updated until after the
// actionPerformed method exists
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
System.out.println(i + " " + j);
Color c = new Color(image.getRGB(j, i));
panel.setBackground(c);
panel.invalidate();
panel.validate();
panel.repaint();
}
}
The other problem you have is that you are required to only modify the state of the UI from within the context of the EDT
Depending on your exact needs, you could use a SwingWorker, which would allow you to process the pixels in the background while updating the UI from within the context of the EDT, however, because SwingWorker consolidates it's updates, you could miss color changes.
A better solution might be to use a java.swing.Timer which would allow you to trigger updates at a specified period, which are triggered within the context of the EDT.
See Concurrency in Swing for more details...
Updated with example
In order to draw pixels, you need something to draw them on. Now, you could simply add each pixel you want to paint to an array and loop that array each time you need to repaint the component, but that's kind of expensive...
Instead, it would be simpler to have a backing buffer of some kind, onto which you paint the pixels and then paint that buffer to the component, which should be faster.
Basically, the allows you to supply the height and width of the expected image and then supply each pixel as you need..
public class ImagePane extends JPanel {
private BufferedImage img;
public ImagePane() {
}
public void reset(int width, int height) {
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
revalidate();
}
public void reset() {
img = null;
revalidate();
}
public void setPixelAt(int x, int y, int pixel) {
img.setRGB(x, y, pixel);
}
#Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (img != null) {
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
}
g2d.dispose();
}
}
Take a look at Performing Custom Painting for more details...
Then you need some way to process the original image and update the image panel...Now, based on the updated requirements, I would use a SwingWorker, the reason for this, is that the SwingWorker can cache what is passed back to the EDT, this allows the background thread to continue processing and caching the output until the EDT (and system) is ready to process it...
public class PixelExposerWorker extends SwingWorker<Void, Pixel> {
private final BufferedImage img;
private final ImagePane imagePane;
private final List<Point> points;
public PixelExposerWorker(BufferedImage img, ImagePane imagePane) {
this.img = img;
this.imagePane = imagePane;
points = new ArrayList<>(img.getWidth() * img.getHeight());
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
points.add(new Point(x, y));
}
}
}
#Override
protected void process(List<Pixel> chunks) {
System.out.println("Publish " + chunks.size());
for (Pixel pixel : chunks) {
imagePane.setPixelAt(pixel.getX(), pixel.getY(), pixel.getColor());
}
imagePane.repaint();
}
#Override
protected Void doInBackground() throws Exception {
int pixelCount = (int) (points.size() * 0.005);
while (!points.isEmpty()) {
for (int count = 0; count < pixelCount && !points.isEmpty(); count++) {
int index = (int) (Math.random() * (points.size() - 1));
Point p = points.remove(index);
Pixel pixel = new Pixel(p.x, p.y, img.getRGB(p.x, p.y));
publish(pixel);
}
Thread.yield();
}
return null;
}
}
Basically, this SwingWorker builds a List of "pixel points", it uses the list to randomly remove points from the list, generate a virtual Pixel and publish back to the EDT for processing. The worker will process around 0.5% of the pixels at a time, meaning that the work is always trying to send a "bunch" of pixels back to the EDT, rather the one at a time.
Finally, it will yield to allow other threads to run (and the hopefully for the EDT to update it self)
An image of 650x975 takes roughly 1m and 10s to fully renderer
And the full code...
import core.util.StopWatch;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
public class PixelShower implements ActionListener {
private static final long serialVersionUID = 2916361361443483318L;
private JFileChooser fc = null;
private JMenuItem item1, item2;
private BufferedImage image = null;
private ImagePane panel = null;
private int width = 0;
private int height = 0;
private BorderLayout card;
private Container contentPane;
public PixelShower() {
JFrame frame = new JFrame("Image Extraction Tool");
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = frame.getContentPane();
panel = new ImagePane();
card = new BorderLayout();
panel.setLayout(card);
panel.setBackground(Color.white);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Menu");
menuBar.add(menu);
item1 = new JMenuItem("Browse an image");
item2 = new JMenuItem("Exit");
item1.addActionListener(this);
item2.addActionListener(this);
menu.add(item1);
menu.add(item2);
frame.setJMenuBar(menuBar);
contentPane.add(panel);
frame.setVisible(true);
}
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
PixelShower img = new PixelShower();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == item1) {
if (fc == null) {
fc = new JFileChooser();
}
int retVal = fc.showOpenDialog(null);
if (retVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
try {
image = ImageIO.read(file);
panel.reset(image.getWidth(), image.getHeight());
PixelExposerWorker worker = new PixelExposerWorker(image, panel);
worker.execute();
} catch (IOException e1) {
e1.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
if (e.getSource() == item2) {
System.exit(0);
}
}
public class ImagePane extends JPanel {
private BufferedImage img;
public ImagePane() {
}
public void reset(int width, int height) {
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
revalidate();
}
public void reset() {
img = null;
revalidate();
}
public void setPixelAt(int x, int y, int pixel) {
img.setRGB(x, y, pixel);
}
#Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (img != null) {
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
}
g2d.dispose();
}
}
public class Pixel {
private int x;
private int y;
private int color;
public Pixel(int x, int y, int color) {
this.x = x;
this.y = y;
this.color = color;
}
public int getColor() {
return color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
public class PixelExposerWorker extends SwingWorker<Void, Pixel> {
private final BufferedImage img;
private final ImagePane imagePane;
private final List<Point> points;
public PixelExposerWorker(BufferedImage img, ImagePane imagePane) {
this.img = img;
this.imagePane = imagePane;
points = new ArrayList<>(img.getWidth() * img.getHeight());
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
points.add(new Point(x, y));
}
}
}
#Override
protected void process(List<Pixel> chunks) {
System.out.println("Publish " + chunks.size());
for (Pixel pixel : chunks) {
imagePane.setPixelAt(pixel.getX(), pixel.getY(), pixel.getColor());
}
imagePane.repaint();
}
#Override
protected Void doInBackground() throws Exception {
StopWatch sw = StopWatch.newInstance().start();
int pixelCount = (int) (points.size() * 0.005);
System.out.println("pixelCount = " + pixelCount + "; " + points.size());
while (!points.isEmpty()) {
StopWatch sw1 = StopWatch.newInstance().start();
for (int count = 0; count < pixelCount && !points.isEmpty(); count++) {
int index = (int) (Math.random() * (points.size() - 1));
Point p = points.remove(index);
Pixel pixel = new Pixel(p.x, p.y, img.getRGB(p.x, p.y));
publish(pixel);
}
Thread.yield();
}
System.out.println("Took " + sw.stop());
return null;
}
}
}

Drawing a line using a Mouse Motion Listener

I'm new at using actionListener and mouselistner. I want to track the Mouse movements and connect the dots as I go. So it will be one continuous line. I am using the MouseMotionListener every time I move the mouse across the screen and get a new set of points. I am not using mousePressed or mouseReleased.
Everytime I move the mouse, I can get every point of where my mouse's position is on my JPanel to show up on my results window, but in order to draw the line between two points, I'm not sure how to decipher between each different set of points so I can have a beginning and ending set of points to use for drawing a line? I checked through here and the API and I'm stuck.
I was asked to put more code and so I gave you my code.
I am basically creating a Jpane that tracks mouse movement when the Track Mouse button or Draw Track Radio button is pressed
These results will print out as (x,y) on the output screen
FYI: You can also draw circles once the Draw circles RadioButton is checked
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
public class MouseTest {
private JFrame frame;
private boolean tracking;
private boolean drawing;
private boolean connectdots;
private int xstart;
private int ystart;
private int xend;
private int y;
private int x;
private int yend;
private int borderWidth = 5;
String drawCircles = "Draw Circles";
String drawTrack = "Draw Track";
public static void main (String [] arg) {
MouseTest first = new MouseTest();
}//main
$
public MouseTest() {
tracking = false;
frame = new JFrame();
frame.setBounds(250,98,600,480);
frame.setTitle("Window number three");
Container cp = frame.getContentPane();
JButton track = new JButton("Track Mouse");
track.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
trackMouse();
}
});
JPanel top = new JPanel();
top.add(track);
cp.add(top,BorderLayout.NORTH);
MyPanel pane = new MyPanel();
cp.add(pane,BorderLayout.CENTER);
pane.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
xstart = e.getX();
ystart = e.getY();
}
public void mouseReleased(MouseEvent e) {
xend = e.getX();
yend = e.getY();
if (xend < xstart) { int tmp = xstart; xstart = xend; xend = tmp; }
if (yend < ystart) { int tmp = ystart; ystart = yend; yend = tmp; }
frame.repaint();
}
});
pane.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
if (tracking) {
x = e.getX();
y = e.getY();
msg("(" + x + ", " + y + ")");
}
}
});
JRadioButton circleButton = new JRadioButton(drawCircles);
circleButton.setMnemonic(KeyEvent.VK_B);
circleButton.setActionCommand(drawCircles);
circleButton.setSelected(false);
JRadioButton trackButton = new JRadioButton(drawTrack);
trackButton.setMnemonic(KeyEvent.VK_C);
ButtonGroup group = new ButtonGroup();
group.add(circleButton);
group.add(trackButton);
JPanel radioPanel = new JPanel(new GridLayout(2,0));
radioPanel.add(circleButton);
radioPanel.add(trackButton);
cp.add(radioPanel,BorderLayout.EAST);
drawing = false;
connectdots = false;
circleButton.addActionListener(new ActionListener() { //Set drawing to true when the button is clicked
public void actionPerformed(ActionEvent ae) {
tracking = !tracking;
drawCircles();
}
});
trackButton.addActionListener(new ActionListener() { //Set drawing to true when the button is clicked
public void actionPerformed(ActionEvent ae) {
drawing = false;
tracking = true;
connectDots();
}
});
frame.setVisible(true);
}//constructor
$
public void trackMouse() {
tracking = !tracking;
}//trackMouse
public void msg(String s) { //new method to simplify the system.out.print method
System.out.println(s);
}
public void drawCircles() {
drawing = true;
}//Allow Drawing of Circles
public void connectDots() {
connectdots = !connectdots;
}//Trying to use for connecting the mouse motion listener points when tracking
$
public class MyPanel extends JPanel {
ArrayList<Circle> circles = new ArrayList<Circle>();
public void paintComponent(Graphics g) {
int width = this.getWidth();
int height = this.getHeight();
msg("H = " + height + ", w = " + width);
g.setColor(Color.BLACK);
Circle c = new Circle(xstart, ystart);
circles.add(c);
if (drawing){
for(int k=0; k<circles.size(); k++){
circles.get(k).draw(g);
}
} // draw the circle
if (connectdots && tracking) { //trying to use for drawing the lines
g.drawLine(x,y,x,y);
}
for (int delta = 0; delta < borderWidth; delta++) {
g.drawRect(delta,delta,width-(2*delta),height-(2*delta));
}
if (xstart != xend || ystart != yend) {
int red = (int)(256*Math.random());
int green = (int)(256*Math.random());
int blue = (int)(256*Math.random());
g.setColor(new Color(red, green, blue));
msg("Colors are: " + red + " - " + green + " - " + blue );
msg("Drawing from: (" + xstart + ", " + ystart + ") to (" + xend + ", " + yend + ")");
msg("Width is " + (xend-xstart) + " - Height is " + (yend-ystart));
g.fillRect(xstart,ystart,xend-xstart,yend-ystart);
}
}
}
}
$
public class Circle {
// instance variables:
int radius; //random radius
int x, y; // coords of the center point
private Graphics g;
public Circle(int xIn, int yIn) {
radius = (int)(127*Math.random());
x = xIn;
y = yIn;
}
public void draw(Graphics g){
g.drawOval(x-radius, y-radius, radius, radius);
g.fillOval(x-radius, y-radius, radius, radius);
int red = (int)(256*Math.random());
int green = (int)(256*Math.random());
int blue = (int)(256*Math.random());
g.setColor(new Color(red, green, blue));
}
public int getX(int xstart) {
// TODO Auto-generated method stub
return x;
}
public int getY(int ystart) {
// TODO Auto-generated method stub
return y;
}
}
You just have to save x and y in some attribute for your class (like lastX and lastY.) When you get the event the second time, your first point coordinates are saved in your attributes and you can then draw your line. This new point has to be saved too to serve as your new lastX and lastY.
Even better: since you'll probably need to redraw your lines each frame (if you want a set of lines and not just a single line to be drawn each frame), use Point to save your coordinates and some kind of list like ArrayList. Save the full set of tracked points in an ArrayList<Point> and then draw each frame iterating over this list.
An example of an sscce:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class Foo3 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private boolean tracking = false;
private int x, y;
public Foo3() {
add(new JToggleButton(new AbstractAction("TrackMouse") {
public void actionPerformed(ActionEvent ae) {
trackMouse(ae);
}
}));
MyMouseAdapter myMA = new MyMouseAdapter();
addMouseMotionListener(myMA);
}
private void trackMouse(ActionEvent ae) {
JToggleButton toggleBtn = (JToggleButton) ae.getSource();
tracking = toggleBtn.isSelected();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void msg(String message) {
System.out.println(message);
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mouseMoved(MouseEvent e) {
if (tracking) {
x = e.getX();
y = e.getY();
msg("(" + x + ", " + y + ")");
}
}
}
private static void createAndShowGui() {
Foo3 mainPanel = new Foo3();
JFrame frame = new JFrame("MouseMotion Eg");
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();
}
});
}
}
Edit: Example 2
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class MouseTestHovercraft extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final int MAX_CLR = 5;
private static final Color CURRENT_LIST_COLOR = new Color(190, 190, 255);
private List<Color> colors = new ArrayList<Color>();
private boolean tracking = false;
private List<Point> currentList = null;
private BufferedImage bufferedImage = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
private Random random = new Random();
public MouseTestHovercraft() {
for (int redIndex = 0; redIndex < MAX_CLR; redIndex++) {
int r = (redIndex * 256) / (MAX_CLR - 1);
r = (r == 256) ? 255 : r;
for (int greenIndex = 0; greenIndex < MAX_CLR; greenIndex++) {
int g = (greenIndex * 256) / (MAX_CLR - 1);
g = (g == 256) ? 255 : g;
for (int blueIndex = 0; blueIndex < MAX_CLR; blueIndex++) {
int b = (blueIndex * 256) / (MAX_CLR - 1);
b = (b == 256) ? 255 : b;
Color c = new Color(r, g, b);
colors.add(c);
}
}
}
add(new JToggleButton(new AbstractAction("TrackMouse") {
public void actionPerformed(ActionEvent ae) {
trackMouse(ae);
}
}));
add(new JButton(new AbstractAction("Clear Image") {
public void actionPerformed(ActionEvent e) {
bufferedImage = new BufferedImage(getWidth(), getHeight(),
BufferedImage.TYPE_INT_ARGB);
repaint();
}
}));
MyMouseAdapter myMA = new MyMouseAdapter();
addMouseListener(myMA);
addMouseMotionListener(myMA);
}
private void trackMouse(ActionEvent ae) {
JToggleButton toggleBtn = (JToggleButton) ae.getSource();
tracking = toggleBtn.isSelected();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void msg(String message) {
System.out.println(message);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bufferedImage, 0, 0, null);
if (currentList != null) {
drawList(g, currentList, CURRENT_LIST_COLOR, 1f);
}
}
private void drawList(Graphics g, List<Point> ptList, Color color,
float strokeWidth) {
if (ptList.size() > 1) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(color);
g2.setStroke(new BasicStroke(strokeWidth));
for (int j = 0; j < ptList.size() - 1; j++) {
int x1 = ptList.get(j).x;
int y1 = ptList.get(j).y;
int x2 = ptList.get(j + 1).x;
int y2 = ptList.get(j + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.dispose();
}
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
if (tracking && e.getButton() == MouseEvent.BUTTON1) {
currentList = new ArrayList<Point>();
currentList.add(e.getPoint());
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (tracking && e.getButton() == MouseEvent.BUTTON1) {
currentList.add(e.getPoint());
Graphics2D g2 = bufferedImage.createGraphics();
Color color = colors.get(random.nextInt(colors.size()));
drawList(g2, currentList, color, 3f);
currentList = null;
repaint();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (tracking && currentList != null) {
currentList.add(e.getPoint());
repaint();
}
}
}
private static void createAndShowGui() {
MouseTestHovercraft mainPanel = new MouseTestHovercraft();
JFrame frame = new JFrame("MouseMotion Eg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Categories