I am writing an application which uses a JScrollPane. In this JScrollPane I want to automatically display search results, This means, that I have to dynamically add and remove the results within the JScrollPane. The results are realised as JTextArea, which are embeded within a GridBagLayout.
When there is a high number of search results, the JScrollPane automatically scrolls to the bottom (It should be at the top). I have solved it with a solution I found here. The problem hereby is, that you can see, how it scrolls back to the top. Is it possible to remove this behaviour?
The following things I found out:
I have to remove the previous search results to display the new ones. If I don't remove the previous ones, it displays correctly.
It neither solves the prblem wgeb I update the JScrollPane every tune after adding arow nor when updating only after adding all rows.
The best would be to just disable autoscroll. I have created an executable example to demonstrate this behavior. When clicking the button "Add Row", 500 rows are added. When clicking it several times, it becomes very clear.
Thank you very much for your help!
import java.awt.GridBagConstraints;
import javax.swing.JTextArea;
public class ScrollPaneTest extends javax.swing.JFrame {
private javax.swing.JButton jButton1;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JScrollPane jScrollPane1;
/**
* Creates new form ScrollPaneTest
*/
public ScrollPaneTest() {
initComponents();
}
/**
* Adds a new row.
* #param index The index of the new row.
*/
private void addRow(int index) {
JTextArea row = new JTextArea("Area " + index);
row.setEditable(false);
row.setBorder(null);
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = index;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0);
jPanel2.add(row, gridBagConstraints);
}
/**
* Initializes the components.
*/
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jPanel1 = new javax.swing.JPanel();
jPanel2 = new javax.swing.JPanel();
jButton1 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPane1.setPreferredSize(new java.awt.Dimension(400, 400));
jScrollPane1.setViewportView(jPanel1);
jPanel1.setLayout(new java.awt.BorderLayout());
jPanel2.setLayout(new java.awt.GridBagLayout());
jPanel1.add(jPanel2, java.awt.BorderLayout.NORTH);
jScrollPane1.setViewportView(jPanel1);
jButton1.setText("Create Rows");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addGap(148, 148, 148)
.addComponent(jButton1)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 245, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jButton1)
.addGap(0, 21, Short.MAX_VALUE))
);
pack();
}
/**
* Creates 500 new rows.
* #param evt
*/
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
jPanel2.removeAll();
for(int i = 0; i < 1000; i++) {
addRow(i);
}
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
jScrollPane1.getVerticalScrollBar().setValue(0);
}
});
jPanel2.validate();
jPanel2.repaint();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ScrollPaneTest().setVisible(true);
}
});
}
}
UPDATE 1
I removed the lambda expressions. Hopefully it should be now compileable also with < Java 8.
UPDATE 2
The problem with the disturbing scrolling behavior has been solved by replacing
jPanel2.validate();
jPanel2.repaint();
with
jScrollPane1.validate();
jScrollPane1.repaint();
Nevertheless, both answers to this question can be very helpful in some other cases and should be given attention.
One way to achieve this is to have a custom JViewPort for your scrollpane. This custom viewport overrides setViewPosition and uses a flag to prevent the scroll, or not.
Here is an example of such code, before changing the content of the textarea, we "lock" the viewport to prevent scrolling, and we unlock it later:
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestNoScrolling {
private int lineCount = 0;
private LockableViewPort viewport;
private JTextArea ta;
private final class LockableViewPort extends JViewport {
private boolean locked = false;
#Override
public void setViewPosition(Point p) {
if (locked) {
return;
}
super.setViewPosition(p);
}
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
}
protected void initUI() {
JFrame frame = new JFrame("test");
ta = new JTextArea(5, 30);
JScrollPane scrollpane = new JScrollPane();
viewport = new LockableViewPort();
viewport.setView(ta);
scrollpane.setViewport(viewport);
frame.add(scrollpane);
frame.pack();
frame.setVisible(true);
Timer t = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
viewport.setLocked(true);
ta.append("Some new line " + lineCount++ + "\n");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
viewport.setLocked(false);
}
});
}
});
t.setRepeats(true);
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestNoScrolling().initUI();
}
});
}
}
You could simply set the Caret position to the start position (0), for example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ScrollNoMore {
public static void main(String[] args) {
new ScrollNoMore ();
}
public ScrollNoMore () {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextArea ta;
private JScrollPane sp;
private Random rnd = new Random();
private boolean initalised = false;
public TestPane() {
setLayout(new BorderLayout());
ta = new JTextArea(20, 40);
sp = new JScrollPane(ta);
add(sp);
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long value = rnd.nextLong();
ta.append(String.valueOf(value) + "\n");
if (!initalised) {
ta.setCaretPosition(0);
initalised = true;
}
}
});
timer.start();
}
}
}
This will only set it the first time the Timer runs, this means that if you move the Caret or scroll position for some reason, it won't "flick" back. You could set up a series of states where by if the user moved the current view, it didn't effect the scrolling, but could be reset as required.
Related
I have a game that has a MainUI class, a GameClass and a SettingsClass. The game works fine except when I click "Restart" or "Help" which takes me to another JFrame and then returns back to the MainUI by a click of a button. But, when that happens, the MainUI class does not dispose of itself when it should. I believe it has to do with instantiating the MainUI class again when I leave and come back from settings but I'm but sure how to fix it.
This is the code I'm using to dispose of the MainUI frame and open a new JFrame:
private final JFrame mainFrame;
mainFrame.dispose(); //the mainFrame variable is passed in the constructor since it's trying to dispose of the MainUI
EndingPage endingPage = new EndingPage(); //open ending page
endingPage.setVisible(true);
This is an example of part of the code for one of the buttons in the settings class
restart.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
MainUI.started=false;
mainFrame.dispose();
TitlePage title = new TitlePage();
title.setVisible(true);
}
});
When one of those buttons are clicked, another JFrame is opened accordingly. To go back to the MainUI this is run:
this.dispose();
MainUI main = new MainUI();
main.setVisible(true);
For more clarification, the second code is run when the user clicks the settings button. Then, the last code is run to get back to the JFrame it already was at. When the user wins the first code is run. But the issue is sometimes the mainFrame.dispose(); in the first code does not work. Any help is appreciated! If you need to see other code please tell me! I'm stuck.
EDIT:
This is the MainUI (there is a timer because I want to delay before the action)
package stackoverflowcode;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class MainUI extends javax.swing.JFrame {
/**
* Creates new form MainUI
*/
public MainUI() {
initComponents();
GameClass game = new GameClass(this, end);
SettingsClass settings = new SettingsClass(restart, this);
Timer timer;
private javax.swing.JButton end;
private javax.swing.JButton restart;
ActionListener action = new ActionListener(){
#Override
public void actionPerformed(ActionEvent event)
{
game.openEnd();
}
};
timer= new Timer (1000,action);
timer.start();
settings.settings();
}
private void initComponents() {
restart = new javax.swing.JButton();
end = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
restart.setText("SETTINGS");
end.setText("END");
javax.swing.GroupLayout layout = new
javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(151, 151, 151)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(end)
.addComponent(restart))
.addContainerGap(168, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(120, 120, 120)
.addComponent(restart)
.addGap(48, 48, 48)
.addComponent(end)
.addContainerGap(86, Short.MAX_VALUE))
);
pack();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MainUI().setVisible(true);
}
});
}
}
SettingsClass
package stackoverflowcode;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
public class SettingsClass {
JButton restart;
JFrame mainFrame;
public SettingsClass(JButton restart, JFrame mainFrame){
this.restart=restart;
this.mainFrame=mainFrame;
}
public void settings() {
restart.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
mainFrame.dispose();
TitlePage title = new TitlePage();
title.setVisible(true);
}
});
}
}
TitlePage
package stackoverflowcode;
public class TitlePage extends javax.swing.JFrame {
/**
* Creates new form TitlePage
*/
public TitlePage() {
initComponents();
private javax.swing.JButton Play;
}
private void initComponents() {
Play = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
Play.setText("PLAY");
Play.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
PlayActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new
javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(134, 134, 134)
.addComponent(Play)
.addContainerGap(209, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(129, 129, 129)
.addComponent(Play)
.addContainerGap(148, Short.MAX_VALUE))
);
pack();
}
private void PlayActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
this.dispose();
MainUI main = new MainUI();
main.setVisible(true);
}
public static void main(String args[]){
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new TitlePage().setVisible(true);
}
});
}
}
GameClass
package stackoverflowcode;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
public class GameClass {
private final JFrame mainFrame;
private final JButton endButton;
public GameClass(JFrame mainFrame, JButton endButton){
this.mainFrame=mainFrame;
this.endButton=endButton;
}
public void openEnd(){
endButton.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
mainFrame.dispose(); //the mainFrame variable is passed in the constructor since it's trying to dispose of the MainUI
TitlePage endingPage = new TitlePage(); //open title page
endingPage.setVisible(true);
}
});
}
}
package newjframe;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.event.MouseInputAdapter;
public class NewJFrame extends javax.swing.JFrame {
public NewJFrame() {
initComponents();
setLocationRelativeTo(null);
}
public class Draggable extends JComponent {
private Point pointPressed;
private JComponent draggable;
public Draggable(final JComponent component, final int x, final int y) {
draggable = component;
setCursor(new Cursor(Cursor.HAND_CURSOR));
setLocation(x, y);
setSize(component.getPreferredSize());
setLayout(new BorderLayout());
add(component);
MouseInputAdapter mouseAdapter = new MouseHandler();
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
public class MouseHandler extends MouseInputAdapter {
#Override
public void mouseDragged(final MouseEvent e) {
Point pointDragged = e.getPoint();
Point location = getLocation();
location.translate(pointDragged.x - pointPressed.x,
pointDragged.y - pointPressed.y);
setLocation(location);
}
#Override
public void mousePressed(final MouseEvent e) {
pointPressed = e.getPoint();
}
}
}
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
panel = new javax.swing.JPanel();
layer = new javax.swing.JLayeredPane();
cbb = new javax.swing.JComboBox<>();
label = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
cbb.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3"}));
cbb.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cbbActionPerformed(evt);
}
});
layer.setLayer(cbb, javax.swing.JLayeredPane.DEFAULT_LAYER);
layer.setLayer(label, javax.swing.JLayeredPane.DEFAULT_LAYER);
javax.swing.GroupLayout layerLayout = new javax.swing.GroupLayout(layer);
layer.setLayout(layerLayout);
layerLayout.setHorizontalGroup(
layerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layerLayout.createSequentialGroup()
.addGap(41, 41, 41)
.addComponent(cbb, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(129, 129, 129)
.addComponent(label)
.addContainerGap(202, Short.MAX_VALUE))
);
layerLayout.setVerticalGroup(
layerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layerLayout.createSequentialGroup()
.addGap(30, 30, 30)
.addGroup(layerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(label)
.addComponent(cbb, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(329, Short.MAX_VALUE))
);
javax.swing.GroupLayout panelLayout = new javax.swing.GroupLayout(panel);
panel.setLayout(panelLayout);
panelLayout.setHorizontalGroup(
panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(layer)
);
panelLayout.setVerticalGroup(
panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(layer)
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(panel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(panel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
pack();
}// </editor-fold>
private void cbbActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
int[] xy = {100, 200, 300};
label.setText("DRAG THIS");
switch (cbb.getSelectedIndex()) {
case 0:
draglabel = new Draggable(label, xy[0], xy[0]);
layer.add(draglabel);
break;
case 1:
draglabel = new Draggable(label, xy[0], xy[1]);
layer.add(draglabel);
break;
case 2:
draglabel = new Draggable(label, xy[0], xy[2]);
layer.add(draglabel);
break;
default:
draglabel = new Draggable(label, xy[0], xy[0]);
layer.add(draglabel);
}
}
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JComboBox<String> cbb;
private javax.swing.JLabel label;
private javax.swing.JLayeredPane layer;
private javax.swing.JPanel panel;
// End of variables declaration
Draggable draglabel;
}
The above code should allow the Jlabels to be dragged and dropped with ease, but every time they're called with the actionlistener in JComboBox, they become harder to drag and drop. GIF added to for better clarity https://imgur.com/rPL5ZMC I have tried repaint() method in the class method, and actionlistner but it didn't work
Again, I would tend to simplify try to clarify things, including:
Use the much simpler MouseListener/MouseAdapter and not D&D as you initially were doing. You appear to have made this change.
Not wrapping the JLabel unnecessarily in another component
Thus the Mouse listeners would be added directly on the JLabel and not on a wrapper object.
Not changing the JLayeredPane's default and unique layout
Explicitly removing the old components before adding new ones
Calling repaint() on the JLayeredPane after adding or removing components.
For example, something like this:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class TestDragging extends JPanel {
private static final String[] ITEMS = {"Item One", "Item Two", "Item Three"};
private static final Dimension PREF_SIZE = new Dimension(400, 400);
private JLabel label;
private JComboBox<String> comboBox = new JComboBox<>(ITEMS);
private JLayeredPane layeredPane = new JLayeredPane();
public TestDragging() {
comboBox.addActionListener(new ComboListener());
JPanel topPanel = new JPanel();
topPanel.add(comboBox);
layeredPane.setPreferredSize(PREF_SIZE);
layeredPane.setBorder(BorderFactory.createEtchedBorder());
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(layeredPane, BorderLayout.CENTER);
}
private class ComboListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (label != null) {
layeredPane.remove(label);
}
int index = comboBox.getSelectedIndex();
int x = 100;
int y = 100 + 100 * index;
String text = comboBox.getSelectedItem().toString();
label = createDraggingLabel(x, y, text);
layeredPane.add(label, JLayeredPane.DEFAULT_LAYER);
layeredPane.repaint();
}
private JLabel createDraggingLabel(int x, int y, String text) {
JLabel label = new JLabel(text);
label.setSize(label.getPreferredSize());
label.setLocation(x, y);
MyMouse myMouse = new MyMouse();
label.addMouseListener(myMouse);
label.addMouseMotionListener(myMouse);
return label;
}
}
private static void createAndShowGui() {
TestDragging mainPanel = new TestDragging();
JFrame frame = new JFrame("Test Dragging");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MyMouse extends MouseAdapter {
private Point pointPressed = null;
#Override
public void mousePressed(MouseEvent e) {
pointPressed = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
label.setCursor(new Cursor(Cursor.HAND_CURSOR));
Point pointDragged = e.getPoint();
Point location = label.getLocation();
int dx = pointDragged.x - pointPressed.x;
int dy = pointDragged.y - pointPressed.y;
location.translate(dx, dy);
label.setLocation(location);
Container container = label.getParent();
container.repaint();
}
}
I've called and instantiated the drag n drop function in the constructor then I've used label.setLocation(x, y) to solve this problem since the JComboBox merely determines the position of the labels, I do not need to have re-instantiate the object every time actionperformed is called.
how do I pass the user credentials back to the containing JFrame, so that the specific user is known by the JFrame? The JFrame has a main method.
Can the containing JFrame somehow get the user from the Dialog?
when the jbtOk actionListener finds an acceptable User, then...?
package legacy.gui.general;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import legacy.database.User;
import legacy.database.Users;
public class LoginDialog extends javax.swing.JDialog {
private static final Logger log = Logger.getLogger(LoginDialog.class.getName());
private final JLabel jlblUsername = new JLabel("Username");
private final JLabel jlblPassword = new JLabel("Password");
private final JTextField jtfUsername = new JTextField(15);
private final JPasswordField jpfPassword = new JPasswordField();
private final JButton jbtOk = new JButton("Login");
private final JButton jbtCancel = new JButton("Cancel");
private final JLabel jlblStatus = new JLabel(" ");
public LoginDialog() {
this(null, true);
}
public LoginDialog(java.awt.Frame parent, boolean modal) {
super(parent, modal);
log.info("simple constructor..");
initComponents();
}
public LoginDialog(final JFrame parent, boolean modal) {
super(parent, modal);
log.info("complex constructor..");
JPanel p3 = new JPanel(new GridLayout(2, 1));
p3.add(jlblUsername);
p3.add(jlblPassword);
JPanel p4 = new JPanel(new GridLayout(2, 1));
p4.add(jtfUsername);
p4.add(jpfPassword);
JPanel p1 = new JPanel();
p1.add(p3);
p1.add(p4);
JPanel p2 = new JPanel();
p2.add(jbtOk);
p2.add(jbtCancel);
JPanel p5 = new JPanel(new BorderLayout());
p5.add(p2, BorderLayout.CENTER);
p5.add(jlblStatus, BorderLayout.NORTH);
jlblStatus.setForeground(Color.RED);
jlblStatus.setHorizontalAlignment(SwingConstants.CENTER);
setLayout(new BorderLayout());
add(p1, BorderLayout.CENTER);
add(p5, BorderLayout.SOUTH);
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
jbtOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
List<User> users = new Users().getUsers();
for (User u : users) {
if (Arrays.equals(u.getPassword().toCharArray(), jpfPassword.getPassword())
&& u.getUser().equals(jtfUsername.getText())) {
parent.setVisible(true);
setVisible(false);
} else {
jlblStatus.setText("Invalid username or password");
}
}
}
});
jbtCancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
parent.dispose();
System.exit(0);
}
});
}
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 580, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 389, Short.MAX_VALUE)
);
pack();
}// </editor-fold>
// Variables declaration - do not modify
// End of variables declaration
}
the JFrame creates the Dialog:
public MainFrame() {
initComponents();
passDialog = new LoginDialog(this, true);
passDialog.setVisible(true);
// setExtendedState(JFrame.MAXIMIZED_BOTH);
}
First of all, create a private instance field called User...
private User user;
Next, change you Login ActionListener to store the matching user...
jbtOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
List<User> users = new Users().getUsers();
for (User u : users) {
if (Arrays.equals(u.getPassword().toCharArray(), jpfPassword.getPassword())
&& u.getUser().equals(jtfUsername.getText())) {
user = u;
dispose();
break;
} else {
jlblStatus.setText("Invalid username or password");
}
}
}
});
Next, create a method in your LoginDialog to allow the caller to get the User value...
public User getUser() {
return user;
}
Next, when the LoginDialog is closed, get the User...
public MainFrame() {
initComponents();
passDialog = new LoginDialog(this, true);
passDialog.setVisible(true);
User user = passDialog.getUser();
}
Side notes
Don't try and control the caller from the LoginDialog. It is not the login dialogs responsibility to determine what should happen if the user selects to Cancel, for example. The LoginDialog should simply do it's contractual job (collect the user details and verify the credentials) and leave the rest up to the caller...
Sometimes the setBounds() works but only in the actionlistener.
my code:
-does not work
public panel() {
initComponents();
setBounds(100,100,105,105);
setMaximumSize(new Dimension(100,100));
}
-does work
private void btnBestellingItemToevActionPerformed(java.awt.event.ActionEvent evt) {
setBounds(100,100,105,105);
setMaximumSize(new Dimension(100,100));
}
-layout manager:
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(btnBestellingItemToev, javax.swing.GroupLayout.PREFERRED_SIZE, 89, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(txtWat, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(txtTafelNr, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 80, Short.MAX_VALUE)))
.addGap(0, 131, Short.MAX_VALUE))
);
Can anyone help me so it works in the constructor also?
As I said, the problem you're having is the fact that the container the component you are trying to move/size has a layout manager on it.
The layout manager is responsible for determining the position and size of all the components within in it.
You have two choices. Write your own layout manager that does what you want it to do. This is reasonably complex process, but is generally a better choice or do without a layout manager.
Personally, when doing things like this, I paint the result directly to the panel, but that's me...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class BouncyBall {
public static void main(String[] args) {
new BouncyBall();
}
public BouncyBall() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BallCourt());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BallCourt extends JPanel {
private JLabel beachBall;
private int delta = 4;
public BallCourt() {
setLayout(null);
beachBall = new JLabel();
try {
beachBall.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/BeachBallOfDoom.png"))));
} catch (IOException ex) {
ex.printStackTrace();
}
beachBall.setBounds(0, 100 - 16, 32, 32);
add(beachBall);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int x = beachBall.getX() + delta;
if (x + beachBall.getWidth() > getWidth()) {
delta *= -1;
x = (getWidth() - beachBall.getWidth()) + delta;
} else if (x < 0) {
delta *= -1;
x = delta;
}
beachBall.setLocation(x, beachBall.getY());
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Code in Question:
textArea.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
posX = e.getX();
posY = e.getY();
}
});
textArea.addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
setLocation(e.getXOnScreen() - posX, e.getYOnScreen() - posY);
}
});
Background:
I have a JFrame, in that JFrame there is a JScrollPane, and in the JScrollPane there is a JTextArea called "textArea". This JTextArea take up the entire JFrame and the JFrame is undecorated. So to give some perspective, here is generally what the JFrame looks like...
When the mouse clicks within the JTextArea and moves, the entire window is dragged. Everything is setup to not be focus able for this work, it's meant to be an overlay.
Issue:
The code listed above works fine and the world is at peace. But once there is enough text for the vertical scroll bar to appear (There is no horizontal because of line wrapping), dragging the window becomes an issue. When you click and just begin to move, the JFrame instantly moves much higher on the screen. The lines in JTextArea, the higher it moves up when you try to move it. I assume that the get*OnScreen() methods are issue because it's all relevant to the JTextArea.
Class in Question:
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main extends JFrame {
private JTextArea textArea;
private JScrollPane textAreaScroll;
private int posX = 0;
private int posY = 0;
public Main() {
initComponents();
initListeners();
for(int i = 0; i < 20; i++){
addLine(i+" Hello");
}
}
public void addLine(String line){
textArea.append("\n> "+line);
textArea.setCaretPosition(textArea.getDocument().getLength());
}
private void initListeners(){
textArea.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
posX = e.getX();
posY = e.getY();
}
});
textArea.addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
setLocation(e.getXOnScreen() - posX, e.getYOnScreen() - posY);
}
});
}
private void initComponents() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {}
textAreaScroll = new JScrollPane();
textArea = new JTextArea();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setUndecorated(true);
setAlwaysOnTop(true);
setAutoRequestFocus(false);
setBackground(new Color(130,210,255,130));
setFocusCycleRoot(false);
setFocusable(false);
setFocusableWindowState(false);
setName("main");
setOpacity(0.4f);
setResizable(false);
textAreaScroll.setBorder(null);
textAreaScroll.setFocusable(false);
textAreaScroll.setRequestFocusEnabled(false);
textArea.setEditable(false);
textArea.setBackground(new Color(0, 0, 0));
textArea.setColumns(20);
textArea.setFont(new Font("Consolas", 0, 14));
textArea.setForeground(new Color(255, 255, 255));
textArea.setLineWrap(true);
textArea.setRows(5);
textArea.setText("> Hello world!\n> another line!");
textArea.setBorder(null);
textArea.setFocusable(false);
textArea.setRequestFocusEnabled(false);
textAreaScroll.setViewportView(textArea);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(textAreaScroll, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(textAreaScroll, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 214, Short.MAX_VALUE)
);
pack();
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Main().setVisible(true);
}
});
}
}
Well your diagnosis was absolutely spot on:
When you click and just begin to move, the JFrame instantly moves
much higher on the screen. The lines in JTextArea, the higher it moves
up when you try to move it. I assume that the get*OnScreen() methods
are issue because it's all relevant to the JTextArea.
So to resolve this use GlassPane of JFrame to attach MouseXXXListeners thus we can get correct co-ordinates when dragging, the main problem with this solution is glasspane will consume events that are meant for other components on JFrame, this can be overcome by redispatching the MouseEvents appropriately):
Create JPanel (this glassPane/JPanel will be transparent via setOpaque(false)), attach xxxAdapters here.
Create custom listener class to redispacth MouseEvents to the necessary components (as glasspane will consume all events to the JTextArea/JScollPane)
Set JPanel as GlassPane of your JFrame via JFrame#setGlassPane(..) .
set JFrame visible than set glassPane visible via setVisible(true) (this has been a Swing glitch for some time if you set it visible before the frame is visible it wont be shown).
Here is your fixed code:
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.MouseInputAdapter;
public class Main extends JFrame {
private JTextArea textArea;
private JScrollPane textAreaScroll;
private JPanel glassPane;//create variable for glasspane
public Main() {
initComponents();
initListeners();
for (int i = 0; i < 20; i++) {
addLine(i + " Hello");
}
}
public void addLine(String line) {
textArea.append("\n> " + line);
textArea.setCaretPosition(textArea.getDocument().getLength());
}
private void initListeners() {
GlassPaneListener gpl = new GlassPaneListener(textAreaScroll.getVerticalScrollBar(), this);
//add the adapters/listeners to the glasspane
glassPane.addMouseMotionListener(gpl);
glassPane.addMouseListener(gpl);
}
private void initComponents() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
}
textAreaScroll = new JScrollPane();
textArea = new JTextArea();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setUndecorated(true);
setAlwaysOnTop(true);
setAutoRequestFocus(false);
setBackground(new Color(130, 210, 255, 130));
setFocusCycleRoot(false);
setFocusable(false);
setFocusableWindowState(false);
setName("main");
setOpacity(0.4f);
setResizable(false);
textAreaScroll.setBorder(null);
textAreaScroll.setFocusable(false);
textAreaScroll.setRequestFocusEnabled(false);
textArea.setEditable(false);
textArea.setBackground(new Color(0, 0, 0));
textArea.setColumns(20);
textArea.setFont(new Font("Consolas", 0, 14));
textArea.setForeground(new Color(255, 255, 255));
textArea.setLineWrap(true);
textArea.setRows(5);
textArea.setText("> Hello world!\n> another line!");
textArea.setBorder(null);
textArea.setFocusable(false);
textArea.setRequestFocusEnabled(false);
textAreaScroll.setViewportView(textArea);
textAreaScroll.setPreferredSize(new Dimension(200, 200));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(textAreaScroll, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE));
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(textAreaScroll, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 214, Short.MAX_VALUE));
//create and make glasspane not opaque
glassPane = new JPanel();
glassPane.setOpaque(false);
//set glasspane as JFrame glassPane
setGlassPane(glassPane);
pack();
setVisible(true);//set JFrame visible
//glassPane can only be setVisible after JFrame is visible
glassPane.setVisible(true);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Main();
}
});
}
}
class GlassPaneListener extends MouseInputAdapter {
private int posX = 0;
private int posY = 0;
Toolkit toolkit;
private final Container contentPane;
private final Component textAreaScroll;
private final Component glassPane;
private final JFrame frame;
private boolean wasClickOnInterestedComponent = false;
public GlassPaneListener(Component textAreaScroll, JFrame frame) {
toolkit = Toolkit.getDefaultToolkit();
this.textAreaScroll = textAreaScroll;
this.frame = frame;
this.glassPane = frame.getGlassPane();
this.contentPane = frame.getContentPane();
}
#Override
public void mouseDragged(MouseEvent e) {
if (!redispatchMouseEvent(e)) {
frame.setLocation(e.getXOnScreen() - posX, e.getYOnScreen() - posY);
}
}
#Override
public void mousePressed(MouseEvent e) {
if (!redispatchMouseEvent(e)) {//check if event was redispatched if not its meant for us :)
posX = e.getX();
posY = e.getY();
}
}
#Override
public void mouseReleased(MouseEvent me) {
wasClickOnInterestedComponent = false;
}
private boolean redispatchMouseEvent(MouseEvent e) {
Point glassPanePoint = e.getPoint();
Container container = contentPane;
Point containerPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, contentPane);
// The mouse event is probably over the content pane.
// Find out exactly which component it's over.
Component component = SwingUtilities.getDeepestComponentAt(container, containerPoint.x,
containerPoint.y);
if ((component != null) && (component.equals(textAreaScroll)) || wasClickOnInterestedComponent) {
wasClickOnInterestedComponent = true;//so that if we drag iur cursor off JScrollBar tghe window wont be moved
// Forward events over the scrollbar
Point componentPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, component);
component.dispatchEvent(new MouseEvent(component, e.getID(), e.getWhen(), e.getModifiers(),
componentPoint.x, componentPoint.y, e.getClickCount(), e.isPopupTrigger()));
return true;//the event was redispatched
} else {
return false;//event was not redispatched
}
}
}
You are saying JFrame instantly moves much higher on the screen. The lines in JTextArea but actually it is scrolled down so only last few lines are visible.
If you would like to see textArea content from top, change is here
initComponents();
initListeners();
for (int i = 0; i < 20; i++) {
addLine(i + " Hello");
}
//set scrolling position to top
textArea.setCaretPosition(0);