Just started using Swing and I chose null layout because I came from the world of Flash and I'm used to specifying every single pixel and size. I'll pick up on layouts later.
The problem right now is the JTextArea that is the view of the JScrollPane only appears half the time I run the program. I can't select it or type in it because it's not there. The pane appears 100% of the time though. If I hit tab a couple times the focus will land on the text area then it'll appear.
I've removed a huge chunk of the code that has nothing to do with the problem. That's why some of the code looks weird like the MenuItemSelected.
There are so many repaints at the end of the PathsPanel's constructor because it was my "last ditch" effort to fix this problem because I read in many threads that repaint usually fixes things not showing up.
Also if I comment out the setText above the repaints, the text area will show up even less often.
DayPlanner
public DayPlanner () {
//win is a JFrame
win.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
win.setLayout (null);
win.setResizable (false);
win.setVisible (true);
MenuItemSelected ("Paths");
win.repaint ();
}
public void MenuItemSelected (String command) {
ChangePanel (PathsPanel.class, pathsPanel);
}
private void ChangePanel (Class panelClass, Component ref) {
activePanel = new PathsPanel (this);
pathsPanel = (PathsPanel) activePanel;
win.add (activePanel);
win.repaint ();
}
}
PathsPanel
public PathsPanel (DayPlanner parent) {
//extends JPanel
this.parent = parent;
setLayout (null);
setBounds (0, 0, 500, 500);
txtMessages = new JTextArea ();
txtMessages.setOpaque (true);
txtMessages.setBackground (Color.RED);
JScrollPane scpMessage = new JScrollPane (txtMessages, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scpMessage.setBounds (0, 0, 500, 500);
txtMessages.setText ("AWD");
txtMessages.repaint ();
scpMessage.repaint ();
repaint ();
}
I bet this has something to do with not using a LayoutManager!
Related
I have recently begun working with JComponents to create GUI systems. Everything is working but the bottom and right sides of the JFrame do not get painted over and remain white.
Screenshot of running GUI:
In the screenshot you can see the 'drknBtn' is displayed correctly; this is because I hovered over it with the mouse before taking the picture. Hovering over the buttons refreshes them and they appear as normal. Due to this, I would assume the panel that holds them, 'bottomPnl' is covering that white space, but that panels background is not showing at the bottom portion. Any ideas on what could cause this? I have tried calling 'bottomPnl.repaint()' directly before calling pack(), but no change.
My code is below.
Note: For each JComponent, I created a class extending that component. This way I could set default values for the components in the constructors of these classes instead of doing each one individually. I'll list the relevant properties of the Frame and Panels.
Frame: setSize(width,height); setResizeable(false); setLocationRelativeTo(null);
Panel: setLayoutManager(from contructor); setPreferredSize(new Dimension(width,height)); same for setMinimumSize and setMaximumSize.
public Display(String title, int w, int h){
width=w;
height=h;
frame = new FrameUI(title,w,h);
//parent panel
parentPnl= new PanelUI(width,height, new FlowLayout(FlowLayout.CENTER,0,0));
parentPnl.setBackground(new Color(100,175,175));
//top panel
topPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
topPnl.setBackground(new Color(100,175,175));
chooseFileBtn = new ButtonUI("Browse...",topPnl.getWidth()/4,(int)(topPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
fc = new FileChooserUI();
fc.setFileFilter(new FileNameExtensionFilter("Image files", ImageIO.getReaderFileSuffixes()));
int result = fc.showOpenDialog(null);
try {
if (result == JFileChooser.APPROVE_OPTION) {
picture.setIcon(new ImageIcon(ImageIO.read(fc.getSelectedFile()).getScaledInstance(picture.getWidth(),picture.getHeight(), 0)));
}
} catch (Exception iOException) {
}
}
});
//middle panel
midPnl= new PanelUI((int)(width*.85),(int)(height*.7), new FlowLayout(FlowLayout.CENTER,0,0));
midPnl.setBackground(new Color(75,125,125));
picture = new LabelUI("",midPnl.getWidth(),midPnl.getHeight());
picture.setBackground(new Color(75,125,125));
picture.setVisible(true);
picture.setOpaque(true);
picture.setIcon(null);
//bottom panel
bottomPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
bottomPnl.setBackground(new Color(100,175,175));
ltnBtn = new ButtonUI("Lighten Picture",bottomPnl.getWidth()/3,(int)(bottomPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
}
});
ltnBtn.setBackground(Color.LIGHT_GRAY);
ltnBtn.setForeground(Color.BLACK);
drknBtn = new ButtonUI("Darken Picture",bottomPnl.getWidth()/3,(int)(bottomPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
}
});
drknBtn.setBackground(Color.DARK_GRAY);
drknBtn.setForeground(Color.WHITE);
//add UI Objects
topPnl.add(chooseFileBtn);
midPnl.add(picture);
bottomPnl.add(ltnBtn);
bottomPnl.add(drknBtn);
parentPnl.add(topPnl);
parentPnl.add(midPnl);
parentPnl.add(bottomPnl);
Container contentPane = frame.getContentPane();
contentPane.add(parentPnl);
frame.pack();
frame.setVisible(true);
}
}
topPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
looks to me like you are manually trying to control the size of the panels and therefore the size of the components added to your panels. Your calculations are wrong and some components aren't displayed properly. Also all your sizes are fixed at creation time and will not adjust if the size of the frame ever changes.
Don't try to control the sizes manually. Use layout managers to dynamically size components based on the properties of the component.
I fail to see why you would want a button to be 15% of the space available to the frame.
If you want the button to be larger than normal you can set extra empty space around the text of the button by using:
button.setMargin( new Insets(50, 50, 50, 50) );
Then just add the button to a panel using a FlowLayout and let the layout manager do its job.
The default layout for a frame is a BorderLayout, so you can then add the "topPnl" to the frame using:
frame.add(topPnl, BorderLayout.PAGE_START);
The other panels can then be added using:
frame.add(midPnl, BorderLayout.CENTER);
frame.add(bottomPnl, BorderLayout.PAGE_END);
This is how Swing was designed to be used with layout managers.
Read the section from the Swing tutorial on How to Use BorderLayout for more information and examples.
The main point is use methods like setMargin(...), to provide hints to the component on what their preferred size should be.
I fixed the problem by removing the 'setSize()' method in the FrameUI constructor. However, I still do not understand how you could dynamically size panels as you said while still maintaining the proportions I want for them. Thank you #camickr for the pointers, my original problem is fixed. I'll look into more javadocs and tutorials on layout managers and such.
I have set a game menu screen on my JFrame using a layered panel. I want to remove all the components from the JFrame and add a new content panel. I've spent a few hours trying to work through this issue but I am unable to come up with a good solution.
public myJFrame ()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setSize (1030, 727);
setVisible(true);
newGame = new JButton();
loadGame = new JButton();
quitGame = new JButton();
newGame.setBounds(new Rectangle(10,600,200,110));
newGame.setIcon(pic1);
newGame.setOpaque(false);
newGame.setContentAreaFilled(false);
newGame.setBorderPainted(false);
loadGame.setBounds(new Rectangle(210,600,200,110));
loadGame.setIcon(pic2);
loadGame.setOpaque(false);
loadGame.setContentAreaFilled(false);
loadGame.setBorderPainted(false);
quitGame.setBounds(new Rectangle(410,600,200,110));
quitGame.setIcon(pic3);
quitGame.setOpaque(false);
quitGame.setContentAreaFilled(false);
quitGame.setBorderPainted(false);
background.setBounds(0,0,1030,727);
titletext.setBounds(0,0,726,170);
lp = getLayeredPane();
lp.add(titletext, new Integer (4)); //* I messed around with these integers quite a bit. *//
lp.add(newGame, new Integer (20)); //* They don't seem to be logically adding to the panel. *//
lp.add(loadGame, new Integer(20)); //* I want the background and titletext to appear behind *//
lp.add(quitGame, new Integer (20)); //* the JButtons. *//
lp.add(background, new Integer (1));
newGame.addActionListener(this);
loadGame.addActionListener(this);
quitGame.addActionListener(this);
}
public void actionPerformed(ActionEvent event)
{
Object obj = event.getSource();
if (obj == newGame)
{
System.out.println("New Game");
lp.remove(4);
getContentPane().add(mop, "Center"); //* Removes myWelcomePanel mwp and adds myOptionsPanel mop *//
repaint();
mop.startgame.addActionListener(this);
}
I have tried repainting, revalidating and removing only certain components but nothing seems to work. I would expect the code posted above to remove the titletext image, but it removes the background image instead.
I would prefer not to mess with the second content pane because it contains a number of objects which I have configured previously.
JLayeredPane.remove(int) refers to the child's absolute index, not its layer position. Why don't you either remove by reference, or simply set the child component's visibility to hidden?
I found a way to make it work. It seems that I must remove each component individually instead of using removeAll(). I identified a new GridLayout sp and set that as the layout. Finally, I had to use revalidate instead of repaint. Repaint left me with a blank screen. If anyone could comment on what is happening here, I would appreciate it!
public void actionPerformed(ActionEvent event)
{
Object obj = event.getSource();
if (obj == newGame)
{
System.out.println("New Game");
lp.remove(background);
lp.remove(titletext);
lp.remove(newGame);
lp.remove(loadGame);
lp.remove(quitGame);
getContentPane().setLayout(sp);
getContentPane().add(mop, "Center");
revalidate();
mop.startgame.addActionListener(this);
}
public class Gui extends JFrame{
JTextField tf_input;
JTextArea ta_output;
JScrollPane sp_taop;
private class klis implements KeyListener{
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode()==KeyEvent.VK_ENTER){
//System.out.println("enter"); //debug
if (!tf_input.getText().isEmpty()){
String input = tf_input.getText();
ta_output.setText(ta_output.getText()+input+'\n');
System.out.println(input); //debug
System.out.println("ok!"); //debug
sp_taop.validate();
tf_input.setText(null);
}
}
}
}
Gui(){
System.out.println("hello");
inti();
render();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void inti() {
setSize(800, 600);
//text field input
tf_input = new JTextField();
tf_input.setSize(550, 24);
tf_input.addKeyListener(new klis());
//text Area
ta_output = new JTextArea("hello World\n",30,60);
ta_output.setSize(ta_output.getPreferredSize());
ta_output.setLineWrap(true);
ta_output.setEditable(false);
sp_taop = new JScrollPane();
sp_taop.add(ta_output);
sp_taop.setSize(ta_output.getSize().width+20,ta_output.getSize().height);
sp_taop.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
}
private void render() {
Panel p = new Panel(null);
ta_output.setLocation(0, 0);
sp_taop.setLocation(10, 10);
tf_input.setLocation(10, (sp_taop.getSize().height+20));
ta_output.repaint();
//adding
p.add(sp_taop);
p.add(tf_input);
add(p);
}
}
I'm running this on java 1.7.0_25 on vbox 4.3.4 lUbuntu
The problem is that the jscroll is not updating itself! What should i do?
I've been searching google for hours now and i have not found anything close to what I am looking for.
By the way sorry about the plain code it was hard to explain the problem without it.
Don't use JScrollPane#add, this is not how scroll panes work, instead try using JScrollPane#setViewportView
Take a look at How to use Scroll Panes
Don't add you text field to another container. Components can only have a single parent, so adding it to a second container will remove it from the first.
On a side note. I would avoid the use of KeyListener, text components have the capacity to consume key events, meaning that your listener may not actually be notified. Also, if it is called, the component will be in the middle of of mutation operation (it will be trying to update it's model) which can cause unexpected issues and possibly dirty updates.
I might be tempted to use a DocumentListener in this case, but you would need to test it.
I would also avoid the use of null layouts. To start with, you won't be able to effect the position of the text field within the context of the scroll pane as the scroll pane uses it's own layout manager.
Moreover, the differences between OS, video drivers and fonts makes the potential rendering outputs endless. Layout managers take out the guess work when dealing with these situations
I was just having a problem with this:
public class Sales extends JPanel{
ArrayList<JPanel> panes;
ArrayList<String> tabs;
JTabbedPane tp;
public Sales(Dimension d){
setSize(d);
setLayout(null);
tp = new JTabbedPane();
Font f = new Font("Arial",Font.PLAIN,32);
tp.setFont(f);
for(Menu menu : FileManager.menus){
JPanel tmp = new JPanel();
/*int s = (int) Math.ceil(Math.sqrt(menu.products.size()));
tmp.setLayout(new GridLayout(s,s));
System.out.println("size" + s);
for(Product p : menu.products){
p.setFont(f);
tmp.add(p);
}*/
tp.addTab(menu.name,null,tmp,"What is this?");
}
tp.setBounds(0,0,getWidth(),getHeight());
add(tp);
}
}
Where Sales is just added to a simple JFrame:
public Main(){
super("HoboGames Pos System");
setUndecorated(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
sale = new Sales(getSize());
add(sale);
}
Everything works, except the components don't paint until the window is updated because of a click or something. So it's a blank screen until you click stuff.(Sorry, I cut some corners on things like making it full screen...)
They don't update because you've made the window visible before you've finished adding components to it.
Try something like...
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
sale = new Sales(getSize());
add(sale);
setVisible(true);
Alternatively, you could call revalidate and repaint on the frame after you've finished adding your components, but to be honest, it's simpler the first way.
Side Note
Using setSize on a component is strongly discouraged, you should be relying an appropriate layout managed, like BorderLayout to make the decision about the size of the component.
I have a JTabbedPane with some tabs and a lot of unused extra space next to the tabs. So I'm trying to use it and place some buttons there (like in Eclipse). I put the buttons on a GlassPane:
JPanel glasspane = getPanelWithButtons();
// panel with FlowLayout.RIGHT
frame.setGlassPane(glasspane);
glasspane.setOpaque(false);
glasspane.setVisible(true);
This works, and I still can click through on the other elements of my gui (most search results I found are about how to prevent this). The only problem so far is that the mouse pointer doesn't change to that double-ended horizontal arrow when it hovers over the bar of a JSplitPane. How can I get this behaviour back?
EDIT
I found that no mouse changing events from any component under the glass pane are shown. Those components would change the mouse cursor to a hand cursor, zoom lenses and others. None of these mouse pointer changes have an effect any more. I guess this is because with the glass pane, the mouse pointer change needs to be made to the glass pane, but I don't want to do all the mouse pointer changing manually.
Well. I figure out how to do it.
Although I spend more than 5 hours to understand all things behind, but the solution is very simple.
Just overwrite 'public boolean contains(int x, int y)' method of glass panel.
public static void main(String[] args)
{
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setSize(800, 600);
final JSplitPane panel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JPanel(), new JPanel());
frame.getContentPane().add(panel, BorderLayout.CENTER);
final JPanel glassPane = new JPanel(){
#Override
public boolean contains(int x, int y)
{
Component[] components = getComponents();
for(int i = 0; i < components.length; i++)
{
Component component = components[i];
Point containerPoint = SwingUtilities.convertPoint(
this,
x, y,
component);
if(component.contains(containerPoint))
{
return true;
}
}
return false;
}
};
glassPane.setOpaque(false);
JButton button = new JButton("haha");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("haha");
}
});
glassPane.add(button);
glassPane.setBorder(BorderFactory.createLineBorder(Color.red));
frame.setGlassPane(glassPane);
//try to comment out this line to see the difference.
glassPane.setVisible(true);
frame.setVisible(true);
}
Well. I have found a solution to the problem "place buttons next to the tabs". I don't use a glass pane any more, but place the buttons directly:
buttonpanel.setBounds(...);
frame.getLayeredPane().add(buttonpanel, 1);
This works and solves my problem. It is a bit more complicated and involves doing layout by hand and listening to frame resize events, though.
Since I still would like to know how to accomplish this with a glass pane, I'm not accepting this answer. Maybe someone comes up with a solution for a glass pane.