JList with ScrollPane auto-scroll bug - java

I have a JList with a ScrollPane. When i press a button, a new line is added to the JList.But the problem is that it doesn't scroll to the last element, but the second last element. Here is a portion of code used : There are no erros. Everything works good except this .
DefaultListModel<String> model = new DefaultListModel<String>();
JList<String> list = new JList<String>(model);
JPanel panel = new JPanel(new GridLayout(0,1));
panel.add(list);
scroll = new JScrollPane(panel);
JPanel p = new JPanel(new BorderLayout());
p.add(scroll, BorderLayout.CENTER);
p.add(buttonPanel, BorderLayout.SOUTH); // here are more button's including the one with the action described below
public void actionPerformed(ActionEvent e) {
list.setFont(new Font("Times new Roman", Font.PLAIN, 30));
model.add(list.getModel().getSize(), "Some text randomly"+ i++);
panel.repaint();panel.revalidate();
scroll.getVerticalScrollBar().setValue(scroll.getVerticalScrollBar().getMaximum());
}

I think that the actual repainting happens after this method has ended. Try calling the last line using SwingUtilities.invokeLater() like this:
SwingUtilities.invokeLater(new Runnable() { public void run() {
scroll.getVerticalScrollBar().setValue(scroll.getVerticalScrollBar().getMaximum(‌));
}
});
Beware while copy-pasting this into your IDE: There is a hidden character (zero-width non-joiner) in the HTML that is not allowed in your Java code. Just remove everything between the braces after getMaximum().
(In Java 8 you can use a Lambda expression to make this statement smaller and better readable.)

Related

JFrame will not load new screen

I have a class that extends JFrame and works by adding in 2 panels with BoxLayout buttons, and one JTabbedPane in the center which displays graphs.
I want one of the buttons to remove all current components in the frame and add new ones.
Here are the methods used.
private void createAndShowGraphs() {
ImageIcon createImageIcon(lsuLettersPath); //simple png file to fill one tab
final JTabbedPane jtp = new JTabbedPane();
JLabel iconLabel = new JLabel();
iconLabel.setOpaque(true);
jtp.addTab(null, icon, iconLabel);
//Here is where the errors begin
JPanel menu = new JPanel();
menu.setLayout(new BoxLayout(menu, BoxLayout.Y_AXIS));
//I want this button to remove all components currently in the JFrame and replace them with new components specified in the createAndShowIntro() method
menu.add(new JButton(new AbstractAction("Intro Pane") {
public void actionPerformed(ActionEvent e) {
//I've also tried putting removeAll in the Intro method
removeAll();
createAndShowIntro();
}
}));
add(jtp, BorderLayout.CENTER);
add(menu, BorderLayout.WEST);
pack();
setVisible(true);
}
private void createAndShowIntro() {
System.out.println("Made it to Intro");
//all I want is a blank JLabel with the String "test" to show up
JPanel test = new JPanel();
test.setLayout(new BorderLayout());
JLabel label = new JLabel();
label.setText("test");
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVerticalAlignment(SwingConstants.CENTER);
test.add(label);
add(test, BorderLayout.CENTER);
test.revalidate();
label.revalidate();
validate();
test.repaint();
label.repaint();
repaint();
pack();
setVisible(true);
}
When I call createAndShowGraphs() in main() and then hit the 'Intro' button, everything freezes and nothing is actually removed. I know it makes it the Intro method because of the "Made it to Intro" string output to the terminal.
I've tried all kinds of combinations of invalidate(), validate(), revalidate(), repaint() on the labels and on the frame itself. Really frustrated because I don't know how else I'm going to be able to display 3 different screens to switch back and forth between while only actually displaying one at a time.
Thanks for your time.

How can I refresh the JPanel?

