【问题标题】:Change current card in card layout with slide effect in swing在卡片布局中更改当前卡片,并在摇摆中具有滑动效果
【发布时间】:2012-07-05 11:43:47
【问题描述】:

我正在尝试使用幻灯片效果更改卡片布局中的当前可见性。但是我在幻灯片的开头看到了一个我无法调试/解决的轻弹。我怎样才能避免这种轻弹?

这里是重现错误的示例代码:

import java.awt.CardLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Main {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel(new CardLayout());
        JLabel label1 = new JLabel("Harry Joy");
        panel.add(label1, "1");
        JLabel label2 = new JLabel("Harsh Raval");
        panel.add(label2, "2");
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(500L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            label2.setLocation(label1.getX() + 10 - label1.getWidth(), label1.getY());
            label1.setLocation(label1.getX() + 10, label1.getY());
            label2.setVisible(true);
        }
        label2.setVisible(false);
        CardLayout cl = (CardLayout)panel.getLayout(); 
        cl.next(panel);
    }
}

这里首先显示label1("Harry Joy")。然后我使label2("Harsh Raval") 可见并尝试更改两者的位置以提供幻灯片效果。但是这里发生的是第一次两个标签都显示在彼此的顶部,然后它开始滑动。我怎么能阻止这种情况,我的意思是把两个标签放在一起显示?如果你运行一次,你就能更好地理解我的意思。

【问题讨论】:

  • “这里是 SSCCE:” 嗯.. 没有。 SSCCE 需要格式良好的类和 main()(除非是小程序)。
  • 这不是 SSCCE,因为没有 main 和 import。您的示例仅在 EDT 用完时才“有效”,这是一个坏主意。
  • 查看this answer。它可能会给你一些想法..
  • @AndrewThompson 嗯.. 这是幻灯片效果的一个很好的例子。我会使用它,但我仍然很想知道我所面临的原因。如果你能照亮它?
  • 两个面板可以动画JSplitPane,如图here

标签: java swing cardlayout


【解决方案1】:

主要问题是您踩到了 CardLayout 的脚趾。 CardLayout 管理组件的边界(位置和大小)和可见性,所以你的循环在这里:

    for (int i = 0; i < 10; i++) {
        try {
            Thread.sleep(500L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        label2.setLocation(label1.getX() + 10 - label1.getWidth(), label1.getY());
        label1.setLocation(label1.getX() + 10, label1.getY());
        label2.setVisible(true);
    }

与 CardLayout 的作用相冲突。默认情况下,CardLayout 会自动显示您添加的第一个组件并隐藏所有其他组件。它还将所有组件的边界设置为相同的边界。

在第一次迭代结束时,label2 的可见性发生变化(从 falsetrue),这最终会触发 CardLayout 重新执行组件的布局,从而将所有组件的边界设置为相同的边界这就是您看到重叠的原因。 CardLayout 并不是要让多个组件同时可见。

请注意,您是在 EDT(事件调度线程)之外运行所有这些,这确实是个坏主意。它可能导致死锁和不可预知的行为(例如您在此处看到的)。

【讨论】:

  • 在我的应用程序中,我使用SwingUtilities.invokeLater(..) 并面临同样的问题。所以我不认为这是因为我在 EDT 上用完了它。关于将 label2 的可见性设置为 true:我这样做是因为如果我不这样做,那么我将看不到 label2 滑动,只会首先滑出,然后最后会显示 label2,这不是我需要的情况。我尝试删除setVisible(true) 行并且效果很好,根本没有重叠,但是正如我所说,只有第一个标签的滑出效果而第二个标签没有滑动效果。所以看起来问题是由于设置 label2 的可见性。
  • @HarryJoy 我在SwingUtilities.invokeLater() 中运行与上面显示的示例相同的示例,您将看不到任何内容,因为您将阻塞 EDT,这意味着无法处理“绘制事件”。重叠也不会发生,因为label2 的可见性更改会触发要执行的布局,而且还会在 EventQueue 的未来事件中发生,因此您的问题与您在 EDT 和可见性之外运行这一事实有关更改标签2。考虑使用 Swing Timer 来制作滑动效果。
  • 好的。我想我必须告诉你我到底有什么:首先我创建一个框架和我的自定义面板,其中包含 CardLayout,使这个框架可见。我在 main 方法和SwingUtilities.invokeLater() 内部执行此操作。然后我启动一个java.swing.Timer 来更改组件的位置,这个例子中循环内部的内容实际上是ActionListeneractionPerformed() 方法内部Timer。这是你的建议吗?
  • @HarryJoy 1)我会更改 LayoutManager(可能考虑使用空布局或实现您自己的) 2)在您的 actionPerformed() 中启动滑动效果。使用 Swing 计时器来执行滑动效果的各个步骤。 Andrew Thompson 在评论中提供的链接实际上与我在这里描述的非常接近。还有第三方库可以为你做到这一点(比如 SwingX)
  • @HarryJoy 在 Swing Timer 中,您不必调用 SwingUtilities.invokeLater,因为您已经在 EDT 中,因此您可以直接在 actionPerformed 中安全地操作 Swing 组件。至于重叠的问题,都是和 CardLayout 冲突造成的。尽管事件发生的顺序与您的问题不完全相同,但原因和后果是相同的。解决方法是一样的:实现自己的LayoutManager,使用空布局或者使用第三方lib。
