Search code examples
javaswingcommand-promptpaintcomponentnotepad

Java - JPanel - painting contents of a text file with an arraylist - Only works the first time


I have a program with a JMenuBar. Inside the JMenuBar has a JButton, the JButton opens up a JMenu that has 2 JMenuItems. When you click on one of the items, it opens a second JFrame, and prints the contents of a text file line by line.

If you select the other option (in the same session), it should change the JFrame title (this works), and print the contents of the other text file (this doesn't work).

I've pinpointed what the issue is, but I have no clue on why this issue is occurring. What's occurring is the code works perfectly the first time it displays the contents of the text file. However, the second time it does not change the text.

What I've found during the second time is described in the comments. Start at the setDocument method, and then move to the paintComponent method.

Here is the class with the issue (there are several other classes for the program, but the issue is solely within this class)...

package PeriodicTable;

import javax.swing.JPanel;

import java.util.ArrayList;

import java.io.IOException;
import java.io.FileReader;
import java.io.BufferedReader;

import java.awt.Graphics;
import java.awt.Color;

import java.lang.Override;

class DocumentPanel extends JPanel {
private ArrayList<String> aryDocument;
DocumentPanel(){
    super();
    setBackground(Color.white);
}
@Override
protected void paintComponent(Graphics gr){
    super.paintComponent(gr);
    //aryDocument holds the contents of the old text file (should have the contents of the other text file)
    for(int index = 0; index < aryDocument.size(); index++){
        //enters loop, re-prints the old document
        gr.drawString(aryDocument.get(index), 5, (index + 1)*10);
    }
}
public void setDocument(String strFileDirectory){ //places contents of text file in an array (line by line)

    //aryDocument is null at this time
    aryDocument = new ArrayList<String>();
    //aryDocument is empty at this time
    try(BufferedReader reader = new BufferedReader(new FileReader(strFileDirectory))){
        for(String strLine; (strLine = reader.readLine()) != null; ){
            aryDocument.add(strLine);
        }
        reader.close();
    }catch(IOException ioe){
        ioe.printStackTrace();
    }
    //aryDocument holds the contents of the other text file
    this.revalidate();
}
}

The following method is in a class called Table (Table implements ActionListener). This method is called by actionPerformed, who uses the ActionCommand value to determine what action does what.

private void loadTextFile(String strName, String strFileDirectory){
    DocumentPanel clsDocumentPanel = new DocumentPanel();
    if(frmDocument == null){
        frmDocument = new JFrame(strName);
        frmDocument.setPreferredSize(new Dimension(600, 700));
        clsDocumentPanel.setDocument(strFileDirectory);
        frmDocument.add(clsDocumentPanel);
        frmDocument.pack();
        frmDocument.setVisible(true);
    }else{
        if(!(frmDocument.getTitle().equals(strName))){
            frmDocument.setTitle(strName);
            clsDocumentPanel.setDocument(strFileDirectory);
            frmDocument.pack();
            frmDocument.setVisible(true);
        }
    }
}

I've rechecked strFileDirectory and confirmed they are the correct values.

What versions/programs/etc am I using?

Java 8

Notepad

Command Prompt

My question in clearly stated...

Why isn't aryDocument's value changing when it goes into paintComponent (after loading the other text file)? How can I fix it?


Solution

  • Here,

    DocumentPanel clsDocumentPanel = new DocumentPanel(); // <- yes, here!
    if(frmDocument == null){
        frmDocument = new JFrame(strName);
        frmDocument.setPreferredSize(new Dimension(600, 700));
        clsDocumentPanel.setDocument(strFileDirectory);
        frmDocument.add(clsDocumentPanel);
        frmDocument.pack();
        frmDocument.setVisible(true);
    }else{
        if(!(frmDocument.getTitle().equals(strName))){
            frmDocument.setTitle(strName);
            clsDocumentPanel.setDocument(strFileDirectory);
            frmDocument.pack();
            frmDocument.setVisible(true);
        }
    }
    

    you create a new instance of DocumentPanel each time the action is performed. The first time you add it to frmDocument. The second time you just call setDocument on it and let it be garbage collected. The first instance (actually attached to the displayed frame) is never updated.

    So, either

    • store clsDocumentPanel somewhere separately, just like you store frmDocument, not as a local variable;
    • make frmDocument a JFrame subclass that exposes access to its document panel and call frmDocument.getDocumentPanel().setDocument(...)—but this violates Demeter's Law;
    • or make frmDocument a JFrame subclass that has a setDocument method that just delegates to setDocument of its panel, then just call frmDocument.setDocument(...).