I want to do a dynamic form in Swing. When I call firstly the dataShow method, it creates the GUI. But when I call it again, it keeps the old panel and display the new at the background.
And when I try to remove the current panel, and then add the new. The GUI became empty
A Thread listen to events (int id in that case).
here is my code to display the dynamic form :
public void showData(int id) throws DAOException, ClassNotFoundException{
FormDAOImpl form = new FormDAOImpl();
String b = form.importTagPoint(id);
//if(compteur%2 == 0) {System.out.println("Compteur : " +compteur); scrollPane.remove(panel);
//frame.getContentPane().remove(scrollPane);
//}
panel = new JPanel(new MigLayout());
if(b == null) b = "";
String[] bits = b.split("\\,");
String delims = "[=]";
while(i<bits.length){
textField = new JTextField();
String[] bitsS = bits[i].split(delims);
textField.setText(bitsS[1]);
JLabel label = new JLabel(bitsS[0]+ " : ");
panel.add(label);
panel.add(textField, "span, grow, alignx center, flowx");
i++;
}
JButton annuler = new JButton("Annuler");
JButton enregistrer = new JButton("Enregistrer");
panel.add(annuler);
panel.add(enregistrer);
panel.revalidate();
panel.repaint();
scrollPane = new JScrollPane(panel);
scrollPane.revalidate();
scrollPane.repaint();
frame.getContentPane().add(scrollPane);
//frame.repaint();
frame.invalidate();
frame.validate();
frame.repaint();
frame.pack();
frame.setMaximumSize(new Dimension(300, 800));
compteur++;
}
First, try calling frame.getContentPane().removeAll() to remove anything that was previously added to it. Obviously do this before you add anything new to it.
Second, try and devise a solution where you don't need to do this, but maintain a single view which can be updated via setters and getters if possible.
If you are literally changing views (show the user something complete different), consider using a CardLayout or JTabbedPane instead

How to redraw JTextPane when one line is changed to a larger font

I have a JTextPane whose model is a DefaultStyledDocument. I'm noticing that if the text is displayed, and I then use setCharacterAttributes to change every character on a line to a much larger font, the font of the characters on that line is changed in the display as I expect, but the lines below it don't move, so that the lines overlap in the display.
Is there a way to force the JTextPane to recompute the text locations and redisplay itself (or a portion of itself)? I tried setting up a DocumentListener with changedUpdate, and changedUpdate is indeed called, but I can't find a way to make it redraw the JTextPane. revalidate() didn't work.
EDIT: The lines do shift by themselves with a smaller test case, so apparently something else I'm doing in the program is interfering, but I haven't figured out what. Anyway, repaint() without revalidate() works if I can't determine what feature is causing a problem and how to get around it.
EDIT 2: The problem occurs when the JTextPane is added to a JPanel and the JPanel is set up with BoxLayout and BoxLayout.X_AXIS. A sample:
public class Demo extends JFrame {
JPanel panel;
JTextPane textPane;
DefaultStyledDocument doc;
SimpleAttributeSet smallText, bigText;
public Demo() {
super("Demo");
doc = new DefaultStyledDocument ();
textPane = new JTextPane(doc);
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
// problem goes away if above line is removed
panel.add(textPane);
panel.setPreferredSize(new Dimension(1000, 500));
textPane.setCaretPosition(0);
textPane.setMargin(new Insets(5,5,5,5));
getContentPane().add(panel, BorderLayout.CENTER);
smallText = new SimpleAttributeSet();
StyleConstants.setFontFamily(smallText, "SansSerif");
StyleConstants.setFontSize(smallText, 16);
bigText = new SimpleAttributeSet();
StyleConstants.setFontFamily(bigText, "Times New Roman");
StyleConstants.setFontSize(bigText, 32);
initDocument();
textPane.setCaretPosition(0);
}
protected void initDocument() {
String initString[] =
{ "This is the first line.",
"This is the second line.",
"This is the third line." };
for (int i = 0; i < initString.length; i ++) {
try {
doc.insertString(doc.getLength(), initString[i] + "\n",
smallText);
} catch (BadLocationException e) {
}
}
}
private void createAndShowGUI() throws InterruptedException {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) throws Exception {
new Demo().runMain();
}
private void runMain() throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
UIManager.put("swing.boldMetal", Boolean.FALSE);
try {
createAndShowGUI();
} catch (InterruptedException e) {
}
}
});
Thread.sleep(2000);
doc.setCharacterAttributes(24, 24, bigText, false);
}
}
Call repaint() it will redraw the container. DocumentListener reflects changes to a text document, so in your case is inappropriate. You can use DefaultStyledDocument.getStyle().addChangeListener() to handle changes of the attributes.
I've just found that it also works to add a setMinimumSize call to the JTextPane:
.......
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
// problem goes away if above line is removed
panel.add(textPane);
textPane.setMinimumSize(new Dimension(1000, 500)); // NEW
// These also work:
// textPane.setMinimumSize(new Dimension(1, 1)); or
// textPane.setMinimumSize(new Dimension(0, 0));
panel.setPreferredSize(new Dimension(1000, 500));
textPane.setCaretPosition(0);
.......
This is slightly preferable to the solution of wrapping the JTextPane inside a JScrollPane, because the latter displays some extra lines near the border even when no scrollbars are displayed.

Changing only one Panel in JFrame

