Swing - scroll JFrame content without using scrollbars - java

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

Related

JTextArea not appearing on JScrollPane when building through methods

I am trying to make a class in which I can create a JFrame by calling different methods to build upon it. However somewhere along the lines my JTextArea is getting lost...
Below is a class called App which holds the methods I need to start building...
public class App {
private JFrame frame = new JFrame();
private JTextArea textArea = new JTextArea();
private JScrollPane scrollPane = new JScrollPane();
public void openJFrame(String title, int x, int y){
JFrame.setDefaultLookAndFeelDecorated(true);
frame.setTitle(title);
frame.setSize(x, y);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public JFrame getJFrame(){
return frame;
}
public void addJTextArea(JScrollPane scrollPane){
scrollPane.add(textArea);
textArea.setLineWrap(true);
textArea.setEditable(true);
textArea.setVisible(true);
}
public JTextArea getJTextArea(){
return textArea;
}
public void addJScrollPane(JFrame frame){
frame.add(scrollPane);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
}
public JScrollPane getJScrollPane(){
return scrollPane;
}
I would like to call this class from my main method and build a JFrame. Below is my attempt.
public class Main {
public static void main(String[] args){
App app = new App();
app.addJTextArea(app.getJScrollPane());
app.addJScrollPane(app.getJFrame());
app.openJFrame("title", 500, 500);
}
What happense is the JFrame and ScrollPane appear.. however my text area doesnt seem to be adding to the scroll pane.
Am I misunderstanding or overlooking something? It may be worth noting that if in the addJTextArea method I add it directly onto the JFrame without using the JScrollPane method it appears (obviously without the scroll pane)
Although JScrollPane may look/act/sound similar to a JPanel, it is not. And thus, using JScrollPane.add() to add a component to the scroll pane may sound natural, but is wrong. JScrollPane can only have one component inside that it scrolls around, and thus add() is wrong, but setViewportView() is the method to use.
You have to adapt your method addJTextArea to use scrollPane.setViewportView() instead of scrollPane.add():
public void addJTextArea(JScrollPane scrollPane){
scrollPane.setViewportView(textArea);
textArea.setLineWrap(true);
textArea.setEditable(true);
textArea.setVisible(true);
}
Replace scrollPane.add(textArea); with scrollPane.setViewportView(textArea);.
For more details read How to Use Scroll Panes

How do I resize JScrollPane after resizing a JTextArea

My JFrame Consists of three main parts a banner at top scrollpane containing a JTextArea center and a JTextField at the bottom. When I re-size the frame I adjust the columns and rows in my JTextArea. When making the frame larger the JTextArea expands visually but removes the scroll-bar. Then if I make the frame smaller the JTextArea stays the same size. This Is where I attempt to re-size my JTextArea.
frame.addComponentListener(new ComponentAdapter() {//Waits for window to be resized by user
public void componentResized(ComponentEvent e) {
uneditTextArea.setRows(((int)((frame.getHeight()-140)/18.8)));//sets Textarea size based on window size
uneditTextArea.setColumns(((int)((frame.getWidth()-100)/10.8)));
frame.revalidate();//refreshes screen
}
});
Why would the ScrollPane not re adjust to the change in size of the TextField.
The Rest of the code is below in case it is needed.
public class window extends JFrame
{
private static JFrame frame = new JFrame("Lillian");
private static JButton inputButton = new JButton("Send");
private static JTextField editTextArea = new JTextField(46);
private static JTextArea uneditTextArea = new JTextArea(26,50);
private static JPanel logoPanel = new JPanel();//Input text window
private static JPanel itextPanel = new JPanel();//Input text window
private static JPanel submitPanel = new JPanel();//Submit Button
private static JPanel bottom = new JPanel();//will contain scrollpane
private static JPanel middle = new JPanel();//willcontain itextpanel & submitbutton
private static JPanel otextPanel = new JPanel();//Text Output
public static void runWindow()
{
ImageIcon logo = new ImageIcon("Lillian_resize.png");//banner
ImageIcon icon = new ImageIcon("Lillian_icon.png");//application icon
frame.setIconImage(icon.getImage());
frame.setSize(660,640);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
logoPanel.setSize(10,10);
JLabel logoLabel = new JLabel(logo);
final JScrollPane scrollPane = new JScrollPane(otextPanel);//adds text to panel will scrollbar
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//scrollbar only apears when more text than screen
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);//scrollbar never apears
scrollPane.setBorder(BorderFactory.createEmptyBorder());
logoPanel.add(logoLabel);
submitPanel.add(inputButton);
itextPanel.add(editTextArea);
otextPanel.add(uneditTextArea);
frame.getContentPane().add(logoPanel,"North");
frame.getContentPane().add(middle);
frame.getContentPane().add(bottom,"South");
middle.add(scrollPane,"North");//adds panels to outer panel
bottom.add(itextPanel, "West");
bottom.add(submitPanel, "East");
uneditTextArea.setLineWrap(true);
uneditTextArea.setWrapStyleWord(true);
uneditTextArea.setEditable(false);
uneditTextArea.setCaretPosition(uneditTextArea.getDocument().getLength());
frame.revalidate();//refreshes screen
//---------------wait for action------------
frame.addComponentListener(new ComponentAdapter() {//Waits for window to be resized by user
public void componentResized(ComponentEvent e) {
uneditTextArea.setRows(((int)((frame.getHeight()-140)/18.8)));//sets Textarea size based on window size
uneditTextArea.setColumns(((int)((frame.getWidth()-100)/10.8)));
frame.revalidate();//refreshes screen
}
});
}
}
There should be no need to use a ComponentListener to resize components. That is the job of the layout managers that you use to dynamically resize the components.
You should not be adding the text area to a JPanel first. Instead when using text areas you would generally add the text area directly to the viewport of a JScrollPane by using code like:
JScrollPane scrollPane = new JScrollPane( textArea );
Then you add the scrollpane to the frame with code like:
frame.add(scrollPane, BorderLayout.CENTER);
As you have noticed you should also NOT use hardcoded literals like "Center". Instead use the variables provided by the layout manager. Since you are using a BorderLayout, use the variables defined in the BorderLayout class.
Also, you should NOT be using static variable to create your GUI. I suggest you read the section from the Swing tutorial on Layout Manager. The tutorial will give you more information and the example code will show you how to better structure your program so that you don't need to use static variables.

Adding jlabel to a jframe using components

I have 2 classes,
My main class creates a frame and I want another class to add content to it. A bit of reading arroudn told me I should use components to do this however when I run my code the frame is empty.
public static void main(String[] args)
{
// create frame
JFrame frame = new JFrame();
final int FRAME_WIDTH = 800;
final int FRAME_HEIGHT = 600;
// set frame attributes
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("My Frame");
frame.setVisible(true);
Component1 Com = new Component1();
Component add = frame.add(Com);
}
My Component class creates a JLabel
public class Component1 extends JComponent {
public void paintComponent()
{
JLabel label = new JLabel("<html>Some Text</html>");
}
}
I don't get any compile errors, however I dont get any text in my JFrame.
Can anyone explain what I'm doing wrong?
Chris
You need to add the JLabel. Also better to extend JPanel instead of JComponent as it has a default layout manager and will make any added components appear without the need to set component sizes. paintComponent is used for custom painting BTW.
public class Component1 extends JPanel {
Component1() {
JLabel label = new JLabel("<html>Some Text</html>");
add(label);
}
}
No need to create a new Component. Just call frame.getContentPane().add(label). And initialize your label before this.

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?

Creating an Outlook style user interface in 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.

Categories

Resources