Model View Presenter and Composite Views
Asked Answered
B

3

7

I'm trying to follow the MVP (specifically Passive-View) pattern in a java swing ui application.

The basic design of the application reminds a wizard control. The screen is divided to two main parts:

  • an active view.
  • a static navigation bar, with navigation buttons.

The user can use buttons to change the active view, but the bar is always displayed.

Modeling this scenario, I have a bunch of diffirent screers, each with it's own presenter, view interface and view implementation (using JPanel). Then I have a Shell presenter, view intefrace and view implementation, using a JFrame. The idea is that the shell will load first and always by displayed, showing the bottom navigation bar and leaving a space for the active view. The shell presenter will allow setting the current active screen, somewhat like this:

interface View {
}

class Presenter {
    View view;

    public Presenter(View view) {
        this.view = view;
    }

    public View getView() {
        return view;
    }

}

interface ShellView extends View {
    void setActiveView(View activeView);
}

class ShellPresenter extends Presenter {
    private ShellView shellView;

    public void setActivePresenter(Presenter activePresenter) { 
        shellView.setActiveView(activePresenter.getView());
    }
}

class ShellFrame implements ShellView {
    private JFrame frame;
    private JPanel activePanel;
    private JPanel navigationBar;

    public ShellFrame() {
        Container c = frame.getContentPane();
        c.add(activePanel);
        c.add(navigationBar);
    }

    public setActiveView(View activeView) {
        ???
    }
}

The problem is in the setActiveView method: I'm not sure how to set the activeView to the activePanel, when the View interface is general and as such doens't know anything about JPanels. Obviously I wouldn't want my presenters to know about JPanels as well.

Burgas answered 10/2, 2010 at 21:14 Comment(1)
Did you ever figure out how to do this? None of these answers are particularly good. I just posted this thread: #17836921Ultramarine
S
1

Could you modify the definition of View to:

interface View {
    JComponent getContainer();
}

So that each view can easily get the view contents? The shell needn't know what implementation of JComponent is being returned.

Steiner answered 10/2, 2010 at 23:40 Comment(0)
G
1

Your View interface needs to provide some way to obtain something displayable in a JPanel:

interface View {
    Component getComponent();
}

Then in ShellFrame (assuming you use BorderLayout, as I would) you can set the view in the following way:

public setActiveView(View activeView) {
   activePanel.add(activeView.getComponent(), BorderLayout.CENTER);     
}
Giotto answered 16/2, 2010 at 21:8 Comment(0)
U
1

The problem is that your JFrame is the View, not each individual active sub-view. The view that is active right now is a rendering job, and therefore should be done by the View, not by the Presenter. Imagine you wanted to swap in a different View that instead of having only one subview visible, they were all visible, but the one that was active had a different background color. The presenter just controls which one is active, but the View controls what is meant by active.

So your task cannot be done without breaking encapsulation because you are trying to do a job that by it's very nature breaks encapsulation. So I would do something like this:

class PresenterManager {
    private Presenter activePresenter;
    private List<Presenter> allPresenters;

    IView view;

    PresenterManager(IView view) {
        this.view = view;
        view.subscribe(this);
    }

    void addPresenter(Presenter p) {
        allPresenters.add(p);
    }

    void setView(int index) {
        view.setView(index);
        activePresenter = allPresenters.get(index);
    }
}

class SwingView implements IView {
    List<SubView> allViews;
    SubView activeView;
    int lastIndex;

    public void setView(int index) {
        if(index != lastIndex) {
            activeView.setVisible(false);
            activeView = allViews.get(index);
            lastIndex = index;
        }
    }
}
Ultramarine answered 2/8, 2013 at 17:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.