I have one JFrame with two Buttons in East and West,Label in North,Label and TextField in South. and one Image in the Center. I want to call another Image after 30 seconds in the center. But every time I call the 2nd image the components in North,South,East and West are disappearing.
This is my code.
//The Components in JFrame.
firstPicblur = new JPanel(new BorderLayout());
pictureblur01 = new ImageIcon("C:\\java pics\\papsi2.jpg");
pictureblurA = new JLabel(pictureblur01);
firstPicblur.add(pictureblurA,BorderLayout.CENTER);
//FirstPicture blurred B
firstPicblurB = new JPanel(new BorderLayout());
pictureblur02 = new ImageIcon("C:\\java pics\\papsi1.jpg");
pictureblurB = new JLabel(pictureblur02);
firstPicblurB.add(pictureblurB,BorderLayout.CENTER);
//Next0Prev buttons
Next0Prev = new JPanel(new BorderLayout());
Next = new JButton("NEXT");
Prev = new JButton("PREV");
//Next0Prev labels is constant at SOUTH
firstPicblurA = new JPanel(new BorderLayout());
clueNo1 = new JLabel("Picture Number 01");
TypeHere = new JTextField("Guess Who");
firstPicblurA.add(TypeHere, BorderLayout.CENTER);
firstPicblurA.add(clueNo1, BorderLayout.SOUTH);
Next0Prev.add(firstPicblur,BorderLayout.CENTER);
Next0Prev.add(Next,BorderLayout.EAST);
Next0Prev.add(Prev,BorderLayout.WEST);
Next0Prev.add(firstPicblurA,BorderLayout.SOUTH);
//other components
and this is the actionEvent
if(e.getSource() == continueButton){
add(Next0Prev);
Next0Prev.setVisible(true);
Next0Prev.repaint();
Next0Prev.revalidate();
loadingEffectBtn.setVisible(false);
Timer ta = new Timer(10000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
add(firstPicblurB);
firstPicblurB.setVisible(true);
Next0Prev.setVisible(true);
}
});
ta.start();
ta.setRepeats(true);
}
but when i do this. The 2nd picture is appearing in center but the N,E,W,S components are disappearing.
This is a bit hard to tell, but what appears to be happening is...
You're creating Next0Prev and adding all your components to it...
Next0Prev = new JPanel(new BorderLayout());
//....
Next0Prev.add(firstPicblur,BorderLayout.CENTER);
Next0Prev.add(Next,BorderLayout.EAST);
Next0Prev.add(Prev,BorderLayout.WEST);
Next0Prev.add(firstPicblurA,BorderLayout.SOUTH);
Which I assume you are adding to the frame...
Then in your Timer's actionPerformed, you're adding firstPicblurB to the main container...
public void actionPerformed(ActionEvent e) {
add(firstPicblurB);
firstPicblurB.setVisible(true);
Next0Prev.setVisible(true);
}
Now, assuming that the main container is either a JFrame or is using a BorderLayout, this will effectively hide Next0Prev as BorderLayout can only have a single component in any of it's 5 positions.
Instead, you should be adding firstPicblurB to Next0Prev...
A simpler solution would be (as has already being pointed out) to use a single JLabel and simple change it's icon
I would also highly recommend that you take the time to read through and apply Code Conventions for the Java Programming Language

Trying to build a GUI that would enable users to add more TextFields to it

I need to let users add more text fields to my JFrame so once the size of the containing frame being JPanel has exceeded its original value a scroll pane would step in.
In order to be able to do this, I came up with an idea to put one JButton and upon hitting it a new TextField would show up (this was my original idea which doesn't necessarily mean I am right). The problem is, once I call the ActionListener class to add more TextFields and eventually stretch its containing panel, the program asks me to make the JPanel final which in turns doesn't allow for stretching of the panel. In other words, it appears to me that I'm just beating around the bush, please help me out put this together, below is a piece of my code:
public class Button {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setLayout(new BorderLayout());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel p = new JPanel(new GridLayout(0, 5));
JScrollPane jsp = new JScrollPane(p);
jsp.setPreferredSize(new Dimension(300,300));
jsp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
jsp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
JButton but = new JButton("Add");
f.add(but);
but.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int height=0;
JTextField jtx = new JTextField();
jtx.setSize(new Dimension(70,20));
jtx.setPreferredSize(new Dimension(70,20));
p.add(jtf);
height+=20;
p.setSize(new Dimension(300, height));
p.setPreferredSize(new Dimension(300, height));
}
});
f.add(jsp, BorderLayout.CENTER);
f.setLocation(300, 300);
f.setVisible(true);
f.pack();
}
}
I will give you an idea : you can add button and inside the button instruction you would add an instruction set that create text field . if you want more than once then you must handle position pointer that tell you the last place the user add text field then by updating the pointer the user can see the text in different place , if you want the user to control the position of the text then he must enter the location .

Categories