1

I am trying to send sendText to my messagePane as soon as I press enter. But, I keep getting a NPE every time and anyway that I try.

public class ChatBox extends JPanel {

private JScrollPane scrollPane;
private String sendText;

public ChatBox() {
    final JTextArea chatPane = new JTextArea();

    scrollPane = new JScrollPane(chatPane,
            JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    add(scrollPane);
    scrollPane.setMinimumSize(new Dimension(550, 50));
    scrollPane.setPreferredSize(new Dimension(550, 50));

    chatPane.addKeyListener(new KeyListener() {

        @Override
        public void keyPressed(KeyEvent e) {
        }

        @Override
        public void keyReleased(KeyEvent e) {
            if( e.getKeyCode() == KeyEvent.VK_ENTER ) {
                sendText = chatPane.getText();
                chatPane.setText(null);
                System.out.println(sendText); // I can see this in console
            }

        }

        @Override
        public void keyTyped(KeyEvent e) {
        }

    });

}


public String getSendText() {
    return sendText;
}


public void setSendText(String sendText) {
    this.sendText = sendText;
}}

That is where I am setting "sendText" to the text that was entered on the chatPane before I cleared it with null. The sysout on the console for sendText shows whatever I type, which means code until then is good I hope.

Then when I try to grab it in the MessageWindow class which is:

public class MessageWindow extends JPanel {

private ChatBox box;

public MessageWindow() {
    JTextArea messagePane = new JTextArea();

    setLayout(new GridBagLayout());

    GridBagConstraints gc = new GridBagConstraints();

    gc.weightx = 1;
    gc.weighty = 1;
    gc.fill = GridBagConstraints.BOTH;
    gc.insets = new Insets(5, 5, 5, 5);
    add(new JScrollPane(messagePane), gc);


    messagePane.append(box.getSendText());   // Here is where I am getting the NPE.

}}

NEW CODE:

public class MessageWindow extends JPanel {

ChatBox box = new ChatBox();

public MessageWindow() {
    JTextArea messagePane = new JTextArea();

    setLayout(new GridBagLayout());

    GridBagConstraints gc = new GridBagConstraints();

    gc.weightx = 1;
    gc.weighty = 1;
    gc.fill = GridBagConstraints.BOTH;
    gc.insets = new Insets(5, 5, 5, 5);
    add(new JScrollPane(messagePane), gc);

    System.out.println(box.getText());  // Getting null in the console.
    messagePane.append(sendText);   // Not getting anything on messagePane.

}

}

Bowhuntr11
  • 47
  • 6
  • So check what is null: messagePane? box? the result of getSendText()? – John3136 Aug 26 '13 at 00:23
  • 2
    Avoid KeyListeners, check out [How to use Action Listeners](http://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html) for a bette approach – MadProgrammer Aug 26 '13 at 00:26
  • 1
    Whenever you get an exception, try to find and read its javadoc for an explanation of possible causes of that exception. Here's the one of NPE: http://docs.oracle.com/javase/7/docs/api/java/lang/NullPointerException.html In your particular case, the cause is listed as 1st item. – BalusC Aug 26 '13 at 00:28
  • MadProgrammer, I was trying to do that, but I could not for the life of me find out how to do an ActionListener in a JTextArea. Only in JTextField. I tried to mess around with FocusListener on the JTextArea before I gave up and just went with the KeyListener. Is there something I was missing with the ActionListener to get it to work with a JTextArea? – Bowhuntr11 Aug 26 '13 at 00:40
  • 1
    @AndrewThompson Given the OPs listening for the enter key, I jumped at ActionListenr, but the question is still low on context so a DocumentListner would certainly still be on the cards as a reasonable suggestion – MadProgrammer Aug 26 '13 at 00:45
  • I don't think either of these classes should extend panel. Instead just hold an instance of a panel (as a class attribute) and populate it as required, with a `getChatBox()` (e.g.) method. That means the panels themselves become largely irrelevant to the main design, it is mainly the components they contain (and there user edited values) that interest us in the bigger picture, and all those components could be declared right alongside the main panel and queried be a `getChatMessage()` (e.g.) method in the class that creates it. In fact, there seems little reason to have classes for either. – Andrew Thompson Aug 26 '13 at 00:46
  • @MadProgrammer *"Given the OPs listening for the enter key"* OK, but.. while enter might be an action listener invoker in a **`JTextField`**, it does not do that in a **`JTextArea`** mostly because, ..well text area does not **accept** an `ActionListener`! – Andrew Thompson Aug 26 '13 at 00:51
  • @AndrewThompson Agreed, just saw KeyListener and went into auto pilot – MadProgrammer Aug 26 '13 at 01:48

2 Answers2

4

The cause of your NPE is obvious:

private ChatBox box;

this is equivalent to:

private ChatBox box = null;

Then here you use box:

messagePane.append(box.getSendText());   // Here is where I am getting the NPE.

But you never give the box variable any viable object reference, and so when you try to use it it will of course be null.

But more importantly, how are you trying to connect these two classes because I see no code that does anything of this sort.

The solution is to first think how you want objects to be connected, to communicate with each other, and then write the code so that this will happen. Perhaps you want to create a new ChatBox object in your MessageWindow class:

private ChatBox box = new ChatBox();

but if one already exists and is already part of a GUI, then you don't in fact want to do this. If one already exists, what you'll instead want to do is to give MessageWindow a setChat(ChatBox chat)... method that sets the reference, either that or pass in the ChatBox reference via the MessageWindow class's constructor:

public MessageWindow(ChatBox chat) {
   this.chat = chat;
   // ... plus other constructor-specific code
}

Also, as has been mentioned in comments, you should not be using a KeyListener here, and as a general rule (that is occasionally broken) should avoid using these constructs in most of your Swing programs.

Rather than a JTextArea that uses a KeyListener, I'd suggest that you use a JTextField that uses an ActionListener to accept input text. For example, please have a look at my code in my answer to this question.


Edit 2
Modification of code from my previous answer to show two chat windows communicating with each other:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.util.List;
import java.util.Scanner;

import javax.swing.*;
import javax.swing.text.JTextComponent;

@SuppressWarnings("serial")
public class TerminalForm extends JPanel {
   private static final int GAP = 3;
   private JTextArea textarea;
   private JTextField textfield;
   private String userName;

   public TerminalForm(String userName, int rows, int cols, InputStream inStream,
         PrintStream printStream) {
      this.userName = userName;
      textarea = prepareTextArea(rows, cols, inStream);
      textfield = prepareTextField(cols, printStream, textarea);

      setLayout(new BorderLayout(GAP, GAP));
      setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
      add(new JScrollPane(textarea), BorderLayout.CENTER);
      add(textfield, BorderLayout.SOUTH);
   }

   public String getUserName() {
      return userName;
   }

   private JTextField prepareTextField(int cols, PrintStream printStream,
         JTextArea textArea) {
      JTextField textField = new JTextField(cols);
      textField.addActionListener(new TextFieldListener(printStream, textArea));
      return textField;
   }

   private JTextArea prepareTextArea(int rows, int cols, InputStream inStream) {
      JTextArea textArea = new JTextArea(rows, cols);
      textArea.setEditable(false);
      textArea.setFocusable(false);
      InputStreamWorker instreamWorker = new InputStreamWorker(textArea,
            inStream);
      instreamWorker.execute();
      return textArea;
   }

   private class TextFieldListener implements ActionListener {
      private PrintStream printStream;
      private JTextArea textArea;

      public TextFieldListener(PrintStream printStream, JTextArea textArea) {
         this.printStream = printStream;
         this.textArea = textArea;
      }

      @Override
      public void actionPerformed(ActionEvent evt) {
         JTextComponent textComponent = (JTextComponent) evt.getSource();
         String text = textComponent.getText();
         textComponent.setText("");

         printStream.println(userName + "> " + text);
         textArea.append(userName + "> " + text + "\n");
      }
   }

   private class InputStreamWorker extends SwingWorker<Void, String> {
      private Scanner scanner;
      private JTextArea textArea;

      private InputStreamWorker(JTextArea textArea, InputStream inStream) {
         this.textArea = textArea;
         scanner = new Scanner(inStream);
      }

      @Override
      protected Void doInBackground() throws Exception {
         while (scanner.hasNextLine()) {
            publish(scanner.nextLine());
         }
         return null;
      }

      @Override
      protected void process(List<String> chunks) {
         for (String chunk : chunks) {
            textArea.append(chunk + "\n");
         }
      }
   }

   private static void createAndShowGui(String userName, final InputStream inStream,
         final PrintStream printStream) {
      int rows = 20;
      int cols = 40;
      TerminalForm mainPanel = new TerminalForm(userName, rows, cols, inStream,
            printStream);

      JFrame frame = new JFrame("Terminal Form: " + userName);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      final PipedOutputStream outStream1 = new PipedOutputStream();
      final PipedInputStream inStream1 = new PipedInputStream();

      try {
         final PipedOutputStream outStream2 = new PipedOutputStream(inStream1);
         final PipedInputStream inStream2 = new PipedInputStream(outStream1);

         createAndShowGui("John", inStream1, new PrintStream(outStream1));
         createAndShowGui("Fred", inStream2, new PrintStream(outStream2));
      } catch (IOException e) {
         e.printStackTrace();
      }

   }
}

Run the code and type into both chat JTextFields to see what I mean.


Edit 3
Key Bindings version

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.util.List;
import java.util.Scanner;

import javax.swing.*;

@SuppressWarnings("serial")
public class TerminalFormWithTextArea extends JPanel {
   private static final int GAP = 3;
   private static final int ENTER_TEXT_AREA_ROWS = 3;
   private JTextArea textarea;
   private JTextArea enterTextArea;
   private String userName;

   public TerminalFormWithTextArea(String userName, int rows, int cols, InputStream inStream,
         PrintStream printStream) {
      this.userName = userName;
      textarea = prepareTextArea(rows, cols, inStream);
      enterTextArea = prepareEnterTextArea(cols, printStream, textarea);

      setLayout(new BorderLayout(GAP, GAP));
      setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
      add(new JScrollPane(textarea), BorderLayout.CENTER);
      add(new JScrollPane(enterTextArea), BorderLayout.SOUTH);
   }

   public String getUserName() {
      return userName;
   }

   private JTextArea prepareEnterTextArea(int cols, PrintStream printStream,
         JTextArea textArea) {
      JTextArea enterTxtArea = new JTextArea(ENTER_TEXT_AREA_ROWS, cols);
      enterTxtArea.setWrapStyleWord(true);
      enterTxtArea.setLineWrap(true);
      // textField.addActionListener(new TextFieldListener(printStream, textArea));
      int condition = JComponent.WHEN_FOCUSED;
      InputMap inputMap = enterTxtArea.getInputMap(condition);
      ActionMap actionMap = enterTxtArea.getActionMap();

      KeyStroke enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
      String enter = "enter";
      inputMap.put(enterKeyStroke, enter);
      actionMap.put(enter, new EnterAction(printStream, enterTxtArea, textArea));
      return enterTxtArea;
   }

   private JTextArea prepareTextArea(int rows, int cols, InputStream inStream) {
      JTextArea textArea = new JTextArea(rows, cols);
      textArea.setEditable(false);
      textArea.setFocusable(false);
      textArea.setWrapStyleWord(true);
      textArea.setLineWrap(true);
      InputStreamWorker instreamWorker = new InputStreamWorker(textArea,
            inStream);
      instreamWorker.execute();
      return textArea;
   }

   private class EnterAction extends AbstractAction {
      private PrintStream printStream;
      private JTextArea enterTxtArea;
      private JTextArea textArea;

      public EnterAction(PrintStream printStream, JTextArea enterTextArea,
            JTextArea textArea) {
         this.printStream = printStream;
         this.enterTxtArea = enterTextArea;
         this.textArea = textArea;
      }

      @Override
      public void actionPerformed(ActionEvent evt) {
         String text = userName + "> " + enterTxtArea.getText();
         enterTxtArea.setText("");
         printStream.println(text);
         textArea.append(text + "\n");
      }
   }

   private class InputStreamWorker extends SwingWorker<Void, String> {
      private Scanner scanner;
      private JTextArea textArea;

      private InputStreamWorker(JTextArea textArea, InputStream inStream) {
         this.textArea = textArea;
         scanner = new Scanner(inStream);
      }

      @Override
      protected Void doInBackground() throws Exception {
         while (scanner.hasNextLine()) {
            publish(scanner.nextLine());
         }
         return null;
      }

      @Override
      protected void process(List<String> chunks) {
         for (String chunk : chunks) {
            textArea.append(chunk + "\n");
         }
      }
   }

   private static void createAndShowGui(String userName, final InputStream inStream,
         final PrintStream printStream) {
      int rows = 20;
      int cols = 40;
      TerminalFormWithTextArea mainPanel = new TerminalFormWithTextArea(userName, rows, cols, inStream,
            printStream);

      JFrame frame = new JFrame("Terminal Form: " + userName);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      final PipedOutputStream outStream1 = new PipedOutputStream();
      final PipedInputStream inStream1 = new PipedInputStream();

      try {
         final PipedOutputStream outStream2 = new PipedOutputStream(inStream1);
         final PipedInputStream inStream2 = new PipedInputStream(outStream1);

         createAndShowGui("John", inStream1, new PrintStream(outStream1));
         createAndShowGui("Fred", inStream2, new PrintStream(outStream2));
      } catch (IOException e) {
         e.printStackTrace();
      }

   }
}
Community
  • 1
  • 1
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thank you, I knew I was doing something dumb. Now I just have to figure out why it still isn't displaying the box.getSendText on the messagePane. Doesn't give me an error, just nothing. I am sure it has to do something with the code running line by line, and it not doing it in the order I want to in, though. – Bowhuntr11 Aug 26 '13 at 00:35
  • On your Edit: I was trying to not to use a KeyListerner, as I did read that it wasn't great to use. But, I could not for the life of me find out how to do an ActionListener in a JTextArea (Guess you can't). So, I tried to mess around with FocusListener on the JTextArea before I gave up and just went with the KeyListener. JTextField, just doens't look right in the program that I am trying to create, is why I am kinda sticking to JTextArea. – Bowhuntr11 Aug 26 '13 at 00:44
  • @Bowhuntr11: you're trying to create a chat application, right? So why not have the text entering component be a JTextField at the bottom, and the record of all entered and received text be above it in a JTextArea? I'll show you some simple code to display what I mean... – Hovercraft Full Of Eels Aug 26 '13 at 00:50
  • Yes it is going to be a chatroom application. Your JTextField does look good in that program. I was just hoping to make it bigger where you could see alot more of what you type, but I was researching and you can't make the JTextField bigger, and keep the cursor in the top left...So you only see one line at a time, when my future users could be writing a long winded story, that could get annoying. Correct? Is there no way to get both worlds? :p I probably will end up changing to the JTextField as I will listen to the guys that know more then me. – Bowhuntr11 Aug 26 '13 at 01:12
  • I still want to figure out why my "messagePane.append(box.getSendText());" wont work on my original program though, as that has really stumped me. :/ I am still trying to fully comprehend where I am going wrong, even after trying to follow your advice about it above. – Bowhuntr11 Aug 26 '13 at 01:13
  • @Bowhuntr11: it's all about object references. Your ChatBox variable, box, did not reference any ChatBox object to begin with, and now probably does reference a ChatBox object, but not the one that is visible in any GUI. – Hovercraft Full Of Eels Aug 26 '13 at 01:15
  • @Bowhuntr11: you can also use Key Bindings on a JTextArea to capture the enter key being entered. – Hovercraft Full Of Eels Aug 26 '13 at 01:16
  • I edited my first post with the new code that I tried. The "box" should now reference a ChatBox object. I am trying to use the getText to get the sendText from the ChatBox class. I keep trying to go down the problem one step at a time, and it works in my head. I understand the reason I am getting null in the console the first time, is because it is null the first time the code is run. But after that, I should be able to enter a word and it come out, but it isn't. Sorry for not understanding, I'm sure its frustrating for you. I've just been stuck on this all day and my head hurts. – Bowhuntr11 Aug 26 '13 at 01:44
  • **"Key Bindings version"** Silver service version. :) – Andrew Thompson Aug 26 '13 at 02:02
  • @Bowhuntr11: again, in your "new code" while box refers to a ChatBox object it is very likely not the same ChatBox object that is displayed in the GUI. It's not enough that box has a viable reference, but it must be the *right* reference. That's why I've been recommending a setter method for this. – Hovercraft Full Of Eels Aug 26 '13 at 17:14
0

Remember to new every instance first before you use the methods inside.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Jeff Bootsholz
  • 2,971
  • 15
  • 70
  • 141