Add jbutton TextArea to the jscrollpane - java

I want a implementation like this.
JscrollPane's panel is divided into two columns.
First column has jtextarea.
Second column should have a jbutton with Ok text.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TestScrollPane extends JFrame {
JPanel newScrollPanel;
JPanel leftPanel;
JScrollPane scrollPane;
JEditorPane editorPane;
JButton button;
public TestScrollPane() {
leftPanel = new JPanel();
newScrollPanel = new JPanel();
editorPane = new JEditorPane();
scrollPane = new JScrollPane(editorPane);
button = new JButton("ok");
leftPanel.setBackground(Color.WHITE);
leftPanel.add(button);
editorPane.setEditable(false);
scrollPane.setPreferredSize(new Dimension(250, 140));
scrollPane.setMinimumSize(new Dimension(10, 10));
String text = "";
for(int i=0;i<50;i++){
text = text + "line " + i + "\n";
}
editorPane.setText(text);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
newScrollPanel.setLayout(new BoxLayout(newScrollPanel, BoxLayout.X_AXIS));
newScrollPanel.add(editorPane);
newScrollPanel.add(leftPanel);
scrollPane.getViewport().add(newScrollPanel);
addComponentListener(new ComponentAdapter() {
#Override
public void componentHidden(ComponentEvent evt) { }
#Override
public void componentShown(ComponentEvent evt) {
changePane();
}
});
this.add(scrollPane);
setFrame();
}
private void changePane() {
leftPanel.setLayout(null);
Insets insets = leftPanel.getInsets();
Dimension size = button.getPreferredSize();
int buttonY = (int) (insets.top + scrollPane.getHeight() - size.getHeight());
button.setPreferredSize(size);
leftPanel.setPreferredSize(new Dimension(((int) size.getWidth()), editorPane.getHeight()));
button.setBounds(0, buttonY, size.width, size.height);
}
void setFrame(){
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
TestScrollPane testScrollPane = new TestScrollPane();
}
});
}
}
I have two questions.
In my class the ok button is not placed in the correct place. when scroll bar mover is at the top corner , ok button should be in the right bottom corner of the visible area. Ok button should be completely visible. In my code only visible a part of the button. Is there any issue with determining the Y coordinates ?
int buttonY = (int) (insets.top + scrollPane.getHeight() - size.getHeight());
Even though the scroll is moved, ok button should not move with the scroll bar. Is that possible to implement ?

Related

Move button on JFrame Java

i have a task to make an application wich will do the following:
If I move a mouse the coordinates should be shown on the status bar
If mouse is clicked then the only one button which is on a JPanel should move to coordinates of click
So the problem is that when i do mouse click - it's fine, button moves to coord's of click, but when i start moving mouse the button comes back to the original position
public class Window extends JFrame {
private JLabel statusBar;
private JPanel mainPanel, statusBarPanel;
JButton button;
public Window()
{
super("Window");
setSize(400,600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainPanel=new JPanel();
statusBarPanel = new JPanel();
statusBar=new JLabel("Coords: ");
add(statusBarPanel, BorderLayout.SOUTH);
add(mainPanel,BorderLayout.CENTER);
mainPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
statusBarPanel.add(statusBar,BorderLayout.CENTER);
button = new JButton("Default text");
mainPanel.add(button);
MyMouseListener myMouseListener=new MyMouseListener();
mainPanel.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
super.mouseMoved(e);
statusBar.setText("Coords: ("+e.getX()+":"+e.getY()+")");
}
});
mainPanel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
button.setLocation(e.getX()-button.getWidth()/2,e.getY()-button.getHeight()/2);
}
});
mainPanel.setFocusable(true);
setVisible(true);
}
}
This is one of the rare cases where you don't want your panel to have a layout manager, since you need absolute positioning.
JPanel has a default layout manager which is a FlowLayout, and your call to setLocation will only have a temporary effect until the panel revalidates its content and places things where they were supposed to be initially.
See the following example with comments, it should give you the general idea :
public class Window extends JFrame {
private final JLabel statusBar;
private final JPanel mainPanel, statusBarPanel;
JButton button;
public Window() {
super("Window");
setSize(400, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainPanel = new JPanel();
mainPanel.setLayout(null);// no layout for absolute positioning
statusBarPanel = new JPanel();
statusBar = new JLabel("Coords: ");
add(statusBarPanel, BorderLayout.SOUTH);
add(mainPanel, BorderLayout.CENTER);
mainPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
statusBarPanel.add(statusBar, BorderLayout.CENTER);
button = new JButton("Default text");
// place the button "manually"
button.setBounds((int) (400 - button.getPreferredSize().getWidth()) / 2, 0,
(int) button.getPreferredSize().getWidth(),
(int) button.getPreferredSize().getHeight());
mainPanel.add(button);
mainPanel.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(final MouseEvent e) {
super.mouseMoved(e);
statusBar.setText("Coords: (" + e.getX() + ":" + e.getY() + ")");
}
});
mainPanel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(final MouseEvent e) {
super.mouseClicked(e);
button.setLocation((int) (e.getX() - button.getPreferredSize().getWidth() / 2),
(int) (e.getY() - button.getPreferredSize().getHeight() / 2));
}
});
mainPanel.setFocusable(true);
setVisible(true);
}
}

