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;
}
}
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.
Related
I have a rectangle, and I am trying to grow it like a graph of some sorts, but it does not show it growing in real time, it just has a white screen then I see a rectangle. Any help would be appreciated, thanks. The code I am having a problem with is under the ¨Animates the bar¨ comment.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Main extends JPanel {
static String[] mainArr;
static int start;
static boolean done = false;
static double datapoint1;
static double datapoint2;
static int jPlaceholder;
public static void main(String[] args) throws Exception {
// Creating the window
JFrame panel = new JFrame();
panel.setSize(450,250);
// Creating the window that shows the animation
JFrame drawingFrame = new JFrame();
drawingFrame.setSize(450,250);
JPanel jp = new JPanel();
jp.setLayout(null);
jp.setBackground(Color.red);
drawingFrame.add(jp);
// Creating all the text fields
JTextField dataTypesTextField = new JTextField("This box is currently not in use. Please do not type anything into this box");
dataTypesTextField.setBounds(50,50, 400,30);
panel.add(dataTypesTextField);
JTextField yearStartTextField = new JTextField("Type in this box what year your data starts in:");
yearStartTextField.setBounds(50,100, 400,30);
panel.add(yearStartTextField);
JTextField yearEndTextField = new JTextField("Type in this box what year your data ends in:");
yearEndTextField.setBounds(50,150, 400,30);
panel.add(yearEndTextField);
// Creating the button to submit the data
JButton enterButton = new JButton("Enter");
enterButton.setBounds(50,200, 100, 30);
panel.add(enterButton);
// =================================== ActionListener for enter button ========================================
enterButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (done==false) {
// Creating the variables to store the data the user just inputted
start = Integer.parseInt(yearStartTextField.getText());
int end = Integer.parseInt(yearEndTextField.getText());
mainArr = new String[end-start+1];
// Gets the data points
dataTypesTextField.setText("Datapoints you will use in order, space between each: ");
done = true;
} else {
// Getting all the data needed
mainArr = dataTypesTextField.getText().split(" ");
double[] datapoints = new double[mainArr.length];
for (int i=0; i<datapoints.length; i++) {
datapoints[i] = Double.parseDouble(mainArr[i]);
}
under here is where I had my problems I am pretty sure, but I could have screwed up somewhere else.
// Animates the bar
for (int i=0; i<datapoints.length-1; i++) {
// Getting all the datapoints
datapoint1 = datapoints[i];
datapoint2 = datapoints[i+1];
int j = 0;
while(j<50) {
j++;
int width = (int) (datapoint1+((datapoint2-datapoint1)/50)*j);
JPanel rectangle = new JPanel();
rectangle.setBackground(Color.black);
rectangle.setBounds(50, 50, width, 30);
jp.add(rectangle);
drawingFrame.setVisible(true);
rectangle.repaint();
System.out.println("The width is: "+width);
at first I thought it was because there was no pause between each ¨frame¨ but it still just shows a white screen, then it shows the rectangle.
try {
Thread.sleep(20);
} catch (Exception exp) {
}
}
}
}
}
});
// =====================================================================================================
// Finishes up both the windows
panel.setLayout(null);
panel.setVisible(true);
}
}
I am creating a dumb phone (like old traditional phone) and I'm using GUI programming. I need help with dialing the numbers. I don't know how to get the numbers to pop up on the display and stay there, and also use the delete button to delete the numbers that is up on the display too. I will post a youtube link so you can see a sample run.
I am currently stuck on passing the text from the button of each number that should display the number, however it's displaying the text of the button. I also, don't know how to keep the number there when other buttons are pressed without it being reset.
Here is my code:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import javax.swing.*;
public class DumbPhone extends JFrame
{
private static final long serialVersionUID = 1L;
private static final int WIDTH = 300;
private static final int HEIGHT = 500;
private static final String CALL_BUTTON_TEXT = "Call";
private static final String TEXT_BUTTON_TEXT = "Text";
private static final String DELETE_BUTTON_TEXT = "Delete";
private static final String CANCEL_BUTTON_TEXT = "Cancel";
private static final String SEND_BUTTON_TEXT = "Send";
private static final String END_BUTTON_TEXT = "End";
private static final String CALLING_DISPLAY_TEXT = "Calling...";
private static final String TEXT_DISPLAY_TEXT = "Enter text...";
private static final String ENTER_NUMBER_TEXT = "Enter a number...";
private JTextArea display;
private JButton topMiddleButton;
private JButton topLeftButton;
private JButton topRightButton;
private JButton[] numberButtons;
private JButton starButton;
private JButton poundButton;
private boolean isNumberMode = true;
private String lastPressed = "";
private int lastCharacterIndex = 0;
private Date lastPressTime;
public DumbPhone()
{
setTitle("Dumb Phone");
setSize(WIDTH, HEIGHT);
setDefaultCloseOperation(EXIT_ON_CLOSE);
createContents();
setVisible(true);
topLeftButton.setEnabled(false);
}
private void createContents()
{
//create JPanel, and JTextArea display
JPanel panel = new JPanel(new GridLayout(5,3));
display = new JTextArea();
display.setPreferredSize(new Dimension(280, 80));
display.setFont(new Font("Helvetica", Font.PLAIN, 32));
display.setLineWrap(true);
display.setEnabled(false);
panel.add(display);
//create JButtons
topLeftButton = new JButton(DELETE_BUTTON_TEXT);
topMiddleButton = new JButton((CALL_BUTTON_TEXT));
topRightButton = new JButton((TEXT_BUTTON_TEXT));
numberButtons = new JButton[10];
numberButtons[1] = new JButton("<html><center>1<br></center></html>");
numberButtons[2] = new JButton("<html><center>2<br>ABC</center></html>");
numberButtons[3] = new JButton("<html><right>3<br>DEF</right></html>");
numberButtons[4] = new JButton("<html><center>4<br>GHI</center></html>");
numberButtons[5] = new JButton("<html><center>5<br>JKL</center></html>");
numberButtons[6] = new JButton("<html><center>6<br>MNO</center></html>");
numberButtons[7] = new JButton("<html><center>7<br>PQRS</center></html>");
numberButtons[8] = new JButton("<html><center>8<br>TUV</center></html>");
numberButtons[9] = new JButton("<html><center>9<br>WXYZ</center></html>");
numberButtons[0] = new JButton("<html><center>0<br>space</center></html>");
poundButton = new JButton("#");
starButton = new JButton("*");
//add JButtons to buttons JPanel
panel.add(topLeftButton);
panel.add(topMiddleButton);
panel.add(topRightButton);
panel.add(numberButtons[1]);
panel.add(numberButtons[2]);
panel.add(numberButtons[3]);
panel.add(numberButtons[4]);
panel.add(numberButtons[5]);
panel.add(numberButtons[6]);
panel.add(numberButtons[7]);
panel.add(numberButtons[8]);
panel.add(numberButtons[9]);
panel.add(starButton);
panel.add(numberButtons[0]);
panel.add(poundButton);
//add Listener instance (inner class) to buttons
topLeftButton.addActionListener(new Listener());
topMiddleButton.addActionListener(new Listener());
topRightButton.addActionListener(new Listener());
//JButton[] array = new JButton[10];
for (int i = 0; i < numberButtons.length; i++)
{
numberButtons[i].addActionListener(new Listener());
numberButtons[i] = new JButton(String.valueOf(i));
}
starButton.addActionListener(new Listener());
poundButton.addActionListener(new Listener());
//add display and buttons to JFrame
setLayout(new BorderLayout());
add(display, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
}
private class Listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == topLeftButton)
{
if(lastPressTime == null)
{
display.setText(ENTER_NUMBER_TEXT);
}
else
{
topLeftButton.setEnabled(true);
lastCharacterIndex--;
lastPressed = lastPressTime.toString();
}
}
else if(e.getSource() == topMiddleButton)
{
if(lastPressTime == null || lastCharacterIndex == 0)
{
display.setText(ENTER_NUMBER_TEXT);
}
else
{
display.setText(CALLING_DISPLAY_TEXT);
}
}
else if(e.getSource() == topRightButton)
{
if(lastPressTime == null || lastCharacterIndex == 0)
{
display.setText(TEXT_DISPLAY_TEXT);
}
else
{
display.setText(CALLING_DISPLAY_TEXT);
}
}
else
{
topLeftButton.setEnabled(true);
if (e.getSource() instanceof JButton)
{
//String text = ((JButton) e.getSource()).getText();
display.setText(lastPressed + " f" + numberButtons[lastCharacterIndex].getText());
}
}
Date currentPress = new Date();
long currentTime = currentPress.getTime();
if(lastPressTime != null)
{
//long lastPressTime = lastPressTime.getTime();
//subtract lastPressTime from currentPress time to find amount of time elapsed since last button pressed.
}
lastPressTime = currentPress;
String buttonLetters = ""; // Parse Letter from button (e.g "abc").
//update lastCharacterIndex.
lastCharacterIndex++;
lastCharacterIndex = lastCharacterIndex % buttonLetters.length();
}
}
for example, if I push the button 2, instead of giving me "2", it will give me < html>< center>2ABC < / center >< / html >
Therefore, I need help with
Having the numberButtons, when pushed to show the numbers that were pushed.
Be able to delete those numbers.
Here is the link to the sample run: https://www.youtube.com/watch?v=evmGWlMSqqg&feature=youtu.be
Try starting the video 20 seconds in.
to delete the number, you can use the labelname.setText("")
At a basic level, you simply want to maintain the "numbers" separately from the UI. This commonly known as a "model". The model lives independently of the UI and allows the model to be represented in any number of possible ways based on the needs of the application.
In your case, you could use a linked list, array or some other simple sequential based list, but the easiest is probably to use a StringBuilder, as it provides the functionality you require (append and remove) and can make a String very simply.
So, the first thing you need to do is create an instance of model as an instance level field;
private StringBuilder numbers = new StringBuilder(10);
this will allow the buffer to be accessed any where within the instance of the class.
Then you need to update the model...
else
{
topLeftButton.setEnabled(true);
if (e.getSource() instanceof JButton)
{
String text = numberButtons[lastCharacterIndex].getText();
numbers.append(text);
}
}
To remove the last character you can simply use something like...
if (numbers.length() > 0) {
numbers.deleteCharAt(numbers.length() - 1);
}
Then, when you need to, you update the UI using something like...
display.setText(numbers.toString());
Now, this is just basic concepts, you will need to take the ideas and apply it to your code base
I am creating a text editor application in Java Swing. I am using JTextPane and I have added code to get all the system fonts and some font sizes in JComboBox.
I have entered the text - "Hello World" in jtextpane and change the font of the words "Hello" to "Arial", font size to 10 and "World" to "Calibri", font size to 12.
My expected scenario:
If I select the word "Hello" or place the cursor in the word "Hello", the font name in the font JCombobox should be changed automatically to "Arial" and font size Jcombobox should be automatically changed to 10, as same as for the word "World", the values in the Jcombobox should be changed to "Calibri" and "12". How can I achieve this?
Thanks in advance.
This basically addresses the issue how to select the combo box item corresponding to the selected text or the cursor position in text. For the example, I have chosen only the font size. The same technique can be applied to the font family also.
The example is a text editor using JTextPane and its document type is a DefaultStyledDocument. There is a JComboBox with a list of font sizes (16 to 50). One can select a section of text in the editor and select a font size from combo box to set the text to that font size. This is achieved using an ItemListener added to the JComboBox. The listener has code to set the editor document's attributes to the newly selected font size - to the selected text.
The editor allows multiple font sizes applied to different parts of the text as in the below picture.
The requirement was when the caret (or cursor) is placed in the text or a portion of selected text - the corresponding font size need to be set in the JComboBox with the font sizes. For this a CaretListener is added to the JTextPane.
This listener's logic mainly locates the caret position, gets the attributes of the document text at that position and extracts the font size attribute. This font size from this attribute is set in the font size combo box.
The example's code:
public class Editor2 {
private JTextPane editor;
private DefaultStyledDocument doc;
private JComboBox<String> fontSizeComboBox;
private boolean caretControlFlag;
private static final int DEFAULT_FONT_SIZE = 18;
public static void main(String [] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Editor2().createAndShowGUI();
}
});
}
private void createAndShowGUI() {
editor = new JTextPane();
editor.setMargin(new Insets(5, 5, 5, 5));
editor.addCaretListener(new MyCaretListener());
JScrollPane editorScrollPane = new JScrollPane(editor);
doc = new DefaultStyledDocument();
initDocAttrs();
editor.setDocument(doc);
final String [] fontSizes = {"Font Size", "16", "18",
"20", "24", "28", "30", "34", "40", "50"};
fontSizeComboBox = new JComboBox<String>(fontSizes);
fontSizeComboBox.setEditable(false);
fontSizeComboBox.addItemListener(new FontSizeItemListener());
JFrame frame = new JFrame("Text Editor");
frame.add(fontSizeComboBox, BorderLayout.NORTH);
frame.add(editorScrollPane, BorderLayout.CENTER);
frame.add(editorScrollPane);
frame.setSize(800, 400);
frame.setLocation(300, 150);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
editor.requestFocusInWindow();
}
private void initDocAttrs() {
Style style = doc.addStyle("my_doc_style", null);
StyleConstants.setFontSize(style, 18);
StyleConstants.setFontFamily(style, "SansSerif");
doc.setParagraphAttributes(0, doc.getLength(), style, true);
}
private class FontSizeItemListener implements ItemListener {
#Override
public void itemStateChanged(ItemEvent e) {
if ((e.getStateChange() != ItemEvent.SELECTED) ||
(fontSizeComboBox.getSelectedIndex() == 0)) {
return;
}
String fontSizeStr = (String) e.getItem();
int newFontSize = 0;
try {
newFontSize = Integer.parseInt(fontSizeStr);
}
catch (NumberFormatException ex) {
return;
}
if (caretControlFlag) {
caretControlFlag = false;
return;
}
setFontAttrs(newFontSize);
editor.requestFocusInWindow();
}
private void setFontAttrs(int newFontSize) {
SimpleAttributeSet attrs = new SimpleAttributeSet();
Style docStyle = doc.getStyle("my_doc_style");
int size = StyleConstants.getFontSize(docStyle);
StyleConstants.setFontSize(attrs, newFontSize);
String attrName = "mysize" + Integer.toString(newFontSize);
attrs.addAttribute(attrName, attrName);
int startPos = editor.getSelectionStart();
String selectedText = editor.getSelectedText();
if (selectedText == null || selectedText.trim().isEmpty()) {
return;
}
int length = selectedText.length();
doc.setCharacterAttributes(startPos, length, attrs, false);
editor.setDocument(doc);
}
}
private class MyCaretListener implements CaretListener {
#Override
public void caretUpdate(CaretEvent e) {
caretControlFlag = true;
int dot = e.getDot();
Element ele = doc.getCharacterElement(dot);
AttributeSet attrs = ele.getAttributes();
String fontSizeStr = "18";
for (Enumeration en = attrs.getAttributeNames(); en.hasMoreElements();) {
String attrName = en.nextElement().toString();
if (attrName.contains("mysize")) {
fontSizeStr = attrName.substring(6);
}
}
fontSizeComboBox.setSelectedItem(fontSizeStr);
caretControlFlag = false;
}
}
}
This question already has answers here:
What is a debugger and how can it help me diagnose problems?
(2 answers)
Closed 4 years ago.
I am working on this program that calculates the Beats per Minute (BPM) when you click the button. When you click two times, it is supposed to display the current BPM, and display the new one with every click after that. What the problem is, though, is that the display isn't changing. What do I need to do?
Here is my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class BPM extends JPanel implements ActionListener {
JLabel label;
public String display;
public int bpm;
public int buttonPressed;
public int time1;
public int time2;
public int time3;
public int counter[];
public void addComponents(Container pane) {
JPanel buttons = new JPanel();
JButton bpmButton = new JButton("Click");
bpmButton.setSize(new Dimension(100, 50));
bpmButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPressed++;
counter = new int[2];
if (buttonPressed == 1) {
counter[0] = (int)(System.currentTimeMillis());
} else if (buttonPressed == 2) {
counter[1] = (int)(System.currentTimeMillis());
calculateTimeBetweenClicks();
setTime();
} else {
counter[0] = counter[1];
counter[1] = (int)(System.currentTimeMillis());
calculateTimeBetweenClicks();
setTime();
}
}
});
display = "0";
label = new JLabel(display, SwingConstants.CENTER);
label.setFont(label.getFont().deriveFont(100.0f)); // original 45
pane.add(label, BorderLayout.PAGE_START);
pane.add(bpmButton, BorderLayout.CENTER);
}
// Calculates the difference between the two saved clicks
public void calculateTimeBetweenClicks() {
if (buttonPressed == 1) {
time1 = counter[0];
} else {
time1 = counter[0];
time2 = counter[1];
}
time3 = time2 - time1;
}
// Calculates the BPM and changes the display accordingly
public void setTime() {
bpm = 60000 / time3;
display = "" + bpm + "";
label.setText(display);
}
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
public static void createAndShowGUI() {
// Creates the window
JFrame frame = new JFrame("BPM Calculator");
frame.setPreferredSize(new Dimension(300, 200)); // original (250, 130)
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Adds the components to the content pane
BPM window = new BPM();
window.addComponents(frame.getContentPane());
//Displays the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// Turns off bold text
UIManager.put("swing.boldMetal", Boolean.FALSE);
// Allows the components to be used and interacted with
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
The problem is in your addComponents method, you are creating a new array on each and every button click (so you end up with a new and empty array). This is throwing off your calculation. Simply move the instantiation of your array to somewhere outside of the ActionListener like this...
public void addComponents(Container pane) {
JPanel buttons = new JPanel();
counter = new int[2]; //Move this line to here...
JButton bpmButton = new JButton("Click");
bpmButton.setSize(new Dimension(100, 50));
bpmButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPressed++;
if (buttonPressed == 1) {
counter[0] = (int)(System.currentTimeMillis());
} else if (buttonPressed == 2) {
counter[1] = (int)(System.currentTimeMillis());
calculateTimeBetweenClicks();
setTime();
} //Removed the else - see edit below :-)
}
});
Additional
Your code as-is seems to get a litle confused after the 2nd click (the first BPM calculation) as it seems to take that 2nd click as the first click of the next set of 2 clicks if you get what I mean. I'm not sure if this is intended behaviour, but if not, I would reset everything in the calculateTimeBetweenClicks method after you've calculated the correct bpm ready for a new set of 2 clicks...
// Calculates the difference between the two saved clicks
public void calculateTimeBetweenClicks() {
if (buttonPressed == 1) {
time1 = counter[0];
} else {
time1 = counter[0];
time2 = counter[1];
//Reset here ready for next 2 clicks...
counter[0]=0;
counter[1]=0;
buttonPressed = 0;
}
time3 = time2 - time1;
}
I am attempting to implmement smart autoscrolling on a JScrollPane containing a JTextPane. The JTextPane is used for logging my app in color. However I'm running into a wall trying to do smart autoscrolling. By smart autoscrolling I don't mean blindly autoscrolling every time something changes, I mean checking to see if your scrolled all the way down, then autoscroll. However no matter what I do it either always autoscrolls or doesn't at all
As a test script, here's the setup (the JFrame has been left out)
final JTextPane textPane = new JTextPane();
textPane.setEditable(false);
final JScrollPane contentPane = new JScrollPane(textPane);
contentPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
And here's the ugly auto add test loop
while (true)
try {
Thread.sleep(1000);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
JScrollBar scrollBar = scroll;
boolean preCheck = ((scrollBar.getVisibleAmount() != scrollBar.getMaximum()) && (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum()));
System.out.println("Value: " + scroll.getValue()
+ " | Visible: " + scrollBar.getVisibleAmount()
+ " | Maximum: " + scrollBar.getMaximum()
+ " | Combined: " + (scrollBar.getValue() + scrollBar.getVisibleAmount())
+ " | Vis!=Max : " + (scrollBar.getVisibleAmount() != scrollBar.getMaximum())
+ " | Comb=Max: " + (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum())
+ " | Eval: " + preCheck);
StyledDocument doc = textPane.getStyledDocument();
doc.insertString(doc.getLength(), "FAGAHSIDFNJASDKFJSD\n", doc.getStyle(""));
if (!preCheck)
textPane.setCaretPosition(doc.getLength());
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
Its not pretty, but it gets the job done.
Here's though the relevant check
boolean preCheck = ((scrollBar.getVisibleAmount() != scrollBar.getMaximum()) && (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum()));
if (preCheck)
textPane.setCaretPosition(doc.getLength());
Thats the part thats been giving me trouble. There is first a check to see if the bar is visible but unusable (not enough text, making the bar the full length), then if the bottom of the bar is equal to the maximum. In theory, that should work. However nothing, including moving the check around, has gotten the results I would like.
Any suggestions?
NOT A DUPLICATE of this or this, as they are wanting it to always scroll, not just sometimes.
Edit:
I replaced the following code with a more flexible version that will work on any component in a JScrollPane. Check out: Smart Scrolling.
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import javax.swing.*;
import javax.swing.text.*;
public class ScrollControl implements AdjustmentListener
{
private JScrollBar scrollBar;
private JTextComponent textComponent;
private int previousExtent = -1;
public ScrollControl(JScrollPane scrollPane)
{
Component view = scrollPane.getViewport().getView();
if (! (view instanceof JTextComponent))
throw new IllegalArgumentException("Scrollpane must contain a JTextComponent");
textComponent = (JTextComponent)view;
scrollBar = scrollPane.getVerticalScrollBar();
scrollBar.addAdjustmentListener( this );
}
#Override
public void adjustmentValueChanged(final AdjustmentEvent e)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
checkScrollBar(e);
}
});
}
private void checkScrollBar(AdjustmentEvent e)
{
// The scroll bar model contains information needed to determine the
// caret update policy.
JScrollBar scrollBar = (JScrollBar)e.getSource();
BoundedRangeModel model = scrollBar.getModel();
int value = model.getValue();
int extent = model.getExtent();
int maximum = model.getMaximum();
DefaultCaret caret = (DefaultCaret)textComponent.getCaret();
// When the size of the viewport changes there is no need to change the
// caret update policy.
if (previousExtent != extent)
{
// When the height of a scrollpane is decreased the scrollbar is
// moved up from the bottom for some reason. Reposition the
// scrollbar at the bottom
if (extent < previousExtent
&& caret.getUpdatePolicy() == DefaultCaret.UPDATE_WHEN_ON_EDT)
{
scrollBar.setValue( maximum );
}
previousExtent = extent;
return;
}
// Text components will not scroll to the bottom of a scroll pane when
// a bottom inset is used. Therefore the location of the scrollbar,
// the height of the viewport, and the bottom inset value must be
// considered when determining if the scrollbar is at the bottom.
int bottom = textComponent.getInsets().bottom;
if (value + extent + bottom < maximum)
{
if (caret.getUpdatePolicy() != DefaultCaret.NEVER_UPDATE)
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
}
else
{
if (caret.getUpdatePolicy() != DefaultCaret.UPDATE_WHEN_ON_EDT)
{
caret.setDot(textComponent.getDocument().getLength());
caret.setUpdatePolicy(DefaultCaret.UPDATE_WHEN_ON_EDT);
}
}
}
private static void createAndShowUI()
{
JPanel center = new JPanel( new GridLayout(1, 2) );
String text = "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n";
final JTextArea textArea = new JTextArea();
textArea.setText( text );
textArea.setEditable( false );
center.add( createScrollPane( textArea ) );
System.out.println(textArea.getInsets());
final JTextPane textPane = new JTextPane();
textPane.setText( text );
textPane.setEditable( false );
center.add( createScrollPane( textPane ) );
textPane.setMargin( new Insets(5, 3, 7, 3) );
System.out.println(textPane.getInsets());
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(center, BorderLayout.CENTER);
frame.setSize(500, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(2000, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
try
{
Date now = new Date();
textArea.getDocument().insertString(textArea.getDocument().getLength(), "\n" + now.toString(), null);
textPane.getDocument().insertString(textPane.getDocument().getLength(), "\n" + now.toString(), null);
}
catch (BadLocationException e1) {}
}
});
timer.start();
}
private static JComponent createScrollPane(JComponent component)
{
JScrollPane scrollPane = new JScrollPane(component);
new ScrollControl( scrollPane );
return scrollPane;
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}