我试图用动画隐藏 JSplitPane
。通过隐藏,我的意思是 setDividerLocation(0)
因此其左侧组件不可见(技术上它是可见的,但宽度为零):
public class SplitPaneTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.setBorder(BorderFactory.createLineBorder(Color.green));
JPanel rightPanel = new JPanel(new GridLayout(60, 60));
for (int i = 0; i < 60 * 60; i++) {
// rightPanel.add(new JLabel("s"));
}
rightPanel.setBorder(BorderFactory.createLineBorder(Color.red));
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
frame.add(splitPane);
JButton button = new JButton("Press me to hide");
button.addActionListener(e -> hideWithAnimation(splitPane));
leftPanel.add(button, BorderLayout.PAGE_START);
frame.setMaximumSize(new Dimension(800, 800));
frame.setSize(800, 800);
frame.setLocationByPlatform(true);
frame.setVisible(true);
});
}
private static void hideWithAnimation(JSplitPane splitPane) {
final Timer timer = new Timer(10, null);
timer.addActionListener(e -> {
splitPane.setDividerLocation(Math.max(0, splitPane.getDividerLocation() - 3));
if (splitPane.getDividerLocation() == 0)
timer.stop();
});
timer.start();
}
}
如果你运行它,会发现一切看起来都很好,并且动画运行流畅。
但是,在实际应用中,JSplitPane
的右侧是一个带有 CardLayout
的 JPanel
,每个卡片都有很多组件。
如果您取消注释此行以模拟组件数量:
// rightPanel.add(new JLabel("s"));
然后重新运行上面的例子,你会发现动画不再流畅运行。所以,问题是,是否有可能使其平滑(-ier)?
我不知道如何找到解决方案 - 如果有的话。
根据我的研究,我注册了一个全局ComponentListener
:
Toolkit.getDefaultToolkit()
.addAWTEventListener(System.out::println, AWTEvent.COMPONENT_EVENT_MASK);
并看到大量正在触发的事件。因此,我认为问题的根源在于每个组件触发了大量的组件事件。另外,似乎具有自定义渲染器的组件(例如 JList
- ListCellRenderer
和 JTable
- TableCellRenderer
)、组件事件正在为所有渲染器触发。例如,如果 JList
有 30 个元素,则只会为其触发 30 个事件(组件)。似乎(这就是我提到它的原因)对于 CardLayout,“不可见”组件也会发生事件。
我知道 60*60
对你来说可能听起来很疯狂,但在真实的应用程序中(我的有 ~1500),因为它是有意义的,绘画更重。
最佳答案
I know that 60*60 might sound crazy to you, but in a real application (mine has ~1500) as it makes sense, the painting is heavier.
每次更改分隔线位置时都会调用布局管理器,这会增加大量开销。
一种解决方案可能是在分隔符动画时停止调用布局管理器。这可以通过重写右侧面板的 doLayout()
方法来完成:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SplitPaneTest2 {
public static boolean doLayout = true;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.setBorder(BorderFactory.createLineBorder(Color.green));
JPanel rightPanel = new JPanel(new GridLayout(60, 60))
{
@Override
public void doLayout()
{
if (SplitPaneTest2.doLayout)
super.doLayout();
}
};
for (int i = 0; i < 60 * 60; i++) {
rightPanel.add(new JLabel("s"));
}
rightPanel.setBorder(BorderFactory.createLineBorder(Color.red));
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
frame.add(splitPane);
JButton button = new JButton("Press me to hide");
button.addActionListener(e -> hideWithAnimation(splitPane));
leftPanel.add(button, BorderLayout.PAGE_START);
frame.setMaximumSize(new Dimension(800, 800));
frame.setSize(800, 800);
frame.setLocationByPlatform(true);
frame.setVisible(true);
});
}
private static void hideWithAnimation(JSplitPane splitPane) {
SplitPaneTest2.doLayout = false;
final Timer timer = new Timer(10, null);
timer.addActionListener(e -> {
splitPane.setDividerLocation(Math.max(0, splitPane.getDividerLocation() - 3));
if (splitPane.getDividerLocation() == 0)
{
timer.stop();
SplitPaneTest2.doLayout = true;
splitPane.getRightComponent().revalidate();
}
});
timer.start();
}
}
编辑:
我不打算包括将充满组件的面板替换为使用组件图像的面板的测试,因为我认为动画是相同的,但由于这是其他人建议的,所以这是我的尝试您的评价:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
public class SplitPaneTest2 {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.setBorder(BorderFactory.createLineBorder(Color.green));
JPanel rightPanel = new JPanel(new GridLayout(60, 60));
for (int i = 0; i < 60 * 60; i++) {
rightPanel.add(new JLabel("s"));
}
rightPanel.setBorder(BorderFactory.createLineBorder(Color.red));
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
frame.add(splitPane);
JButton button = new JButton("Press me to hide");
button.addActionListener(e -> hideWithAnimation(splitPane));
leftPanel.add(button, BorderLayout.PAGE_START);
frame.setMaximumSize(new Dimension(800, 800));
frame.setSize(800, 800);
frame.setLocationByPlatform(true);
frame.setVisible(true);
});
}
private static void hideWithAnimation(JSplitPane splitPane) {
Component right = splitPane.getRightComponent();
Dimension size = right.getSize();
BufferedImage bi = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
right.paint( g );
g.dispose();
JLabel label = new JLabel( new ImageIcon( bi ) );
label.setHorizontalAlignment(JLabel.LEFT);
splitPane.setRightComponent( label );
splitPane.setDividerLocation( splitPane.getDividerLocation() );
final Timer timer = new Timer(10, null);
timer.addActionListener(e -> {
splitPane.setDividerLocation(Math.max(0, splitPane.getDividerLocation() - 3));
if (splitPane.getDividerLocation() == 0)
{
timer.stop();
splitPane.setRightComponent( right );
}
});
timer.start();
}
}
关于java - 当有数千个组件时使动画更快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63158480/