【解决方案2】:

【讨论】:

  • 在这个例子中也发生了同样的事情。当您从最后一个组件滑到第一个组件时,您有时会看到与我相同的问题。要在此示例中正确地增加侦听器类中的计时器延迟。我很想知道这个问题的原因以及可能的解决方案。
【解决方案3】:

几个 cmets,可能只是 cmets

  • Thread.sleep 的想法是正确的,但仅从 Runnable.Thread 开始

  • 对于 Swing GUI 有 Swing Timer,

  • Swing Timer 保证移动由标准 LayoutManager 铺设的任何可见JComponents(不闪烁)

但在这个(我的代码示例)案例中,我再次使用 Swing Timer,也不是所有事件都将在 EDT 上完成,方法是从 Runnable 调用自己的生活而不影响其余的 Swing 事件,GUI e.i.,

请注意,此问题基于 AWT 嵌套/继承,例如对于 JComboBoxes 弹出窗口(调整大小并使用 doLayout())不起作用

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ShakingButtonDemo implements Runnable {

    private JButton button;
    private JRadioButton radioWholeButton;
    private JRadioButton radioTextOnly;

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new ShakingButtonDemo());
    }

    @Override
    public void run() {
        radioWholeButton = new JRadioButton("The whole button");
        radioTextOnly = new JRadioButton("Button text only");
        radioWholeButton.setSelected(true);
        ButtonGroup bg = new ButtonGroup();
        bg.add(radioWholeButton);
        bg.add(radioTextOnly);
        button = new JButton("  Shake with this Button  ");
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                shakeButton(radioWholeButton.isSelected());
            }
        });
        JPanel p1 = new JPanel();
        p1.setBorder(BorderFactory.createTitledBorder("Shake Options"));
        p1.setLayout(new GridLayout(0, 1));
        p1.add(radioWholeButton);
        p1.add(radioTextOnly);
        JPanel p2 = new JPanel();
        p2.setLayout(new GridLayout(0, 1));
        p2.add(button);
        JFrame frame = new JFrame();
        frame.setTitle("Shaking Button Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(p1, BorderLayout.NORTH);
        frame.add(p2, BorderLayout.SOUTH);
        frame.setSize(240, 160);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void shakeButton(final boolean shakeWholeButton) {
        final Point point = button.getLocation();
        final Insets margin = button.getMargin();
        final int delay = 75;
        Runnable r = new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 30; i++) {
                    try {
                        if (shakeWholeButton) {
                            moveButton(new Point(point.x + 5, point.y));
                            Thread.sleep(delay);
                            moveButton(point);
                            Thread.sleep(delay);
                            moveButton(new Point(point.x - 5, point.y));
                            Thread.sleep(delay);
                            moveButton(point);
                            Thread.sleep(delay);
                        } else {// text only
                            setButtonMargin(new Insets(margin.top, margin.left + 3, margin.bottom, margin.right - 2));
                            Thread.sleep(delay);
                            setButtonMargin(margin);
                            Thread.sleep(delay);
                            setButtonMargin(new Insets(margin.top, margin.left - 2, margin.bottom, margin.right + 3));
                            Thread.sleep(delay);
                            setButtonMargin(margin);
                            Thread.sleep(delay);
                        }
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        };
        Thread t = new Thread(r);
        t.start();
    }

    private void moveButton(final Point p) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                button.setLocation(p);
            }
        });
    }

    private void setButtonMargin(final Insets margin) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                button.setMargin(margin);
            }
        });
    }
}

【讨论】:

    猜你喜欢
    • 2016-10-24
    • 2012-05-30
    • 2012-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-09
    相关资源
    最近更新 更多