Why JPanel.focusGaind and Lost don't work?

Please take a look at the following code (I've missed the imports purposely)
public class MainFrame extends JFrame {
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainFrame frame = new MainFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public MainFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
tabbedPane.setBounds(10, 11, 414, 240);
contentPane.add(tabbedPane);
JPanel panel = new JPanel();
panel.addFocusListener(new FocusListener() {
#Override
public void focusLost(FocusEvent arg0) {
System.out.println("lost");
// I want to do something here, if I reach here!
}
#Override
public void focusGained(FocusEvent arg0) {
System.out.println("gained");
// I want to do something here, if I reach here!
}
});
tabbedPane.addTab("New tab", null, panel, null);
JButton button = new JButton("New button");
panel.add(button);
JPanel panel_1 = new JPanel();
tabbedPane.addTab("New tab", null, panel_1, null);
JPanel panel_2 = new JPanel();
tabbedPane.addTab("New tab", null, panel_2, null);
}
}
I've created this class to test it and then add the onFocusListener in my main code, but it's not working the way I expect. Please tell what's wrong or is this the right EvenetListener at all?
JPanels are not focusable by default. If you ever wanted to use a FocusListener on them, you'd first have to change this property via setFocusable(true).
But even if you do this, a FocusListener is not what you want.
Instead I'd look to listen to the JTabbedPane's model for changes. It uses a SingleSelectionModel, and you can add a ChangeListener to this model, listen for changes, check the component that is currently being displayed and if your component, react.
You are using setBounds and null layouts, something that you will want to avoid doing if you are planning on creating and maintaining anything more than a toy Swing program.
Edit
For example:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("serial")
public class MainPanel extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = 300;
private static final int GAP = 5;
private static final int TAB_COUNT = 5;
private JTabbedPane tabbedPane = new JTabbedPane();
public MainPanel() {
for (int i = 0; i < TAB_COUNT; i++) {
JPanel panel = new JPanel();
panel.add(new JButton("Button " + (i + 1)));
panel.setName("Panel " + (i + 1));
tabbedPane.add(panel.getName(), panel);
}
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout());
add(tabbedPane, BorderLayout.CENTER);
tabbedPane.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent evt) {
Component component = tabbedPane.getSelectedComponent();
System.out.println("Component Selected: " + component.getName());
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
MainPanel mainPanel = new MainPanel();
JFrame frame = new JFrame("MainPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
JPanel is a lightweight container and it is not a Actionable component so it does not get focus events. It lets you add focus listener because of swing component hierarchy. In Order to get tab selected events you need to use JTabbedPane#addChangeListener.
Hope this helps.

Using BoxLayout as vertical FlowLayout to hold JPanel

I want to have a vertical FlowLayout to host my JPanels. A lot of ppl suggest using BoxLayout. However, I realize its behavior is not exactly same as FlowLayout
FlowLayout
BoxLayout with Y axis
As you can see, in FlowLayout, when I stretch parent panel's width, its child panels' width remains the same.
However, in BoxLayout, when I stretch parent panel's height, its child panels' height changed!. This seems to have similar behavior as 1 column 2 rows GridLayout. This is not what I want.
Is there any way to prevent this?
I try to have vertical filler on the top and bottom of parent panel.
new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
But it doesn't help much. My 2 child panels' height still stretch along when I change parent's height.
see how BoxLayout(accepting min, max and preferred size, then resize depends of this value) works,
in compare with FlowLayout (accepting only PreferredSize, child arent resizable with container)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BoxStructAndJComponents {
private JFrame frame;
private JPanel intro;
private JPanel name;
public BoxStructAndJComponents() {
frame = new JFrame("JFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = createUI();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
private JPanel createUI() {
intro = new JPanel() {
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(100, 100);
}
};
intro.setBackground(Color.red);
//intro.setLabelFor(name);
name = new JPanel() {
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(100, 100);
}
};
name.setBackground(Color.blue);
final JButton button = new JButton("Pick a new name...");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
}
});
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20));
intro.setAlignmentX(JComponent.CENTER_ALIGNMENT);
name.setAlignmentX(JComponent.CENTER_ALIGNMENT);
button.setAlignmentX(JComponent.CENTER_ALIGNMENT);
panel.add(intro);
//panel.add(Box.createVerticalStrut(5));
panel.add(Box.createHorizontalStrut(5));
panel.add(name);
panel.add(Box.createRigidArea(new Dimension(150, 10)));
panel.add(button);
return panel;
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
BoxStructAndJComponents listDialogRunner = new BoxStructAndJComponents();
}
});
}
}
You have to use Glue (see: http://docs.oracle.com/javase/tutorial/uiswing/layout/box.html#glue).
A simple and flexible way to achieve this behaviour, is to use GridBagLayout:
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestGridBagLayout {
protected void initUI1() {
final JFrame frame = new JFrame("Grid bag layout");
frame.setTitle(TestGridBagLayout.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
// gbc.weighty = 1.0; Uncomment this line if you want the labels to spread vertically
for (int i = 0; i < 10; i++) {
panel.add(new JLabel("Label " + (i + 1)), gbc);
}
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TestGridBagLayout test = new TestGridBagLayout();
test.initUI1();
}
});
}
}

