Creating an Outlook style user interface in Java? - java

I'm looking to create an Outlook style UI in a Java desktop app, with a list of contexts or nodes in a lefthand pane, and the selected context in a pane on the right. How do I go about this?
I'm looking for a bit more detail than 'use a JFrame'. A tutorial or walk through would be good, or some skeleton code, or a framework/library that provides this kind of thing out of the box.
Thanks.
Edit
My (edited) code so far:
UIPanel
public class UIPanel extends javax.swing.JPanel {
private final JSplitPane splitPane;
public UIPanel() {
super(new BorderLayout());
initComponents();
JPanel contextPnl = new ContextPanel();
JPanel treePnl = new NodePanel(contextPnl);
this.splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
true, new JScrollPane(treePnl), new JScrollPane(contextPnl));
add(splitPane, BorderLayout.CENTER);
//not sure I need these?
splitPane.setVisible(true);
treePnl.setVisible(true);
contextPnl.setVisible(true);
}
NodePanel
public class NodePanel extends javax.swing.JPanel {
JPanel _contextPanel;
public NodePanel(JPanel contextPanel) {
initComponents();
_contextPanel = contextPanel;
initialise();
}
private void initialise(){
nodeTree.addTreeSelectionListener(getTreeListener());
}
private TreeSelectionListener getTreeListener(){
return new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)
nodeTree.getLastSelectedPathComponent();
// if nothing is selected
if (node == null)
return;
// get selected node
Object nodeInfo = node.getUserObject();
CardLayout layout = (CardLayout) _contextPanel.getLayout();
//layout.show(_contextPanel, "test"); //show context for selected node
}
};
}
ContextPanel
public class ContextPanel extends javax.swing.JPanel {
JPanel _cards;
final static String CONTEXT1 = "Context 1";
final static String CONTEXT2 = "Context 2";
JPanel _context1;
JPanel _context2;
public ContextPanel() {
initComponents();
intialiseContexts();
}
public void updateContext(String contextName){
//TODO
}
private void intialiseContexts(){
_context1 = new NodeContext();
_context2 = new NodeContext();
_cards = new JPanel(new CardLayout());
_cards.add(_context1, CONTEXT1);
_cards.add(_context2, CONTEXT2);
}

The key concept here is to define a JSplitPane as your top-level Component with a horizontal split. The left-hand side of the split pane becomes your "tree" view while the right-side is the context panel.
The trick is to use CardLayout for your context panel and to register a TreeSelectionListener with the tree panel's JTree so that whenever a tree node is selected, the CardLayout's show method is called in order to update what the context panel is currently showing. You will also need to add the various Components to the context panel in order for this approach to work.
public class UIPanel extends JPanel {
private static final String BLANK_CARD = "blank";
private final JSplitPane splitPane;
public UIPanel() {
super(new BorderLayout());
JPanel treePnl = createTreePanel();
JPanel contextPnl = createContextPanel();
this.splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
true, new JScrollPane(treePnl), new JScrollPane(contextPnl));
add(splitPane, BorderLayout.CENTER);
}
}
EDIT: Example Usage
public class Main {
public static void main(String[] args) {
// Kick off code to build and display UI on Event Dispatch Thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("UIPanel Example");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
// Add UIPanel to JFrame. Using CENTER layout means it will occupy all
// available space.
frame.add(new UIPanel(), BorderLayout.CENTER);
// Explicitly set frame size. Could use pack() instead.
frame.setSize(800, 600);
// Center frame on the primary display.
frame.setLocationRelativeTo(null);
// Finally make frame visible.
frame.setVisible(true);
}
});
}
}
Additional Advice
I can see you've created separate classes for your NodePanel and ContextPanel. Given the simplicity of these classes and how tightly coupled they are it probably makes more sense to embed all the UI components directly within UIPanel and have utility methods that build the two sub-panels. If you do keep with NodePanel and ContextPanel try to make them package private rather than public.
The CardLayout approach works well if you have a small(ish) number of nodes and you know them in advance (and hence can add their corresponding Components to the CardLayout in advance). If not, you should consider your context panel simply using BorderLayout and, whenever you click on a node you simply add the relevant node component to the BorderLayout.CENTER position of the NodePanel and call panel.revalidate() to cause it to perform its layout again. The reason I've used CardLayout in the past is that it means my nodes only need to remember one piece of information: The card name. However, now I think of it I don't see any real disadvantage with this other approach - In fact it's probably more flexible.

