问题描述
我正在制作一个简单的应用程序,并通过练习与 FPS 相关的方法如何在 Java 中工作以制作具有高刷新率的应用程序来学习,但是,我不知道我应该使用上述哪种方法。我的应用程序有一个按钮和一个文本字段以及一个我制作的简单布局管理器,它应该根据原始框架大小(使用比率)更改组件的位置。每次组件的位置更改时,布局管理器都应该能够更新面板(或我真的不知道的框架)。此外,当用户更改框架尺寸时,组件应平滑移动且不会滞后。我怎样才能完成所有这些?现在我的问题如下所示,似乎 panel.updateUI() 没有做任何事情。
如您所见,当您缓慢更改帧大小时,会出现一条黑线,并且不会消失。速度快的时候会变大,看得很清楚,但是又消失了,而FPS 60以上应该看不到这样的东西。
这就是我目前的做法:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
public class Test {
private JFrame frame;
private SwingWorker<Void,Void> sw;
private JTextField txtGi;
protected volatile static boolean b = false;
private double FPS = 0;
protected static volatile JButton btnStart;
public static void main(String[] args) {
EventQueue.invokelater(new Runnable() {
public void run() {
try {
Test window = new test();
window.frame.setVisible(true);
} catch (Exception e) {
e.printstacktrace();
}
}
});
}
public test() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100,100,230,230);
frame.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
frame.setMinimumSize(new Dimension(230,230));
JPanel panel = new JPanel();
panel.setBounds(0,216,193);
panel.setBackground(Color.PINK);
frame.getContentPane().add(panel);
panel.setLayout(null);
btnStart = new JButton("Start");
btnStart.setName("btnStart");
btnStart.setFont(new Font("Segoe Print",Font.BOLD | Font.ITALIC,17));
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println(frame.getMinimumSize());
System.out.println(
"#sw.isDone(): " + sw.isDone() + " sw.isCancelled(): " + sw.isCancelled() + " b: " + b);
}
});
btnStart.setBounds(50,120,101,33);
panel.add(btnStart);
System.out.println("1: " + Thread.currentThread().getName());
frame.validate();
panel.validate();
sw = new SwingWorker<Void,Void>() {
@Override
protected Void doInBackground() throws Exception {
b = true;
System.out.println(Thread.currentThread().getName() + " is going to sleep for 250 millis.");
System.out.println(frame.isShowing());
while (frame.isShowing() == false) {
System.out.println("initializing the frame.");
Thread.sleep(320);
}
System.out.println(frame.isShowing());
int cc = 0;
int FC = 0;
while (frame.isShowing()) {
Thread.sleep(1);
panel.updateUI();
long s = System.currentTimeMillis();
System.out.println("2: " + Thread.currentThread().getName());
cc++;
Graphical_AI.setComponentLocationWithButton(btnStart);
System.out.println("3: " + Thread.currentThread().getName());
if (Graphical_AI.thread1.isAlive()) {
System.out.println();
System.out.println("waiting for " + Graphical_AI.thread1.getName() + " to finish!");
System.out.println();
Graphical_AI.thread1.join();
Thread.sleep(1);
panel.updateUI();
}
Graphical_AI.setComponentLocationWithButton(txtGi);
if (Graphical_AI.thread1.isAlive()) {
System.out.println();
System.out.println("waiting for " + Graphical_AI.thread1.getName() + " to finish!");
System.out.println();
Graphical_AI.thread1.join();
Thread.sleep(1);
panel.updateUI();
}
long e = System.currentTimeMillis();
FPS = 1.0 / ((e - s) / 1000.0);
FC++;
if (FC == 60) {
txtGi.setText(String.valueOf("FPS: " + (int) FPS));
FC = 0;
}
System.out.println("FPS: " + (int) FPS);
if (cc == 1000) {
Thread.sleep(5);
cc = 0;
System.gc();
System.out.println("memory optimized.");
}
}
return null;
}
@Override
protected void process(List<Void> chunks) {
super.process(chunks);
}
@Override
protected void done() {
System.out.println("sw.isDone(): " + sw.isDone() + " b: " + b);
super.done();
}
};
sw.execute();
txtGi = new JTextField();
txtGi.setName("txtGi");
txtGi.setFont(new Font("Segoe Script",21));
txtGi.setBounds(50,40,134,70);
panel.add(txtGi);
txtGi.setColumns(10);
}
}
布局管理器:
import java.awt.Container;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Graphical_AI {
protected static volatile int count = 0;
protected static volatile int C_X;
protected static volatile int C_Y;
protected static double x_C_To_E_ratio;
protected static double y_C_To_E_ratio;
protected static int C_WIDTH;
protected static int C_HEIGHT;
protected static int Con_WIDTH;
protected static int Con_HEIGHT;
protected static volatile int N_C_X;
protected static volatile int N_C_Y;
protected static Container con;
protected volatile static boolean bool = false;
protected static volatile ArrayList<Double> al = new ArrayList<Double>();
protected static volatile ArrayList<String> al_for_O_names = new ArrayList<String>();
protected static volatile String C_Name;
protected static volatile String C_O_Name;
protected volatile static boolean bool2 = false;
protected static volatile Thread thread1;
protected static void analyzeJComponentSize(JComponent comp) throws InterruptedException {
System.out.println("W: " + comp.getWidth() + " H: " + comp.getHeight());
Thread.sleep(30);
}
protected static void analyzeJFrameSize(JFrame frame) throws InterruptedException {
System.out.println("W: " + frame.getWidth() + " H: " + frame.getHeight());
Thread.sleep(30);
}
protected static void setComponentLocationWithButton(JComponent comp) throws InterruptedException {
thread1 = Thread.currentThread();
thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("count: " + count);
C_Name = comp.getName();
System.out.println("C_Name: " + C_Name);
if (count == 0) {
C_O_Name = comp.getName();
System.out.println("C_O_Name: " + C_O_Name);
con = comp.getParent();
Con_WIDTH = con.getWidth();
Con_HEIGHT = con.getHeight();
System.out.println("** (double) Con_WIDTH: " + (double) Con_WIDTH);
System.out.println("** (double) Con_HEIGHT: " + (double) Con_HEIGHT);
x_C_To_E_ratio = (double) con.getWidth() / (double) comp.getX();
y_C_To_E_ratio = (double) con.getHeight() / (double) comp.getY();
if (bool2 == false) {
al.add(x_C_To_E_ratio);
al.add(y_C_To_E_ratio);
al_for_O_names.add(comp.getName());
bool2 = true;
}
System.out.println("** x_C_To_E_ratio: " + x_C_To_E_ratio);
System.out.println("** y_C_To_E_ratio: " + y_C_To_E_ratio);
System.out.println("** (double) con.getWidth() / (double) comp.getX(): "
+ (double) con.getWidth() / (double) comp.getX());
C_X = comp.getX();
C_Y = comp.getY();
C_WIDTH = comp.getWidth();
C_HEIGHT = comp.getHeight();
System.out.println("** C_WIDTH: " + C_WIDTH + "," + C_HEIGHT);
System.out.println("** (double) con.getWidth() / (double) C_X): " + (double) con.getWidth() + " / "
+ (double) C_X);
count++;
}
if (C_Name != al_for_O_names.get(0)) {
count = 0;
System.out.println("%% C_Name: " + C_Name);
System.out.println("%% C_O_Name: " + C_O_Name);
System.out.println("%% al_for_O_names.get(0): " + al_for_O_names.get(0));
System.out.println("%% count became to:" + count);
al_for_O_names.remove(0);
al_for_O_names.add(comp.getName());
} else {
System.out.println("*ELSE STATEMENT*");
count = 1;
}
if (count == 0) {
C_Name = comp.getName();
System.out.println("$$ C_Name: " + C_Name);
con = comp.getParent();
Con_WIDTH = con.getWidth();
Con_HEIGHT = con.getHeight();
System.out.println("$$ (double) Con_WIDTH: " + (double) Con_WIDTH);
System.out.println("$$ (double) Con_HEIGHT: " + (double) Con_HEIGHT);
x_C_To_E_ratio = (double) con.getWidth() / (double) comp.getX();
y_C_To_E_ratio = (double) con.getHeight() / (double) comp.getY();
if (bool2 == false) {
al.add(x_C_To_E_ratio);
al.add(y_C_To_E_ratio);
bool2 = true;
}
System.out.println("$$ x_C_To_E_ratio: " + x_C_To_E_ratio);
System.out.println("$$ y_C_To_E_ratio: " + y_C_To_E_ratio);
System.out.println("$$ (double) con.getWidth() / (double) comp.getX(): "
+ (double) con.getWidth() / (double) comp.getX());
C_X = comp.getX();
C_Y = comp.getY();
C_WIDTH = comp.getWidth();
C_HEIGHT = comp.getHeight();
System.out.println("$$ C_WIDTH: " + C_WIDTH + "," + C_HEIGHT);
System.out.println("$$ (double) con.getWidth() / (double) C_X): " + (double) con.getWidth() + " / "
+ (double) C_X);
count++;
}
try {
System.out.println("## after if count block ## comp W: " + comp.getWidth() + " comp H: "
+ comp.getHeight() + "\ncomp.getLocation(): " + comp.getLocation()
+ " comp.getLocationOnScreen(): " + comp.getLocationOnScreen());
System.out.println("con W: " + con.getWidth() + ",con H: " + con.getHeight());
if (x_C_To_E_ratio != al.get(0) || y_C_To_E_ratio != al.get(1)) {
x_C_To_E_ratio = al.get(0);
y_C_To_E_ratio = al.get(1);
Thread.sleep(2);
}
if (((double) con.getWidth() / (double) C_X) > x_C_To_E_ratio + 0.025) {
Thread.sleep(2);
System.out.println((((double) con.getWidth() / (double) C_X)) + "," + x_C_To_E_ratio);
while (((double) con.getWidth() / (double) C_X) > x_C_To_E_ratio + 0.025) {
C_X += 1;
N_C_X = C_X;
if (((double) con.getWidth() / (double) C_X) < x_C_To_E_ratio + 0.025) {
System.out.println("## in if bigger ## ");
break;
}
System.out.println("## in if bigger ## C_X: " + C_X + ",N_C_X " + N_C_X);
System.out.println("## in if bigger ## (double) con.getWidth() / (double) C_X: "
+ (double) con.getWidth() + " / " + (double) C_X);
System.out.println("## in if bigger ## ((double) con.getWidth() / (double) C_X): "
+ ((double) con.getWidth() / (double) C_X) + ",x_C_To_E_ratio:" + x_C_To_E_ratio
+ ",((double) con.getWidth() / (double) C_X) == x_C_To_E_ratio: "
+ (((double) con.getWidth() / (double) C_X) == x_C_To_E_ratio));
if (((double) con.getWidth() / (double) C_X) == x_C_To_E_ratio
|| ((double) con.getWidth() / (double) C_X) - x_C_To_E_ratio < 0.002) {
bool = true;
System.out.println("## in if bigger ## GI.bool: " + bool);
System.out.println("## in if bigger ## W: " + comp.getWidth() + " H: "
+ comp.getHeight() + "\ncomp.getLocation(): " + comp.getLocation()
+ " comp.getLocationOnScreen(): " + comp.getLocationOnScreen() + " Con_WIDTH: "
+ Con_WIDTH + " Con_HEIGHT: " + Con_HEIGHT + " con.getWidth(): "
+ con.getWidth() + " con.getHeight(): " + con.getHeight());
System.out.println("## in if bigger ## $GI.bool: " + bool + " $Test.b: " + Test.b);
break;
}
comp.setBounds(N_C_X,comp.getY(),C_WIDTH,C_HEIGHT);
}
}
else if (((double) con.getWidth() / (double) C_X) < x_C_To_E_ratio - 0.025) {
Thread.sleep(2);
System.out.println((((double) con.getWidth() / (double) C_X)) + " *** " + x_C_To_E_ratio);
while (((double) con.getWidth() / (double) C_X) < x_C_To_E_ratio - 0.025) {
C_X -= 1;
N_C_X = C_X;
if (((double) con.getWidth() / (double) C_X) > x_C_To_E_ratio - 0.025) {
break;
}
System.out.println("C_X: " + C_X + ",N_C_X " + N_C_X);
System.out.println("(double) con.getWidth() / (double) C_X): " + (double) con.getWidth()
+ " / " + (double) C_X);
System.out.println("((double) con.getWidth() / (double) C_X): "
+ ((double) con.getWidth() / (double) C_X) + ",((double) con.getWidth() / (double) C_X) == x_C_To_E_ratio: "
+ (((double) con.getWidth() / (double) C_X) == x_C_To_E_ratio));
if (((double) con.getWidth() / (double) C_X) == x_C_To_E_ratio
|| ((double) con.getWidth() / (double) C_X) - x_C_To_E_ratio > -0.002) {
bool = true;
System.out.println("GA.bool: " + bool);
System.out.println("W: " + comp.getWidth() + " H: " + comp.getHeight()
+ "\ncomp.getLocation(): " + comp.getLocation()
+ " comp.getLocationOnScreen(): " + comp.getLocationOnScreen() + " Con_WIDTH: "
+ Con_WIDTH + " Con_HEIGHT: " + Con_HEIGHT + " con.getWidth(): "
+ con.getWidth() + " con.getHeight(): " + con.getHeight());
System.out.println("$GA.bool: " + bool + " $T.b: " + Test.b);
break;
}
comp.setBounds(N_C_X,C_HEIGHT);
}
}
} catch (InterruptedException e) {
e.printstacktrace();
}
}
});
thread1.start();
}
}
** 现在,当您移动组件时,它们似乎不会滞后,如果您有更好的解决方案,我将不胜感激。
** 我不想使用 pack() 方法或任何预先构建的布局管理器。
** 我已经阅读了很多类似的问题,但我没有找到答案。
** 我正在使用 eclipse windowbuilder。
解决方法
自从你在你的问题中说,我还没有考虑这么多,
我不想使用 pack() 方法或任何预构建的布局管理器。
此评论立即取消了我认为您的问题的资格。有了这个限制,我无法回答这个问题。
这是我能想到的最简单的 Swing FPS 测试示例 GUI。
我使用 pack
方法。我使用 Swing 布局管理器。如果这是一个问题,请继续自己努力。
正如我在评论中所说,Oracle 有一个教程 Creating a GUI With JFC/Swing,它将带您完成创建 Swing GUI 的步骤。跳过 Netbeans 部分。
我做的第一件事是调用 SwingUtilities
invokeLater
方法来启动 Swing 应用程序。此方法可确保在 Event Dispatch Thread 上创建和执行 Swing 组件。
接下来我做的是构造一个 JFrame
。 JFrame
方法必须按特定顺序调用。这是我在大多数 Swing 应用程序中使用的顺序。
我构建了一个按钮 JPanel
和一个绘图 JPanel
。按钮 JPanel
包含开始 JButton
。绘图 JPanel
每秒绘制帧数。
我使用 Swing Timer
来捕获当前时间和帧数。您可以通过调整 Timer
构造函数中的 int
值来调整 Timer
的速度。我不推荐低于 5 的值。
这是完整的可运行代码。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class FPSTestExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new FPSTestExample());
}
private long startTime,endTime,countInterval;
private final DrawingPanel drawingPanel;
public FPSTestExample() {
this.drawingPanel = new DrawingPanel();
}
@Override
public void run() {
JFrame frame = new JFrame("FPS Test Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawingPanel,BorderLayout.CENTER);
frame.add(createButtonPanel(),BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5,5,5));
JButton button = new JButton("Start");
panel.add(button);
button.addActionListener(new ActionListener() {
private Timer timer;
@Override
public void actionPerformed(ActionEvent event) {
if (timer == null) {
FPSTestExample.this.startTime = System.currentTimeMillis();
timer = new Timer(15,new FPSListener());
timer.start();
}
}
});
return panel;
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setPreferredSize(new Dimension(300,200));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (FPSTestExample.this.endTime > 0L) {
long elapsedTime = FPSTestExample.this.endTime -
FPSTestExample.this.startTime;
int fps = (int) (FPSTestExample.this.countInterval * 1000L /
elapsedTime);
String fpsString = "FPS: " + fps;
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
g2d.setFont(getFont().deriveFont(48f));
FontMetrics fm = g2d.getFontMetrics();
Rectangle2D r2d = fm.getStringBounds(fpsString,g2d);
int width = (int) Math.round(r2d.getWidth());
int height = (int) Math.round(r2d.getHeight());
int x = (getWidth() - width) / 2;
int y = (getHeight() - height + fm.getAscent()) / 2;
g2d.drawString(fpsString,x,y);
}
}
}
public class FPSListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent event) {
FPSTestExample.this.endTime = System.currentTimeMillis();
FPSTestExample.this.countInterval++;
FPSTestExample.this.drawingPanel.repaint();
}
}
}