Java 在JFrame中从JPanel内部切换JPanel

Java 在JFrame中从JPanel内部切换JPanel,java,swing,jframe,jpanel,Java,Swing,Jframe,Jpanel,我试图构建一个基于按钮的动态应用程序,这些按钮从创建的面板内部切换JFrame的主面板(这是我应用程序的顶部内容) 这个问题更多地涉及设计问题,而不是开发问题。我不确定我的设计,但我会尽力解释的 我有一个代表我的应用程序的JFrame,它包含一个JTabbedPane(其中有多个选项卡)。每个选项卡都包含一个默认JPanel,在该JPanel中,我调用一个呈现我的视图的控制器(我尝试使用MVC模式) 但是从所谓的视图来看,我有一些按钮必须切换JPanel的内容(JTabbedPane中包含的JP

我试图构建一个基于按钮的动态应用程序,这些按钮从创建的面板内部切换JFrame的主面板(这是我应用程序的顶部内容)

这个问题更多地涉及设计问题,而不是开发问题。我不确定我的设计,但我会尽力解释的

我有一个代表我的应用程序的JFrame,它包含一个JTabbedPane(其中有多个选项卡)。每个选项卡都包含一个默认JPanel,在该JPanel中,我调用一个呈现我的视图的控制器(我尝试使用MVC模式)

但是从所谓的视图来看,我有一些按钮必须切换JPanel的内容(JTabbedPane中包含的JPanel)。我正在寻找切换视图的最佳方式,同时又不违反OOP的标准规则

示意图:

JFrame
    JTabbedPane
        JPanel
            View1
                Button (pointing somehow to View2)
        JPanel
            View3
                 Button (pointing somehow to View4)
当我在视图1中单击按钮时,我希望:

JFrame
    JTabbedPane
        JPanel
            View2
                ...
        JPanel
            View3
                 Button (pointing somehow to View4)
加载视图2后,如果我通过JTabbedPane进入视图3并单击View3.0按钮,我希望得到如下内容:

JFrame
    JTabbedPane
        JPanel
            View2
                ...
        JPanel
            View4
                 ...
正如我所说,这更多的是一个设计问题。我的“设计”是否足够好,不必为每个组件提供JFrame的参考?我该怎么做呢

我希望我的课文中没有那么多错误,英语不是我的母语。此外,欢迎对我的英语写作技巧进行任何修改


谢谢。

我想说,您的JTabbedPane是对面板感兴趣的类-因为它是JTabbedPane,可以切换其内容

因此,我建议将JTabbedPane扩展到?TabbedPane(where?代表“我的”或项目中的任何命名约定),并将其扩展为ActionListener,如下所示:

public class MyTabbedPane extends JTabbedPane implements ActionListener {
    ...
    public void actionPerformed(ActionEvent aev) {
        // replace panels
        revalidate();
        repaint();
    }
}
然后在需要的地方(可能是按钮)将选项卡式窗格注册为操作侦听器

如果要切换到多个面板,则需要更多代码来传递状态。 您可以编写自己的侦听器:

public class PanelSwitchActionListener {
    public void panelSwitchRequested(...);
}

然后在panelswitchrequest()方法的(…)部分传递枚举,指明要切换到哪个面板,甚至可能是面板本身。然后在按钮中添加一个操作(监听器),并使它们对注册的PanelSwitchActionListener(很可能是您的JTabbedPane)触发panelSwitchRequested()。

我会尝试使用模型。该模型将维护一个视图列表以及一种方法,通过该列表,给定一个视图引用,它可以返回下一个视图(即
model.getNextView(view)


这个模型可以传递给您的控制器(在这里我要说的是pafau提到的定制JTabbedPane),它可以根据需要构造所有选项卡。通过这种方式,您可以将导航机制与视图分离,并将逻辑委托给模型。

我个人会重新考虑使用选项卡式窗格和视图之间切换的设计。通常情况下,首选带标签的窗格玻璃

然而,如果你仍然需要这样做,试着用不同的方式思考你的观点。我认为JPanel实际上是观点。如果将控制器附加到每个视图,将模型附加到每个控制器,则可以在它们之间切换,而不会在视图或控制器中添加太多逻辑,但仍然可以将视图与模型分离。我将一个使用和的粗略示例放在一起,但在其他方面相当简单。当然,所有类都应该重命名为适合您的项目的类

View.java

public class View extends JPanel {

    private CardLayout layout;


    public View(Controller controller, JComponent... subviews) {
        layout = new CardLayout();
        setLayout(layout);

        for (int index = 0; index < subviews.length; ++index) {
            JButton changeButton = new JButton("Change");
            changeButton.addActionListener(controller);
            subviews[index].add(changeButton);
            add(subviews[index], "View" + index);
        }
    }

    public void setSubview(int index) {
        layout.show(this, "View" + index);
    }

}
public class NullView extends View {

    public NullView() {
        super(null);
    }

    @Override
    public void setSubview(int index) {
        // do nothing
    }

}
public class Controller implements ActionListener {

    private Model model;

    private View view;

    public Controller(Model model) {
        this.model = model;
        this.view = new NullView();
    }

    public void setView(View view) {
        this.view = view;
    }

    public void actionPerformed(ActionEvent e) {
        model.nextIndex();
        view.setSubview(model.getCurIndex());
    }

}
public class Model {

    private int nViews;

    private int curIndex;

    public Model(int nViews) {
        if (nViews <= 0) nViews = 1;
        this.nViews = nViews;
    }

    public void nextIndex() {
        curIndex = (curIndex + 1) % nViews;
    }

    public int getCurIndex() {
        return curIndex;
    }

 }
Controller.java

public class View extends JPanel {

    private CardLayout layout;


    public View(Controller controller, JComponent... subviews) {
        layout = new CardLayout();
        setLayout(layout);

        for (int index = 0; index < subviews.length; ++index) {
            JButton changeButton = new JButton("Change");
            changeButton.addActionListener(controller);
            subviews[index].add(changeButton);
            add(subviews[index], "View" + index);
        }
    }

    public void setSubview(int index) {
        layout.show(this, "View" + index);
    }

}
public class NullView extends View {

    public NullView() {
        super(null);
    }

    @Override
    public void setSubview(int index) {
        // do nothing
    }

}
public class Controller implements ActionListener {

    private Model model;

    private View view;

    public Controller(Model model) {
        this.model = model;
        this.view = new NullView();
    }

    public void setView(View view) {
        this.view = view;
    }

    public void actionPerformed(ActionEvent e) {
        model.nextIndex();
        view.setSubview(model.getCurIndex());
    }

}
public class Model {

    private int nViews;

    private int curIndex;

    public Model(int nViews) {
        if (nViews <= 0) nViews = 1;
        this.nViews = nViews;
    }

    public void nextIndex() {
        curIndex = (curIndex + 1) % nViews;
    }

    public int getCurIndex() {
        return curIndex;
    }

 }
Model.java

public class View extends JPanel {

    private CardLayout layout;


    public View(Controller controller, JComponent... subviews) {
        layout = new CardLayout();
        setLayout(layout);

        for (int index = 0; index < subviews.length; ++index) {
            JButton changeButton = new JButton("Change");
            changeButton.addActionListener(controller);
            subviews[index].add(changeButton);
            add(subviews[index], "View" + index);
        }
    }

    public void setSubview(int index) {
        layout.show(this, "View" + index);
    }

}
public class NullView extends View {

    public NullView() {
        super(null);
    }

    @Override
    public void setSubview(int index) {
        // do nothing
    }

}
public class Controller implements ActionListener {

    private Model model;

    private View view;

    public Controller(Model model) {
        this.model = model;
        this.view = new NullView();
    }

    public void setView(View view) {
        this.view = view;
    }

    public void actionPerformed(ActionEvent e) {
        model.nextIndex();
        view.setSubview(model.getCurIndex());
    }

}
public class Model {

    private int nViews;

    private int curIndex;

    public Model(int nViews) {
        if (nViews <= 0) nViews = 1;
        this.nViews = nViews;
    }

    public void nextIndex() {
        curIndex = (curIndex + 1) % nViews;
    }

    public int getCurIndex() {
        return curIndex;
    }

 }

是否需要将视图从View2和View4切换回View1和View3?还是另一种观点?或者你只是在一个方向上切换?是的,也许从视图4我将不得不切换回视图3(从视图2切换到视图1),但它永远不会是选项卡间的切换。+1为了解释好,一定要鼓励。谢谢!我不知道为什么我不考虑听众,这是显而易见的。然而,它给我带来了一个与Java更相关的问题。在我的JTabbedPane中,我没有任何对按钮的引用(按钮将取决于从一个视图到另一个视图),因此我无法在JTabbedPane中添加侦听器。而且,在我看来,我可以添加监听器,但是我没有任何对JTabbedPane的引用,所以我可以将它传递给我自己的监听器。我觉得自己被困在这里了。我看到了一个与我类似的问题:大卫的回答说要给孩子们一个最重要部分的参考。在我的例子中,按照这个方法,我应该给我的控制器一个对JTabbedPane的引用,在控制器中给这个对视图的引用。但是,这在Java(或任何语言)中是一种好的实践吗?这种方法不是违反了MVC的规则吗?也许我错了,因为我只是通过Web框架使用MVC(例如Symfony),但每次我使用它时,控制器都负责视图。此外,通常(在web开发中)有一个控制器和多个视图,而没有任何模型。或控制器,查看并复制与控制器相关的模型,以便对模型执行困难的操作。那么,将视图附加到模型上不是很奇怪吗?M代表模型的右边。控制器将使用模型来控制视图?通过这种方式,您可以在不影响控制器或视图工作方式的情况下更改模型。这就是我的理解anyway@pataprout我不知道MVC是否有任何标准规则。我没有太多时间来分析你的答案,但我想感谢你花了这么多时间,以及你非常精确的答案。我会尽快分析的。