You might want to look at using a platform like eclipse as a starting point. It provides a very rich environment for creating these applications so you do not have to start everything from scratch. The online guides and help are very good and there are several books on the subject.

Related

Custom view classes in IntelliJ IDEA

So I am building a Swing layout in IntelliJ IDEA using the layout manager GridLayoutManager(IntelliJ).
It's going ok, I can layout all my stuff etc but everything is in the same code file and considering I am using a JPanel with a JTabbedPane, I would like each pane of the tabbed pane to be represented in a separate class.
How is this possible?
There are a couple ways you could do this, either extending JPanel or creating another class which contains a JPanel
Inheritance Based Solution
public class MyCustomPanel extends JPanel {
// implement your custom behavior in here
}
Then in your where you create your JTabbedPane you'd have something like this:
private void init() {
JTabbedPane jtp = new JTabbedPane();
JPanel jp = new MyCustomPanel();
jtp.add(jp);
}
Although this works, extending JPanel may cause headaches in the long run. Another approache, which favors composition over inheritance might look something like this:
Composition Based Solution
public class MyCustomPanel {
private JPanel myPanel = new JPanel();
public MyCustomPanel() {
// add your customizations to myPanel
}
public JPanel getPanel() {
return myPanel;
}
}
and then where you create your JTabbedPane
private void init() {
JTabbedPane jtp = new JTabbedPane();
MyCustomPanel mp = new MyCustomPanel();
jtp.add(mp.getPanel());
}

Swing - scroll JFrame content without using scrollbars

I have following problem. Is there a way to scroll Jframe content without using scrollbars, just to do it programatically in code. I have Japplet inside and I can't find a way to scroll its content without showing scrolls. Whole scrolling action should be performed not on user action, but when my thread wants to do so. Waiting for help, thanks.
I can't find any way to do that. I was trying to add my component (Applet) to Jscrollpane and that to jframe, but it causes situation, when only white screen is displayed.
JFrame class:
public class SimulationFrame extends JFrame {
private SimulationWindow simulationWindow;
public SimulationFrame() throws HeadlessException {
super(PropertiesHelper.getWindowTitle());
simulationWindow = new SimulationWindow();
JScrollPane scrollPane = new JScrollPane(simulationWindow);
this.getContentPane().add(scrollPane);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
}
}
JComponent:
public SimulationWindow() {
setLayout(new BorderLayout());
graph = GraphHelper.provideGraphInstance();
Dimension layoutSize = new Dimension(PropertiesHelper.getGraphHolderWidth(),
PropertiesHelper.getGraphHolderHeight());
graphLayout = new StaticLayout<Checkpoint, Route>(graph, new CheckpointPositionTransformer());
graphLayout.setSize(layoutSize);
visualizationViewer = new VisualizationViewer<Checkpoint, Route>(graphLayout, new Dimension(
PropertiesHelper.getWindowWidth(), PropertiesHelper.getWindowHeight()));
visualizationViewer.getRenderContext().setVertexLabelTransformer(new CheckpointLabelTransformer());
visualizationViewer.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR);
visualizationViewer.getRenderContext().setVertexFillPaintTransformer(new CheckpointColorTransformer());
visualizationViewer.getRenderContext().setEdgeDrawPaintTransformer(new RouteColorTransformer());
visualizationViewer.getRenderContext().setEdgeLabelTransformer(new RouteLabelTransformer());
final ImageIcon mapBackground = createMapBackground();
if (mapBackground != null) {
mapBackgroundImagePaintable = new BackgroundImagePaintable(visualizationViewer, mapBackground);
visualizationViewer.addPreRenderPaintable(mapBackgroundImagePaintable);
}
add(visualizationViewer);
scrollRectToVisible(new Rectangle(1000,100));
}
VisualizationViewer is a class that extends JPanel. Placing scrollRectToVisible in this constructor didn't works.
Any tips? Perhaps this implementation is wrong, where Jcomponent contains Jpanel itself?
Use method
public void scrollRectToVisible(Rectangle aRect)
of the JComponent added in JScrollPane

