通过调用Thread.sleep
,您可能会阻塞事件调度线程,该线程负责处理绘制请求等。这意味着在您的循环真正完成之前,EDT 将无法处理面板的更新位置。
相反,使用javax.swing.Timer
...
查看Swing 中的并发以获取更多详细信息
更新
顺便说一句,这是在 FlowLayout 下,所以我认为这可能与它有关,但不确定
您正在与布局管理器作斗争,您会发现一旦您使用了正在使用revalidate
的容器usePanel
,它将重新定位回布局管理器希望组件所在的位置。
尝试查看Sliding-Layout以获得更好的解决方案
更新了一个基本示例
动画是随时间变化的幻觉。在更新 UI 时,Swing 对开发人员提出了一些相当严格的要求。
除了布局管理器之类的东西,Swing 要求对 UI 的所有交互和修改都在事件调度线程的上下文中完成。它还要求在 EDT 以外的另一个线程中执行任何长时间运行或阻塞的进程。
这使我们陷入了第 22 个问题。我们需要在后台运行,因此我们不会阻止 EDT 处理绘制请求(除其他外),但我们需要从 EDT 的上下文中更新我们的组件......
对我们来说幸运的是,有许多可用的解决方案。您的问题最简单的方法是使用javax.swing.Timer
此示例使用根窗格的玻璃窗格功能来提供覆盖滑出,有关更多详细信息,请参阅如何使用根窗格。
它还使用可变定时动画。也就是说,面板的位置是基于动画中时间的进展,而不是一些固定的增量
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class SlidingPane {
private SlidePane slidePane = new SlidePane();
public static void main(String[] args) {
new SlidingPane();
}
public SlidingPane() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JButton slideButton = new JButton("Slide");
slideButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
slidePane.slide();
}
});
JFrame frame = new JFrame("Testing");
JPanel glassPane = new JPanel(null);
glassPane.setOpaque(false);
glassPane.add(slidePane);
frame.setGlassPane(glassPane);
glassPane.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
frame.add(new JLabel("Look ma, no hands!"), gbc);
frame.add(slideButton, gbc);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class SlidePane extends JPanel {
private long startTime = -1;
private int runTime = 1000;
private int startX;
private int targetX;
private boolean slideIn = false;
private Timer slideTimer;
public SlidePane() {
setBackground(Color.DARK_GRAY);
setBorder(new LineBorder(Color.BLACK));
setLocation(-getPreferredSize().width, 0);
setLayout(new GridBagLayout());
JLabel label = new JLabel("I'm your overload");
label.setForeground(Color.WHITE);
add(label);
slideTimer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
long diff = System.currentTimeMillis() - startTime;
double progress = (double)diff / (double)runTime;
if (progress >= 1d) {
progress = 1d;
slideTimer.stop();
startTime = -1;
}
Container parent = getParent();
int height = parent.getHeight();
setSize(getPreferredSize().width, height);
int x = calculateProgress(startX, targetX, progress);
setLocation(x, 0);
revalidate();
repaint();
}
});
}
protected int calculateProgress(int startValue, int endValue, double fraction) {
int value = 0;
int distance = endValue - startValue;
value = (int) Math.round((double) distance * fraction);
value += startValue;
return value;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
public void slide() {
slideTimer.stop();
startTime = System.currentTimeMillis();
slideIn = !slideIn;
startX = getX();
targetX = 0;
if (!slideIn) {
targetX = -getPreferredSize().width;
}
slideTimer.start();
}
}
}