Reputation: 427
I'm new to Java and I've been trying to build a simple instant messaging program. I would like the text box (the place where you type your messages before sending them) to dynamically change size (w/ a max height constraint) as a person enters in a large message, kinda like what happens on WhatsApp or iMessage.
I've tried to count the number of lines of text that are in the text box (taking into account the effects of text wrapping), and then increase/decrease the text box's height according to the number of text-wrapped lines present. I determined the height of 1 row of text using the getScrollableUnitIncrement() method.
Also, as I am learning, is there a better method of dynamically changing the size of the text box than the one I've outlined above?
I used a JTextArea embedded in a JScrollPane and I'm using a componentListener on the JTextArea to resize the textbox as appropriate.
Please check out the while loops in the component listener method as I don't think this part of program works properly...
Here's a snippet of the code:
public class clienterrors extends JFrame {
private JTextArea userText;
private JTextPane chatWindow;
private String userName="testName";
private Document doc;
// Automatic resizing of the text box
private static Dimension textBoxSize = new Dimension(20, 20);
public static int numRows = 1;
private static final int rowHeight = 20;
private final int maxHeight = 80;
public static void main(String[] args) {
clienterrors george = new clienterrors();
george.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public clienterrors(){
super("Client instant messaging platform");
// Chat window initialisation
chatWindow = new JTextPane();
chatWindow.setEditable(false);
doc = chatWindow.getDocument();
add(new JScrollPane(chatWindow), BorderLayout.CENTER);
// Text box initialisation
userText = new JTextArea();
userText.setLineWrap(true);
userText.setWrapStyleWord(true);
JScrollPane jsp = new JScrollPane(userText);
jsp.setPreferredSize(textBoxSize);
jsp.setMaximumSize(new Dimension(20, 40));
// Gets the text box to resize as appropriate
userText.addComponentListener(
new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
// Needs to cater for when removing & pasting large messages into the text box
while (countLines(userText) > numRows && textBoxSize.getHeight() < maxHeight) {
textBoxSize.setSize(20, (int) textBoxSize.getHeight() + rowHeight);
revalidate();
numRows++; // numRows is used as an update to see which
}
while (countLines(userText) < numRows && textBoxSize.getHeight() > 20){
textBoxSize.setSize(20, (int)textBoxSize.getHeight() - rowHeight);
revalidate();
numRows--;
}
}
}
);
// Allows u to send text from text box to chat window
userText.addKeyListener(
new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
if(e.getKeyChar() == '\n' && enterChecker(userText.getText())){
// returns the text (-1 on the substring to remove the \n escape character when pressing enter)
showMessage("\n" + userName + ": " + userText.getText().substring(0, userText.getText().length() - 1));
userText.setText("");
}
}
}
);
add(jsp, BorderLayout.SOUTH);
//JFrame properties
setSize(300, 400);
setVisible(true);
}
// shows message on the chat window
private void showMessage(final String text){
SwingUtilities.invokeLater(
new Runnable() {
@Override
public void run() {
try{
doc.insertString(doc.getLength(), text, null);
}catch(BadLocationException badLocationException){
badLocationException.printStackTrace();
}
// place caret at the end (with no selection), so the newest message can be automatically seen by the user
chatWindow.setCaretPosition(chatWindow.getDocument().getLength());
}
}
);
}
// Prevents the user from sending empty messages that only contain whitespace or \n
private static boolean enterChecker(String t){
for(int i=0; i<t.length(); i++)
if (t.charAt(i) != '\n' && t.charAt(i) != ' ')
return true;
return false;
}
// This counts the number of wrapped lines in the text box to compare to numRows - only used to resize the text box
// (got this off the internet)
private static int countLines(JTextArea textArea) {
if(!enterChecker(textArea.getText())) return 0; // this prevents getting an error when you're sending an empty message
AttributedString text = new AttributedString(textArea.getText());
FontRenderContext frc = textArea.getFontMetrics(textArea.getFont())
.getFontRenderContext();
AttributedCharacterIterator charIt = text.getIterator();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(charIt, frc);
float formatWidth = (float) textArea.getSize().width;
lineMeasurer.setPosition(charIt.getBeginIndex());
int noLines = 0;
while (lineMeasurer.getPosition() < charIt.getEndIndex()) {
lineMeasurer.nextLayout(formatWidth);
noLines++;
}
return noLines;
}
}
Upvotes: 2
Views: 4741
Reputation: 7360
A JTextArea automatically updates its preferred size so that all text can be displayed. If you don't wrap it in a ScrollPane, the BorderLayout will automatically display what you want without any additional logic:
public class ClientErrors extends JFrame {
private JTextArea userText;
private JTextPane chatWindow;
private String userName = "testName";
// Automatic resizing of the text box
public static int numRows = 1;
private static final int rowHeight = 20;
private final int maxHeight = 80;
private Document doc;
public static void main(String[] args) {
ClientErrors george = new ClientErrors();
george.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public ClientErrors() {
super("Client instant messaging platform");
// Chat window initialisation
chatWindow = new JTextPane();
chatWindow.setEditable(false);
doc = chatWindow.getDocument();
add(new JScrollPane(chatWindow), BorderLayout.CENTER);
// Text box initialisation
userText = new JTextArea();
userText.setLineWrap(true);
userText.setWrapStyleWord(true);
// Allows u to send text from text box to chat window
userText.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == '\n' && enterChecker(userText.getText())) {
// returns the text (-1 on the substring to remove the \n
// escape character when pressing enter)
showMessage("\n"
+ userName
+ ": "
+ userText.getText().substring(0,
userText.getText().length() - 1));
userText.setText("");
}
}
});
add(userText, BorderLayout.SOUTH);
// JFrame properties
setSize(300, 400);
setVisible(true);
}
// shows message on the chat window
private void showMessage(final String text) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
doc.insertString(doc.getLength(), text, null);
} catch (BadLocationException badLocationException) {
badLocationException.printStackTrace();
}
// place caret at the end (with no selection), so the newest
// message can be automatically seen by the user
chatWindow.setCaretPosition(chatWindow.getDocument()
.getLength());
}
});
}
// Prevents the user from sending empty messages that only contain
// whitespace or \n
private static boolean enterChecker(String t) {
for (int i = 0; i < t.length(); i++)
if (t.charAt(i) != '\n' && t.charAt(i) != ' ')
return true;
return false;
}
}
EDIT: If you also want a maximum height for your input JTextArea, which scrolls after a maximum height has been reached, I would suggest wrapping it in a scroll pane and updating its preferred size when the text area's changes.
Upvotes: 2