I am trying to implement a JScrollPane with a JTextArea. The JTextArea is being appended to, and I want the JScrollPane to keep scrolling down as more text is added. How can this be achieved?
For (what I think is) a simpler answer check out: Text Area Scrolling.
Prior to JDK5, you would have to manually change the caret's position after each append. You can now give this behaviour as a default like this :
JTextArea textArea = new JTextArea();
DefaultCaret caret = (DefaultCaret)textArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
The advantage of this is that you don't need to use this snippet more than once in your code!
I found the answer here:
JScrollPane and JList auto scroll
scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
}
});
If you are constantly writing data to it you could use:
textArea.setCaretPosition(textArea.getDocument().getLength());
just after you add the new data.
This would automatically scroll all the way down the JScorllPane.
Here is the solution.
JTextArea textArea = new JTextArea();
DefaultCaret caret = (DefaultCaret)textArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);`
The accepted solution works good, but only when the text area is editable, i.e. without jTextArea.setEditable(false) . The solution suggested by Krigath is more general, but has the problem as asked here JScrollPane and JList auto scroll. Using answers from that question you can get general solution, e.g.:
JScrollPane scrollPane = new JScrollPane(jTextArea);
verticalScrollBarMaximumValue = scrollPane.getVerticalScrollBar().getMaximum();
scrollPane.getVerticalScrollBar().addAdjustmentListener(
e -> {
if ((verticalScrollBarMaximumValue - e.getAdjustable().getMaximum()) == 0)
return;
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
verticalScrollBarMaximumValue = scrollPane.getVerticalScrollBar().getMaximum();
});
The Pane then is scrolled down only when vertical scroll bar is expanding, in response to appended lines of text.
I admit that that a method to filter events without extra variables could be found, and would appreciate if somebody post it.
A work around is possible: you can declare that listener as a class then instantiate it on the event where it is needed. After which you can remove the class after forcing a repaint of the screen. Works like a charm.
I was look at the answers and found that #user9999 answer is a good solution for those who want the scrollbar continuously scroll. I edited the code, got rid of the variable. It make the scrolling stop - when the user is scrolling manually. If the user scrolls to the end of the textarea or scrollarea, the auto scrolling continues.
(also as #user9999 suggested i removed the variable and added the jScrollPane1.getHeight() as the measure value to stop scrolling if the current scrollbar value is lower than max)
Here is the workaround:
jScrollPane1.getVerticalScrollBar().addAdjustmentListener(
e -> {
if ((e.getAdjustable().getValue() - e.getAdjustable().getMaximum()) > -jScrollPane1.getHeight() - 20){
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
}
});
Edit:
Added -20 to the -jScrollPane1.getHeight() - 20 as it is sometimes doesnt scroll without it, i guess the -20 can be changed depends on the font size of the TextArea.
Be careful if you're about to use auto scroll within a multithreaded program.
Like for example if somewhere is a method like
public void addNewLine(String s){
textPane.setText(textPane.getText()+"\n"+s);
if(autoscrollCheckBox.isSelected()){
this.revalidate();
JScrollBar vertical = scrollPane.getVerticalScrollBar();
vertical.setValue(vertical.getMaximum());
}
}
You will get the following exception, if the addNewLine (or the vertical.setValue(...)) method is called from another thread. (Especially, if you're try to resize the window or try to scroll while, autoscroll is enabled)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.text.BoxView.calculateMajorAxisRequirements(BoxView.java:871)
at javax.swing.text.BoxView.checkRequests(BoxView.java:930)
at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:568)
...
The reason for this is, the multithreaded call of the method is messing up with Swings' eventhandling, so you'll get random results or like above an error (read morehere).
The correct way is to call SwingUtilities.invokeLater(...):
public void addNewLine(String s){
SwingUtilities.invokeLater( () -> {
textPaneOutput.setText(textPaneOutput.getText()+"\n"+s);
if(autoscrollCheckBox.isSelected()){
this.revalidate();
JScrollBar vertical = scrollPane.getVerticalScrollBar();
vertical.setValue(vertical.getMaximum());
}
});
}
This way you'll able to auto scroll threadsafe!
Related
I am new to GWT and have made 3 textarea objects and have added them to a vertical panel, which is also added to my rootpanel. However, I cannot seem to input any text in these textareas. Any suggestions?
VerticalPanel panel = new VerticalPanel();
TextArea tb = new TextArea();
TextArea tb1 = new TextArea();
TextArea tb2 = new TextArea();
panel.add(tb);
panel.add(tb1);
panel.add(tb2);
RootPanel.get().add(panel);
I would try enabling them:
tb.setEnabled(true)
tb1.setEnabled(true)
tb2.setEnabled(true)
But I don't think that should be necessary.
There might be something small you are missing, I would compare all of your code to this. It seems to be a good working example that you could compare your code to and see if you missed a small step.
It seems you may need to add the TextArea objects to horizontal panels and then add those horizontal panels to the vertical panel.
The problem you describe maybe caused by adding another widget on top of your TextArea widgets. In this case TextArea widget may remain visible, but it will be unusable.
I don't see it in the code snippet that you provided, but maybe it's not all of your code.
Try this. It is the example straight from the GWT Javadoc.
Maybe you need to use setCharacterWidth(int size) and setVisibleLines(int size) before adding it.
public class TextBoxExample implements EntryPoint {
public void onModuleLoad() {
//Make an 80 x 50 TextArea
TextArea ta = new TextArea();
ta.setCharacterWidth(80);
ta.setVisibleLines(50);
// Add them to the root panel.
VerticalPanel panel = new VerticalPanel();
panel.add(ta);
RootPanel.get().add(panel);
}
}
I'm having more "I'm hopeless at programming" problems.
I have a piece of code which uses StringBuilder to display elements of an array in a text panel of a GUI when the program starts. Here's the StringBuilder code:
// memory tab
StringBuilder mList = new StringBuilder();
memLocList = new Memory[MEM_LOCATIONS];
mem = new Memory();
for (int i = 0; i < memLocList.length; i++) {
memLocList[i] = mem;
memLocList[i].setOpCode(00);
mList.append(String.format("%10s %04x %10s %6s", "Address: ", i,
"Value: ", memLocList[i].getOpCode()));
mList.append("\n");
}
JComponent memTab = makeTextPanel(mList.toString());
tabs.addTab("Memory", new JScrollPane(memTab));
}
protected JComponent makeTextPanel(String t) {
text = t;
JPanel panel = new JPanel(false);
JTextPane filler = new JTextPane();
filler.setFont(new Font("Courier", Font.PLAIN, 14));
filler.setText(text);
filler.setAlignmentX(LEFT_ALIGNMENT);
panel.setLayout(new GridLayout(1, 1));
panel.add(filler);
return panel;
}
The GUI also has a text entry panel where a String of hex values can be entered.
On clicking a button, the user is prompted for another value, which corresponds to the position in the array where the first hex value should be inserted.
Once these values have been entered, I'd like the display to be updated / refreshed to reflect this but am unsure of how to go about it.
I found this question here, which is similar but I'm not sure if implementing Observer/Observable pattern is the right way to proceed, and even if it is, how I'd go about it:
Best Way to Constantly Update GUI Elements
My initial approach was to add an "updateDisplay()" method, which I could call after processing the button click and re-call the makeTextPanel method:
public void updateDisplay() {
makeTextPanel(text);
}
I thought this might refresh it but it has no effect of the display.
Any help appreciated.
You hold your array in a model class, and you allow other classes to "listen" to this by giving this class a SwingPropertyChangeSupport object as well as an addPropertyChangeListener(...) method. Then give the array a setXXX(...) method, and in that method fire the SwingPropertyChangeSupport object after updating the array. There are examples of just this sort of thing on this site, some written by me.
For example: here, here, here, ...
By the way, I'm not surprised that your call to makeTextPanel(text) doesn't work. It creates a JPanel, but you don't appear to do anything with the JPanel that is returned from the method. But nor should you. I don't think that creating new JPanels is the solution you want, but rather updating the Strings displayed by a component of some sort such as a JList or JTextArea using the listener framework that I've described above.
If any of this is confusing, please ask for clarification.
I have a problem with displaying the components of a JScrollPane. Let me first explain the context. I've got one big splitpane:
center = new JSplitPane(JSplitPane.VERTICAL_SPLIT, p, p1);
center.setDividerLocation(0.9);
center.setDividerSize(3);
center.setResizeWeight(1);
center.setContinuousLayout(true);
The p pane is shown the right way, no problem here. But the p1 pane won't be displayed, i can see the empty bottom-part of the splitPane, but that's all.
JPanel p = new JPanel();
p.add(canvas);
JPanel p1 = new JPanel();
p1.add(canvasPropPane);
The canvasPropPane is a scrollPane that i initialize like this:
VolumeSizeAndPosition volum = new VolumeSizeAndPosition();
canvasPropPane = new JScrollPane(volum);
volume was tested on an independent frame and have been shown the right way.
I tried showing on the canvasPropPane a simple button canvasPropPane.add(wildButton); and it has a strange behavior: it paints the button only after i hover the mouse over it's location; at repaint (after resizing the scrollpane) it disappears.
I've solved similar problems by calling invalidate() on all of the underlying nested Swing objects. So for your particular question p.invalidate() and p1.invalidate() may help. I believe this strange behavior to be a bug in Swing.
I intend to take the elements of an integer list and add it to a label and print them in downward fashion one after another. I wrote the following code for it.
public static JFrame ListDraw(JFrame frame , ArrayList<Integer> e)
{
for(int i= 0;i<e.size();i++)
{
JLabel j = new JLabel(e.get(i).toString(),JLabel.CENTER);
frame.add(j);
}
return frame;
}
But it just prints the last array element in the label. What am I doing wrong here?
---------------------(update)
This is just a query that I have regarding the same thing. Therefore I am going to ask it here only. Is there any way to print the label items in a stack as in vertical alignment. Right now I get all the values printed in the horizontal fashion.
I guess you need to set layout for your frame, f.ex: frame.setLayout(new FlowLayout());.
Your frame isn't adapting to the new group of elements- the LayoutManager isn't getting a chance to resize the window. At the end of your function, add frame.pack().
You should use setBounds() to define the size of your frame and give it a LayoutManager of your choice.
i used textarea1.setVisible(false); but still i can see the border of the text area at run time. i want the textarea to be completely invisible
Can anyone help in this issue?
It sounds like you have a Panel around your text area since setVisible(false) should definitely hide the entire component. If so, make the panel invisible. Care to post some code so we can examine and help?
You have to hide the scroll pane which your text area is sitting in. If for some reason you have no direct access to it here is the way to get it:
public static final JScrollPane getScrollPane( JComponent component ) {
Container p = component .getParent();
if (p instanceof JViewport) {
Container gp = p.getParent();
if (gp instanceof JScrollPane) {
return (JScrollPane)gp;
}
}
return null;
}
Find your textarea scrollpane, then set the visibility to false, like this:
jScrollPane4.setVisible(false);