简体   繁体   中英

How to avoid flickering when calling repaint() in 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, ie 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 KeyListener s, it will solve focus related issues...

You may also find Concurrency in Swing of assistance...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM