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

时间:2013-11-09 03:30:14

标签: java swing jframe jpanel

我已经看过许多与此类似的问题的答案,但是他们似乎都说要画到我正在做的JPanel上,所以我不明白如何让我的矩形不闪烁。

我正在制作一个截图制作工具,就像Mac上的CMD + SHIFT + 4功能一样。我有一切正常工作,即拖动矩形时会拍照,但是你选择的矩形会一直闪烁。

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) {}
}

1 个答案:

答案 0 :(得分:2)

Swing中的绘画通常是从paintComponent继承的JComponent方法的上下文中完成的。这样可以确保在油漆循环发生时,涂漆的内容是双缓冲的,这样可以防止油漆过程中的闪烁。

Swing中的绘画是由RepaintManager控制的,它决定何时和应该绘制什么是它的责任。您可以通过调用其中一种RepaintManager方法向repaint发出请求以执行绘画更新,并且可能会在将来的某个时间安排更新

使用...

Graphics g = ((JComponent) source).getGraphics();

这是一个非常糟糕的主意。它能够返回null并且只是Graphics上下文的快照,并且在正确的绘制周期发生时会擦干净。

请查看Performing custom paintingPainting in AWT and Swing了解详情

此...

while (true) {
    currentPoint = MouseInfo.getPointerInfo().getLocation();
    if (currentPoint != startPoint && clicked) {
        drawRectangle(startPoint, currentPoint, picture1);
    }
//System.out.println(currentPoint);
    //delay(1);
}

让我感到害怕,原因有二,首先,它不是推荐的监控鼠标更新的方式,而是两个,在你创建UI的同一个上下文中调用它,这使它有可能“挂起”你的程序

我还建议您查看使用KeyListener的{​​{3}},它将解决与焦点相关的问题......

您还可以找到key bindings API的帮助......