Create blocking glass pane show method

I've created a modal dialog inside a JFrame using the glass pane. My display method is quite simple: it creates a JPanel as glass pane with some alpha background and adds the JLabel and an ok and close button. Then the glass pane is set and displayed via frame.getGlassPane().setVisible(true);.
Everything works fine: if I call the method the pane is displayed and I can click ok or cancel and the glass pane hides. But the method returns directly after showing the glass pane. But I want it to behave like the JOptionPane methods: they block until the dialog is closed.
But everytime I'm trying to insert any kind of busy waiting at the end of my show method the GUI is frozen if I click the open button. I've also tried to get the mechanism from JDialog#show() but that's a bit to complicated for me.
So how to block the show method while the glass pane is visible?
Here is a simple example:
public class GlassPaneSSCE extends JPanel {
private JFrame parentFrame;
public GlassPaneSSCE(JFrame parent) {
parentFrame = parent;
addKeyListener(new KeyAdapter() {});
addMouseListener(new MouseAdapter() {});
setBackground(new Color(0, 0, 0, 100));
initGui();
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
private void initGui() {
setLayout(new FlowLayout(FlowLayout.CENTER));
setOpaque(false);
final JPanel content = new JPanel(new BorderLayout(4, 4));
content.setOpaque(true);
content.setBorder(new EmptyBorder(8, 8, 8, 8));
JLabel top = new JLabel("Title of this little modal dialog");
content.add(top, BorderLayout.NORTH);
JPanel inner = new JPanel(new BorderLayout());
content.add(inner, BorderLayout.CENTER);
inner.add(new JScrollPane(new JList(new String[] {
"Item 1 ",
"Item 2", "Item 3"
})));
Box ctrlButtons = Box.createHorizontalBox();
ctrlButtons.setBorder(new EmptyBorder(0, 4, 4, 4));
ctrlButtons.add(Box.createHorizontalGlue());
ctrlButtons.add(new JButton(new AbstractAction("OK") {
#Override
public void actionPerformed(ActionEvent e) {
parentFrame.getGlassPane().setVisible(false);
parentFrame.setGlassPane(new JPanel());
}
}));
content.add(ctrlButtons, BorderLayout.SOUTH);
add(content);
}
public void display() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
parentFrame.setGlassPane(GlassPaneSSCE.this);
parentFrame.getGlassPane().setVisible(true);
// Set the focus on the glass pane
requestFocus();
setFocusCycleRoot(true);
}
});
// The next line should be executed only if
// the ok button is clicked and not before
System.out.println("End of display()");
}
public static void main(String[] args) {
final JFrame f = new JFrame();
f.setLayout(new FlowLayout(FlowLayout.CENTER));
JTextArea tp = new JTextArea(10, 10);
for (int i = 0; i < 20; i++) {
JButton b = new JButton("Open");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
GlassPaneSSCE gp = new GlassPaneSSCE(f);
gp.display();
}
});
f.add(b);
tp.append("Item " + (i+1) + "\n");
}
f.add(new JScrollPane(tp));
f.setSize(600, 600);
f.setVisible(true);
}
}

