首页 > 解决方案 > 实时切换内容窗格

问题描述

我尝试制作带有介绍的java小项目,并在完整的视频或按键后跳到菜单。我为“游戏”的状态做了枚举。我如何切换 JPanel 并停止旧 JPanel 所做的一切,因为 intro 也在 Thread 中播放音乐。

contentPane.addMouseListener(new MouseAdapter() {
                public void mousePressed(MouseEvent e) {
                    System.out.println("Clicked");
                    State = GameState.MainMenu;
                }
            });
            if (State == GameState.Intro) {
                contentPane.removeAll();
                contentPane.add(intro);
                contentPane.revalidate();
                contentPane.repaint();
                intro.music.interrupt();
                System.out.println(State);
            } else if (State == GameState.MainMenu) {
                contentPane.removeAll();
                contentPane.add(menu);
                contentPane.revalidate();
                contentPane.repaint();
                System.out.println(State);
            }
        }

标签: javamultithreadingswingjpanel

解决方案


我建议的第一件事是使用CardLayout- 有关更多详细信息,请参阅如何使用 CardLayout

这将使视图之间的切换变得更加简单。

接下来,我建议设计一种独立于视图本身的“导航”系统。这个想法是,任何一个视图都应该知道或关心下一个(或上一个)视图,并且应该只向导航系统发出“简单”请求,例如“显示下一个视图”。由导航系统决定这意味着什么(以及如何驾驶它)。

这使您可以更好地控制整个导航过程的定义和管理。

然后我会将它与可选的“生命周期”概念结合起来,这将允许导航系统在导航状态发生变化时通知这些实现。

“如何”实际做到这一点,很大程度上取决于你的整体设计和意图,但它可能看起来像......

LifeCycle

public interface LifeCycle {
    public void willShow();
    public void didShow();
    public void willHide();
    public void didHide();
}

NavigationController

public interface NavigationController {
    public void next();
}

public interface View {
    public String getName();
    public JComponent getView();
}

默认实现...

public class DefaultView implements View {

    private String name;
    private JComponent view;

    public DefaultView(String name, JComponent view) {
        this.name = name;
        this.view = view;
    }
    
    @Override
    public String getName() {
        return name;
    }

    @Override
    public JComponent getView() {
        return view;
    }
    
}

public class DefaultNavigationController implements NavigationController {
    private List<View> views;
    private Container parent;
    private CardLayout layout;
    
    private View currentView;

    public DefaultNavigationController(Container parent, CardLayout layout) {
        this.parent = parent;
        this.layout = layout;
    }

    protected Container getParent() {
        return parent;
    }

    protected CardLayout getLayout() {
        return layout;
    }
    
    public void add(String name, JComponent comp) {
        getParent().add(comp, name);
        views.add(new DefaultView(name, comp));
    }

    protected List<View> getViews() {
        return views;
    }

    @Override
    public void next() {
        List<View> views = getViews();
        if (views.isEmpty()) {
            return;
        }
        int index = (currentView == null ? -1 : views.indexOf(currentView)) + 1;
        if (index >= views.size()) {
            // This is the last view
            return;
        }
        View previousView = currentView;
        View nextView = views.get(index);
        willHide(previousView);
        willShow(nextView);
        getLayout().show(getParent(), nextView.getName());
        didHide(previousView);
        didShow(nextView);
        
        currentView = nextView;
    }
    
    protected void willHide(View view) {
        if (view != null && view.getView() instanceof LifeCycle) {
            LifeCycle cycle = (LifeCycle)view.getView();
            cycle.willHide();
        }
    }
    
    protected void willShow(View view) {
        if (view != null && view.getView() instanceof LifeCycle) {
            LifeCycle cycle = (LifeCycle)view.getView();
            cycle.willShow();
        }
    }
    
    protected void didHide(View view) {
        if (view != null && view.getView() instanceof LifeCycle) {
            LifeCycle cycle = (LifeCycle)view.getView();
            cycle.didHide();
        }
    }
    
    protected void didShow(View view) {
        if (view != null && view.getView() instanceof LifeCycle) {
            LifeCycle cycle = (LifeCycle)view.getView();
            cycle.didShow();
        }
    }
}

如您所见,我喜欢使用interfaces,这对系统的其他部分隐藏了实现细节,并为我提供了更好的定制点。例如,组件不关心“如何”NavigationController实现,只关心它遵循特定且可定义的工作流程。

好的,但这怎么可能?让我们从支持的组件的基本实现开始LifeCycle,它可能看起来像......

public class IntroPane extends JPanel implements LifeCycle {
    private NavigationController navigationController;

    protected NavigationController getNavigationController() {
        return navigationController;
    }
    
    protected void next() {
        getNavigationController().next();
    }

    @Override
    public void willShow() {
        // Prepare any resources
        // Lazy load resources
        // Do other preparation work which doesn't need to be done in the
        // constructor or which needs to be recreated because of actions
        // in will/didHide
    }

    @Override
    public void didShow() {
        // Start animation loops and other "UI" related stuff
    }

    @Override
    public void willHide() {
        // Pause the animation loop and other "UI" related stuff
    }

    @Override
    public void didHide() {
        // Dispose of system intensive resources which can be recreated
        // in willShow
    }
}

你可以使用类似的东西来设置它......

CardLayout cardLayout = new CardLayout();
JPanel contentPane = new JPanel(cardLayout);

DefaultNavigationController navigationController = new DefaultNavigationController(contentPane, cardLayout);
navigationController.add("intro", new IntroPane());
navigationController.add("menu", new MenuPane());
navigationController.add("game", new GamePane());

“但是等等,”你说,“这是一个线性导航,我需要一些更动态的东西!”

远点。在那种情况下,我会创建,是的,另一个interface!这将从(线性)扩展NavigationController并提供一种“显示”任意命名视图的方法。

为什么要这样做?因为并非所有视图都需要决定应该显示哪个视图的能力,所以有些视图只想显示下一个(或上一个)视图,例如,MenuPane可能是唯一实际需要非线性导航控制器的视图,所有其他视图只需要能够向后或向前移动

但是我该如何阻止Thread

这是一个复杂的问题,并不总是很容易回答。“基本”答案是,您需要定义一个控制机制,该机制可以与线程上下文的“可中断”支持一起工作Thread并退出线程上下文。

也许如何杀死 Java 线程将是一个起点


推荐阅读