简体   繁体   English

在Java中调用repaint()时如何避免闪烁?

[英]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. 我已经看过许多类似问题的答案,但是它们似乎都说是在借鉴我正在做的JPanel,所以我不明白如何使矩形不闪烁。

I am making a Screenshot maker, like the CMD+SHIFT+4 feature on macs. 我正在制作一个屏幕截图制作程序,就像Mac上的CMD + SHIFT + 4功能一样。 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 . Swing中的绘画通常是在从JComponent继承的paintComponent方法的上下文中完成的。 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. Swing中的绘画由RepaintManager控制,确定何时以及应绘画什么是责任。 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 您可以通过调用repaint方法之一向RepaintManager发出请求以执行绘制更新,并且它可能会在将来的某个时间安排更新

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. 它具有返回null的能力,仅是Graphics上下文的快照,并且在发生适当的绘制周期时将被清除干净。

Take a look at Performing custom painting and Painting in AWT and Swing for more details 有关更多详细信息,请参见在AWT和Swing执行自定义绘画绘画。

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 吓到我,原因有二,其一,这不是建议的监视鼠标更新的方法,二是在您创建UI的同一上下文中调用它,这使其有可能“挂起”程序

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... 我还建议您通过使用KeyListener来了解键绑定API ,它将解决与焦点相关的问题...

You may also find Concurrency in Swing of assistance... 您可能还会在并发援助中找到并发 ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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