Java scroll JScrollPane with JPanel within to bottom

I have a JScrollPane with a very high JPanel inside, that is changed dynamically, items being appended at its end. What I want, is to scroll to the bottom of aforementioned JScrollPane in order for the newly appended items to be visible instantly on addition (they are not appended to the scroll pane directly, but to its JPanel, and are private objects, so cannot be referenced.
How can I simply have that scroll pane scroll to the very bottom?
Thanks in advance!
JComponent.scrollRectToVisible(Rectangle). Call that on the JPanel instance.
E.G.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ScrollToNewLabel {
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
JPanel gui = new JPanel(new BorderLayout(3,3));
final JPanel panel = new JPanel(new GridLayout(0,1));
JScrollPane scroll = new JScrollPane(panel);
scroll.setPreferredSize(new Dimension(80,100));
gui.add(scroll, BorderLayout.CENTER);
JButton addLabel = new JButton("Add Label");
gui.add(addLabel, BorderLayout.NORTH);
ActionListener listener = new ActionListener() {
int counter = 0;
public void actionPerformed(ActionEvent ae) {
panel.add(new JLabel("Label " + ++counter));
panel.revalidate();
int height = (int)panel.getPreferredSize().getHeight();
Rectangle rect = new Rectangle(0,height,10,10);
panel.scrollRectToVisible(rect);
}
};
addLabel.addActionListener(listener);
JOptionPane.showMessageDialog(null, gui);
}
});
}
}
Screen shot
E.G. 2
This e.g. is based on Vincent's answer, to use JScrollPane.getVerticalScrollBar().setValue(height). Where height is the preferred height of the panel in pixels.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ScrollToNewLabel {
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
JPanel gui = new JPanel(new BorderLayout(3,3));
final JPanel panel = new JPanel(new GridLayout(0,1));
final JScrollPane scroll = new JScrollPane(panel);
scroll.setPreferredSize(new Dimension(80,100));
gui.add(scroll, BorderLayout.CENTER);
JButton addLabel = new JButton("Add Label");
gui.add(addLabel, BorderLayout.NORTH);
ActionListener listener = new ActionListener() {
int counter = 0;
public void actionPerformed(ActionEvent ae) {
panel.add(new JLabel("Label " + ++counter));
panel.revalidate();
int height = (int)panel.getPreferredSize().getHeight();
scroll.getVerticalScrollBar().setValue(height);
}
};
addLabel.addActionListener(listener);
JOptionPane.showMessageDialog(null, gui);
}
});
}
}
scrollRectToVisible(...) and scrollBar.setValue(...) are the general solutions.
You may be interested in Scrolling a Form which ensures that when you tab to a component the form will scroll automatically to make sure the the component will be visible in the scrollpane. Behind the scenes it uses scrollRectToVisible().
A simple way to move the scrollbar all the way to the bottom is to set its value to 100 like this:
scroll.getVerticalScrollBar().setValue(100);
This causes it to move to the bottom of the viewport. You can add this after the component is added to the panel.
This is how I scroll down programmatically.
I like how it scrolls to the bottom rather smoothly instead of jumping there immediately.
/**
* Scrolls a {#code scrollPane} to its bottom.
*
* #param scrollPane
* the scrollPane that we want to scroll all the way down
*
*/
private void scrollDown(JScrollPane scrollPane) {
JScrollBar verticalBar = scrollPane.getVerticalScrollBar();
int currentScrollValue = verticalBar.getValue();
int previousScrollValue = -1;
while (currentScrollValue != previousScrollValue) {
// Scroll down a bit
int downDirection = 1;
int amountToScroll = verticalBar.getUnitIncrement(downDirection);
verticalBar.setValue(currentScrollValue + amountToScroll);
previousScrollValue = currentScrollValue;
currentScrollValue = verticalBar.getValue();
}
}

Categories