Whenever I set the panel's Layout to FlowLayout, the JTable appears, however my imageBackground and buttons are misplaced. And when I set the layout to null, the the table doesn't appear, but the buttons and imageBackground are where I wanted them to be. What am I'm going to do with this?
public class AssetPanel extends JPanel implements ActionListener{
private ArrayList<AssetDetails> assetList;
private Frame frame;
private Database db;
private JTable assetTable;
private JScrollPane scrollPane;
private JButton btnBack;
private JButton btnView;
public AssetPanel (Frame frame){
super();
this.frame = frame;
initialize();
}
public void initialize(){
setName("Assets");
setSize(700, 475);
setLayout(null);
setVisible(true);
db = new Database();
btnView = new JButton("View");
btnView.addActionListener(this);
btnView.setBounds(450, 400, 90, 20);
add(btnView);
btnBack = new JButton("Back");
btnBack.setFont(new Font("Tahoma", Font.BOLD, 12));
btnBack.setBounds(550, 400, 90, 20);
btnBack.addActionListener(this);
add(btnBack);
ImageIcon imageBackground = new ImageIcon(AssetPanel.class.getResource("/resources/assets.png"));
JLabel jlBackground = new JLabel(imageBackground);
jlBackground.setBounds(0,0, 700, 475);
add(jlBackground);
initializeTable();
}
#Override
public void actionPerformed(ActionEvent ae) {
if(ae.getSource() == btnBack){
frame.changePanel("Main Menu");
}
}
public void initializeTable(){
Object[][] assetData;
assetList = new ArrayList<>();
String[] columnNames = {"Asset Name", "Date Acquired", "Type", "Classification"};
assetList = db.getAssetTable();
assetData = new Object[assetList.size()][columnNames.length];
for(int i = 0; i < assetList.size(); i++){
assetData[i][0] = assetList.get(i).getAssetName();
assetData[i][1] = assetList.get(i).getDateAcquired();
assetData[i][2] = assetList.get(i).getType();
assetData[i][3] = assetList.get(i).getClassification();
}
assetTable = new JTable(assetData, columnNames);
assetTable.setPreferredScrollableViewportSize(new Dimension(400, 100));
assetTable.setLocation(150, 100);
assetTable.setFillsViewportHeight(true);
scrollPane = new JScrollPane(assetTable);
add(scrollPane);
}
}
Don't use a null layout or use the setBounds() method to position and size components.
however my imageBackground and buttons are misplaced
A background is a Container component. That is you create it as a component and paint an image as the background. Then you add other components to the background component. Now the image will appear in the background and the other components appear on top of it.
See the Background Panel to give an example of creating a background component.
On possible solution: I recommend switching to Mig Layout as a solution to all java layout problems. I now use it for the layout of every single container component in my apps. If you switch you'll probably be glad you did (will never again have problems like that listed in this question).
http://www.miglayout.com/
MigLayout may be included in the JDK in a future version of java.
null layouts mean you have to explicitly place all the components.
I recommend BoxLayout. it's really simple, and you can put in spacers to create space between objects, and glue to fill in all remaining space.
you can also nest the boxes, as well.
if you look at java sample code (and at the source for things);
they nest a lot of JPanels to get the complicated layouts.
Try adding this before trying the steps below if it does not work:
// Set your flow layout thus:
setLayout(new FlowLayout(FlowLayout.LEFT,5,5));
// Set your table Auto Resize Mode to OFF
assetTable.setAutoResizeMode(assetTable.AUTO_RESIZE_OFF);
EXTRA: Try if above tips does not help
Technically, your class should extend a JFrame.
Add a root layout to the class(i.e. the JFrame):
setLayout(new FlowLayout(FlowLayout.LEFT,5,5));
Create two panels; one should contain your label and buttons components.
The other should contain the JScrollPane that contains your table.
Both panels can have their Layout which determines how the components will be laid out.
You can use FlowLayout.
Then you can add both panels to the mother layout (JFrame).
Related
I have an application where I use two JPanels. One of them is a PaintPanel. The second panel,the jtextfield and the jtextarea work fine but they look too cramped against the right side. I tried changing the sizes with setSize() but it didn't work.
The code for the paintpanel
public void center() {
jpCenter = new PaintPanel();
jpCenter.addMouseListener(this);
jpCenter.setSize(100, 100);
jpCenter.setBackground(Color.white);
add(jpCenter, BorderLayout.CENTER);
}
The code for the panel of the chatbox
public void east() {
// CREATE EAST Panel
gl = new GridLayout(4, 1);
jpEast = new JPanel();
jpEast.setSize(200, 200);
jpEast.setLayout(gl);
jpEast.setBackground(Color.white);
label = new JLabel("Number of shapes: ");
jpEast.add(label);
// ADD TEXT FIELD
jtf = new JTextField();
jtf.setText("");
jtf.setSize(200, 200);
jpEast.add(jtf);
// ADD BUTTON
jbSend = new JButton("Send");
jbSend.setEnabled(false);
jbSend.setSize(20, 60);
jpEast.add(jbSend);
jbSend.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
send(jtf.getText());
jtf.setText("");
}
});
// ADD TEXT AREA
jta = new JTextArea("");
jta.setSize(100, 100);
jpEast.add(jta);
// ADD EAST panel
add(jpEast, BorderLayout.EAST);
}
Avoid setting the size of components since it can make them not work well on all platforms, and with JTextArea in particular, it will not allow it to expand correctly if held within a JScrollPane (which is where a JTextArea belongs). Note also that most layout managers don't even respect a component's size but rather its preferred size.
Instead, set the row and column properties of your JTextAreas (done most easily via the JTextArea(int row, int column) constructor), the column property of your JTextField, the font sizes of other components (if need be). Then allow your container (JPanel) layout managers and component's own preferred sizes size all appropriately when you call pack() on your top-level window (often a JFrame), after adding all components but before setting it visible.
For more specific help, consider posting an image of the GUI you're getting vs. the one you're trying to achieve.
I read some answered questions in this forum (this one for example) where it is strictly recommended to avoid the use of setXXXSize() methods to resize components in swing applications.
So, coming to my problem, i would like to know how to best resize a JScrollPane in order to avoid its parent panel to increase its size without any control.
Before writing some code, i want to describe the real situation, since i will post a "toy example".
In my JFrame i'm currently using a border layout for my content pane. At BorderLayout.CENTER there is a JPanel where i do some custom painting.
At BorderLayout.EAST there is a JPanel (say eastPanel) containing some components inside another panel (this panel will be added to eastPanel at BorderLayout.NORTH), and a JScrollPane which contains a JTable (added to eastPanel at BorderLayout.CENTER). This table will have a lot of rows.
Since i want eastPanel's height to be the same as centerPanel's height, i need some way to avoid the JScrollPane to increase its size in order to try to display as much rows as possible.
For now i wasn't be able to find another solution apart from calling setPreferredSize on the eastPanel containing the scrollpane, but i have to admit that i hate this kind of solution.
Sample Code
In this code sample i added some random labels at the north of eastPanel and inside the JScrollPane, since my purpose was to post a short sample of code.
However, the situation is very similar to the one i have described above.
I wasn't be able to solve my problem without using this "terrible" line of code :
eastPanel.setPreferredSize(new Dimension(eastPanel.getPreferredSize().width, centerPanel.getPreferredSize().height));
I would like to avoid a more complex layout for a simple situation like this. Am i missing something ? Also, is setting that empty border an acceptable way to set the size of the panel where i will do some custom painting?
Code :
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class Test
{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new TestFrame().setVisible(true);
}
catch(Exception exception) {
JOptionPane.showMessageDialog(null, "Fatal error while initialiing application", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
}
}
class TestFrame extends JFrame
{
public TestFrame() {
super("Test");
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel pane = new JPanel(new BorderLayout(20, 0));
pane.setBorder(new EmptyBorder(20, 20, 20, 20));
JPanel centerPanel = new JPanel();
centerPanel.setBackground(Color.WHITE);
centerPanel.setBorder(new EmptyBorder(400, 400, 0, 0));
// centerPanel.setPreferredSize(new Dimension(400, 400));
JPanel eastPanel = new JPanel(new BorderLayout(0, 20));
JPanel labelsContainer = new JPanel(new GridLayout(0, 1));
for(int i=0;i<7;i++) labelsContainer.add(new JLabel(String.valueOf(i)));
eastPanel.add(labelsContainer, BorderLayout.NORTH);
JPanel moreLabelsContainer = new JPanel(new GridLayout(0, 1));
for(int i=7;i<70;i++) moreLabelsContainer.add(new JLabel(String.valueOf(i)));
JScrollPane scroll = new JScrollPane(moreLabelsContainer, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
eastPanel.add(scroll, BorderLayout.CENTER);
eastPanel.setPreferredSize(new Dimension(eastPanel.getPreferredSize().width, centerPanel.getPreferredSize().height));
pane.add(centerPanel, BorderLayout.CENTER);
pane.add(eastPanel, BorderLayout.EAST);
setContentPane(pane);
pack();
setLocationRelativeTo(null);
}
}
Thanks for your help !
I am not aware of a layout manager that restricts the height of the panel based on the height of a specific component in the panel.
One way is to customize the behaviour of the parent panel that contains the two child components.
The code might be something like:
#Override
public Dimension getPreferredSize()
{
Dimension d = super.getPreferredSize();
BorderLayout layout = (BorderLayout)getLayout();
Component center = layout.getLayoutComponent(BorderLayout.CENTER);
int centerHeight = center.getPreferreidSize().height;
if (d.height > centerHeight)
d.height = centerHeight;
return d;
}
This approach will allow for dynamic calculation of the height based on the component in the center.
Another option is to write you own layout manager. Then you can control this type of logic from within the layout manager.
Also, is setting that empty border an acceptable way to set the size of the panel where i will do some custom painting?
I override the getPreferredSize() to return the appropriate dimension.
By using the EmptyBorder you lose the ability to add a true Border to the panel, so I wouldn't recommend it.
I am working on a little card game and I have been having some trouble: when I try to add dynamic components to my JLayeredPane it does not display them.
I have a custom component that represents a card and I want to display 2 of them in a layered fashion. For that I have the following class:
public class PairView extends JPanel {
private JLayeredPane layeredPane;
private CardView attackCard;
private CardView defenceCard;
private static Point origin = new Point(0, 0);
private static int offset = 10;
public PairView() {
}
public PairView(Card attackCard) {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(120, 170));
this.defenceCard = null;
this.attackCard = new CardView(attackCard);
this.attackCard.setOpaque(true);
this.attackCard.setForeground(Color.black);
this.attackCard.setBorder(BorderFactory.createLineBorder(Color.black));
this.attackCard.setBounds(origin.x, origin.y, 100, 150);
layeredPane.add(this.attackCard, 0);
this.origin.x += offset;
this.origin.y += offset;
}
public void addDefenceCard(Card defenceCard) throws DurakException {
if (this.defenceCard == null) {
this.defenceCard = new CardView(defenceCard);
this.defenceCard.setOpaque(true);
this.defenceCard.setForeground(Color.black);
this.defenceCard.setBorder(BorderFactory.createLineBorder(Color.black));
this.defenceCard.setBounds(origin.x, origin.y, 100, 150);
layeredPane.add(this.defenceCard, 1);
} else {
throw new DurakException("A defence Card is already present");
}
}
I tested this via the drag and drop interface in NetBeans and I have the following problem:
From what I understand, the default constructor is always called, so when I create 2 random CardView components and add them to the layered pane in the default constructor the parent JFrame display them just fine.
If I use the overwritten constructor or try to add another component (like the addDefenceCard method) it does not display the added component.
calling revalidate() or repaint() isn't doing anything.
How to get the components to show?
Your class extend JPanel, but you never add any components to the panel so there is nothing to display.
You need to add the JLayeredPane to the panel:
layeredPane = new JLayeredPane();
this.add( layeredPane );
I don't know if you need the set the layout to a BoxLayout. The default FlowLayout of the panel will respect the preferredSize of any component added to it.
this.attackCard.setOpaque(true);
this.attackCard.setForeground(Color.black);
this.attackCard.setBorder(BorderFactory.createLineBorder(Color.black));
You may want to consider setting these properties in the constructor of the CardView class. That way the default properties are set in one place and can easily be changed.
Instead of using a layered pane you may want to consider using the Overlap Layout. It was designed to support the requirement of overlapping components.
I am trying to use Java Swing to create a simple GUI in which I have a drawing pad and some buttons it all works fine until I add this code for the JTextField:
String text = "hello";
JTextArea textArea = new JTextArea(text);
textArea.setPreferredSize(new Dimension(500, 50));
textArea.setEditable(false);
Before adding this code the drawpad displays on the left of the screen followed by the buttons, when I add this only the drawpad is displayed unless I resize the frame in which case the buttons and text field reappear although the text field is hidden behind the drawpad slightly. Here is the full code:
public class testGUI extends Frame{
public static void main(String[] args) {
JFrame frame = new JFrame("Neural Networks");
frame.setSize(700, 300); //set the size of the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true); //make it visible
Container content = frame.getContentPane();
content.setLayout(new BorderLayout());
JPanel mainPanel = new JPanel();
final PadDraw drawPad = new PadDraw();
drawPad.setSize(100, 100);
content.add(drawPad);
JButton clearButton = new JButton("Clear");
clearButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
drawPad.clear();
}
});
JButton loadButton = new JButton("Load");
loadButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//Load something here
}
});
JButton testButton = new JButton("Test Draw Pad Image");
testButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//
}
});
JButton loadImage = new JButton("Test image from file");
loadImage.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//String filename = textField.getText();
}
});
String text = "hello";
JTextArea textArea = new JTextArea(text);
textArea.setPreferredSize(new Dimension(500, 50));
textArea.setEditable(false);
mainPanel.add(clearButton);
mainPanel.add(loadButton);
mainPanel.add(testButton);
mainPanel.add(loadImage);
mainPanel.add(textArea);
content.add(mainPanel);
}
}
You're adding the drawPad and the mainPanel to the content panel, which uses BorderLayout, without specifying any location. They thus end up both in the center position of the border layout, which is supposed to contain only one component.
See How to use BorderLayout in the Swing tutorial.
Also note that setting the preferred size is not something you should do. Instead, the preferred size is supposed to be automatically computed based on other sttings (the contained components, the number of rows and columns of a text area, etc.)
And a JTextArea should be enclosed into a JScrollPane to be good-looking and allow you to scroll.
JPanel mainPanel = new JPanel();
The default layout for a JPanel is a FlowLayout, so all the components flow on a single row. If there is not enough room on the row then the components wrap to the next row.
So when you add the JTextArea the flow is disturbed. The solution is to use a combination of layout managers to get your desired layout effect. Read the section from the Swing tutorial on Using Layout Managers for more information and examples.
JTextArea textArea = new JTextArea(text);
textArea.setPreferredSize(new Dimension(500, 50));
Also, you should NOT set the preferred size of the text area (or any Swing component for that matter). Instead you should do something like:
JTextArea textArea = new JTextArea(rows, columns);
and let the component determine its own preferred size. Also a text area is typically used with a JScrollPane and then you add the scroll pane to your panel:
JScrollPane scrollPane = new JScrollPane( textArea );
Edit:
Taking a second look at your code you have many more problems.
The point of using a layout manager is to have the layout manager set the size and location of the components. So your code should not have any logic related to the size/location of a component.
When you use the add(...) statement on a BorderLayout without a constraint, the component gets added to the CENTER. However only the last component added is managed by the BorderLayout. So only the "mainPanel" is given a size/location by the layout manager. That is why you need the setSize(...) statement on the drawPad to make the component visible. Although you now have the problem that two components are painted in the same space.
So to see the drawPad on the left you might want to use:
content.add(drawPad.BorderLayout.LINE_START);
However this still probably won't work because I'm guessing you are doing custom painting on the draw pad which means you will also need to override the getPreferredSize() method of the class so the layout manager can use the information to determine the size of the component. Read the section from the Swing tutorial on Custom Painting for more information and working examples.
Finally some other issues:.
The setVisible(...) statement should be invoked AFTER all the components have been added to the frame.
To follow Java standards, class names should start with an upper case character.
You should NOT be extending "Frame". There is no need to extend any class in your example.
Read the tutorial and download the demos for examples of better structured code.
I'm playing around with some swing guis and am trying to create a basic program. This program is going to have a tabbed pane with a varying amount of tabs depending on the size of an array. (My end goal is to have users change the amount of items in the array, therefore changing the amount of tabs).
Each tab is going to have the exact same components, text area, table and a few buttons and labels. What I would like to do is instead of coding these tabs individually and rewriting my code over and over what I want to do is create a class to put all my components into.
I am however kind of stumped. This is my class for creating the tabs:
public class LocaleTab {
public LocaleTab(){
JPanel tab = new JPanel();
JLabel label = new JLabel();
label.setPreferredSize(new Dimension(300, 300));
tab.add(label);
}
}
And here's my code that I'm trying to call with it:
LocaleTab tab1 = new LocaleTab();
JTabbedPane localesTabPane = new JTabbedPane();
localesTabPane.add(tab1);
I'm getting an error when I try and compile this. I'm thinking my methodology is probably completely wrong.
The method add(Component) in the type JTabbedPane is not applicable
for the arguments (LocaleTab)
One are that concerns me is when I try to use the data in the tables and text areas in each tab(event listeners is what I'll be using i think? I haven't gotten to that stage yet though!) how will I target the individual tabs components?
Change to:
public class LocaleTab extends JPanel {
public LocaleTab(){
JLabel label = new JLabel();
label.setPreferredSize(new Dimension(300, 300));
add(label);
}
}
Probably you are looking for something close to this:
public class LocaleTab {
private JPanel tab;
public LocaleTab() {
tab = new JPanel();
JLabel label = new JLabel();
label.setPreferredSize(new Dimension(300, 300));
tab.add(label);
}
public JPanel getTabPanel() {
return tab;
}
}
And use LocaleTab as shown below.
LocaleTab tab1 = new LocaleTab();
JTabbedPane localesTabPane = new JTabbedPane();
localesTabPane.add(tab1.getTabPanel());
Additionally to know about how JTabbedPane works please take a look here: How to Use Tabbed Panes