Controlling another class from a different class

I am having a bit of problem regarding Swing. I have a JFrame called FrameMain. Inside it is a JPanel called panelChoices.
When FrameMain is called/created, it fills up the panelChoices object with a number of PanelEntries objects, which is a JPanel with a number of JButtons in it (it is a different class that I wrote).
What I want to do is when I click one of the buttons inside the PanelEntries object, I want to destroy/remove FrameMain, along with the rest of it components (including the PanelEntries object that contains the JButton).
I've tried using super but it returns the JPanel (the PanelEntries object) that holds the JButton and not FrameMain that holds them all together. How can I achieve this?
EDIT: It seems that I am not clear enough, so here's a bit more information from my work. I don't have the actual code right now because I am on a different machine but I hope this will help elaborate my question.
public class FrameMain() {
private JFrame frameMain;
private JPanel panelChoices;
public FrameMain(args) {
createGUI();
loadData();
}
private void createGUI() {
JFrame frameMain = new JFrame();
JPanel panelChoices = new JPanel(new GridLayout(1,1));
frameMain.add(panel);
// removed formatting and other design codes since they are not important.
pack();
}
private void loadData() {
boolean available;
for (int i = 1; i <= 10; i++) {
// do some if/else and give value to boolean available
PanelEntries panel = new PanelEntries(i, available);
frameMain.add(panel);
// more code here to handle data.
}
}
}
public class PanelEntries() extends JPanel {
public PanelEntries(int num, boolean avb) {
JButton button = new JButton("Button Number " + num);
button.setEnabled(avb);
add(button);
// add action listener to created button so that it calls 'nextScreen()' when clicked.
// more code
pack();
}
private void nextScreen() {
// destroy/dispose MainFrame here.
// See Notes.
AnotherFrame anotherFrame = new AnotherFrame();
}
}
Notes:
All classes are inside their own .java file.
I need to know how to dispose FrameMain from the button inside the PanelEntries object, not just disposing a JFrame.
As per the given information,
If you want to exit the application, its not a big deal use System.exit(0); :)
If you mean to dispose the frame, jframe.dispose();
If you want to remove a componet / all components you can use .remove(Component) / .removeAll() etc
If this did not help, please re-write your question with more information.

JScrollPane Problems

If I add a JTable to a JPanel and then add that JPanel to a JScrollPane, whenever that JTable gains focus, the scroll pane automatically scrolls to the very bottom, which is bad.
I have many reasons for doing it like this, so I'm hoping for some kind solution to stop this auto-scrolling.
OH, and here's the kicker...It only seems to happen when running the app through JNLP/WebStart, and it does NOT do it in Eclipse, which is even more frustrating.
Here's a quick example that if you launch through JLNP, click the text field, click the table, then it auto-scrolls to the bottom:
import java.awt.*;
import javax.swing.*;
public class ScrollDemo extends JPanel{
private static final long serialVersionUID = 1L;
public ScrollDemo()
{
this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
JTable table = new JTable(100,6);
this.add(new JTextField());
JPanel panel = new JPanel();
panel.add(table);
JScrollPane scroll = new JScrollPane(panel);
this.add(scroll);
}
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("ScrollDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create and set up the content pane.
JComponent newContentPane = new ScrollDemo();
newContentPane.setOpaque(true); // content panes must be opaque
frame.setContentPane(newContentPane);
frame.setPreferredSize(new Dimension(500, 500));
// Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I am unable to reproduce the problem, but I can offer a few observations:
The frame's add() method forwards the component to the contentPane automatically.
Instead of setting the frame's preferred size, use setPreferredScrollableViewportSize() on the table.
If the unwanted scrolling is due to updating the table's model, see this example of how to temporarily suspend scrolling.
Kudos for using the event dispatch thread.
Addendum: The nested panel having a (default) FlowLayout is superfluous.
import java.awt.*;
import javax.swing.*;
/** #see https://stackoverflow.com/questions/8319388 */
public class ScrollDemo extends JPanel {
public ScrollDemo() {
this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
JTable table = new JTable(100, 6);
table.setPreferredScrollableViewportSize(new Dimension(320, 240));
this.add(new JTextField());
this.add(new JScrollPane(table));
}
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("ScrollDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create and set up the content pane.
frame.add(new ScrollDemo());
frame.pack();
// Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
Just in case anyone cares, the problem was that the eclipse launcher and the JNLP launcher for the app had a different KeyboardFocusManager, and the JNLP one had some quirks.
I was unaware of a custom KeyboardFocusManager being added on the JLNP side, but at least it explains things.
Thanks everyone.
I have had auto scrolling issue on JTables when I place them on an intermediary JPanel instead of placing them directly as the JScrollPane's viewport. In those cases, removing the extraneous panel and putting the table directly on the scrollpane has solved my issues.
Well, I ended up having to rework how I was adding my objects to the scroll pane to avoid this issue. Specifically, if the table is going to be taller than the viewport height, then I just have to add the table directly to the scrollpane then change how I was using the intermediary panel.
Thanks to everyone for your input!
According to this, to change the selection for your JTable you need to change the selection model. Try using the following to set the selected item to the very first one.
ListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.setSelectionInterval(start, end);
See if that gives the focus to the top of the JTable?
EDIT: I have not done this before, so not sure what start and end will need to be, try 0,0 or 0,1 maybe?

Java (Swing): JScrollPane.setBounds() does not apply?

I'm trying to create a simple JList with a scrollbar, and therefore i need to have the JList within a JScrollPane. So far, so good. However, for some reason i can't resize/position the JScrollPane!? It sounds logic that everything inside it should stretch to 100%, so if i set the JScrollPane to be 300px wide, the elements inside will be as well. Is that correct?
While you're at it, please critisize and give me hints if i should change something or optimize it.
Anyhow, here's the code:
package train;
import java.awt.*;
import javax.swing.*;
public class GUI {
private DefaultListModel loggerContent = new DefaultListModel();
private JList logger = new JList(loggerContent);
GUI() {
JFrame mainFrame = new JFrame("title");
this.addToLog("testing testing");
this.addToLog("another test");
// Create all elements
logger = new JList(loggerContent);
JScrollPane logWrapper = new JScrollPane(logger);
logWrapper.setBounds(10, 10, 20, 50);
// Add all elements
mainFrame.add(logWrapper);
// Show everything
mainFrame.setSize(new Dimension(600, 500));
mainFrame.setVisible(true);
}
public void addToLog(String inputString) {
int size = logger.getModel().getSize();
loggerContent.add(size, inputString);
}
}
Thanks in advance,
qwerty
EDIT: Here's a screenshot of it running: http://i.stack.imgur.com/sLGgQ.png
The setVisibleRowCount() method of JList is particularly convenient for this, as suggested in the relevant tutorial. ListDemo is a good example.
Addendum:
please critisize and give me hints…
Well, since you ask: Don't invoke public methods in the constructor; make them private or invoke them after the constructor finishes. There's no need to find the last index for add(), when addElement() is available. Also, be sure to construct your GUI on the event dispatch thread .
import java.awt.*;
import javax.swing.*;
/** #see http://stackoverflow.com/questions/5422160 */
public class ListPanel extends JPanel {
private DefaultListModel model = new DefaultListModel();
private JList list = new JList(model);
ListPanel() {
list.setVisibleRowCount(5);
}
public void append(String inputString) {
model.addElement(inputString);
}
private void init() {
for (int i = 0; i < 10; i++) {
this.append("String " + String.valueOf(i));
}
JFrame mainFrame = new JFrame("GUI");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane jsp = new JScrollPane(list);
mainFrame.add(jsp);
mainFrame.pack();
mainFrame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ListPanel().init();
}
});
}
}
The bounds & size of a component are generally ignored over that of it's preferred size and the constraints of the layout being used by the container.
To solve this problem, learn how to use layouts & apply them appropriately.
Try to put your JScrollPane inside a JPanel and add the panel to the frame.
JPanel panel = new JPanel();
panel.add (logWrapper);
mainFrame.add(panel);
Then set the bounds of the panel instead of the JScrollpane
panel.setBounds(10, 10, 20, 50);
The probles is that Swing uses layout managers to control child bounds property. Adding a JScrollpane directly to the main frame, doesn't allow you to choose right bounds properly.

Categories

Resources