So im writing a small test program for fun replicating a inventory from games like minecraft and runescape. Basically a frame with another one inside it, and pictures of your items in it, and a scroll bar to scroll down through all the stuff you have in your inventory. The "Stuff" i would have in my inventory would be buttons added later on with their own functionality, so you can scroll through vertically and see all the "stuff." Right now i have some test buttons being added to deomsntrate the error. Basically i want the buttons to be 100,100 and for them to be in a row of 4, and go onto the next column. I though GridLayout would be the best choice, but it seems to add more rows after being added into a scrollpane. Well heres the code skimmed down:
public class inventory extends JFrame{
public static void main(String[] args){
new inventory();
}
JPanel mainInv = new JPanel();
JScrollPane sp;
public inventory(){
setSize(500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Toolkit tk = this.getToolkit();
setLocation(tk.getScreenSize().width/2-getWidth()/2, tk.getScreenSize().height/2-getHeight()/2);
setLayout(null);
mainInv.setSize(getWidth()-10, 1000);
mainInv.setBackground(Color.blue);
mainInv.setLayout(new GridLayout(8,4));
sp = new JScrollPane(mainInv, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
sp.setMaximumSize(new Dimension(400,400));
sp.setBounds(5, 5, 500-10, 500-130);
JButton[] testButs = new JButton[100];
for(int i = 0; i < 50; i++){
testButs[i] = new JButton("Test Button " + i);
testButs[i].setSize(100,100);
mainInv.add(testButs[i]);
}
add(sp);
setVisible(true);
}
}
With GridLayout the number of rows is the dominating factor.
If you have 8 rows and 4 columns that can only fit 48 buttons, if you try to add a 49th button it will create a 5th column not a 9th row.
You can solve your problem by setting up the GridLayout with more rows.
Related
I'm trying to make a GUI for that simulates elevators in a building (really to test threading/c-scan), but when generating the buttons for the elevator control panel and the buttons for each individual floor, I'm kind of stuck. I thinking about trying to generate a new pair of buttons for each floor, and generating a new control panel per elevator. Also there's the difficulty of having a variable amount of floors. Anyway my question what is this best way to go about doing this? Perhaps it's not necessary to generate new buttons for everything and just use one set and change what the actions do per floor/elevator? I'm asking because I'm not very familiar with GUIs. Thanks for the help
If all the elevators, and the control panel are the same, you can use a singular method and pass in the elevator or the control panel. CustomPanel extends JPanel and has a method foo.
public void createElevatorButtons(final CustomPanel panel) {
ArrayList<JButton> buttons = new ArrayList<>(); //arraylist of buttons we can keep track of
JPanel buttonPanel = new JPanel(); //the visible component
for(int i = 1; i <= numberOfFloors;i++) {
JButton button = new JButton(String.valueOf(i)); //creates buttons for floors 1 to max
buttons.add(button);
buttonPanel.add(button);
}
panel.add(buttonPanel);
//add the action listeners
for(JButton button : buttons) {
button.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent e) {
JButton floor = (JButton) e.getSource();
int floorNumber = Integer.parseInt(floor.getText());
panel.foo(floorNumber); //we tell the elevator/panel/whatever to do something, you will have to extend JPanel to do foo
}
});
}
}
In this case that the number of floors is variable you can create an array of buttons:
JButton[] buttons = new JButton[MAX_NUMBER_OF_FLOORS];
Then when you determine the exact of number of floors at runtime, you can go to instantiate and add the buttons:
for(int i=0; i<numberOfFloors; i++) {
buttons[i] = new JButton();
controlPanel.add(buttons[i]);
}
Something like this should work.
Assign MAX_NUMBER_OF_FLOORS a big number like 100, there should be a possible limit given by the problem.
What i want to achieve: I want to be able to add components to a scrollpane inbetween components already added. In other words, i somehow want to move the index up and plop some new components where the old ones used to be. I created a short program to illustrate my problem, it replace the components it doesnt add them. can someone help?
Why i want to achieve it (to answer the inevitable question related to this, this is long only read if interested): Ive got a scrollpane in which i need to display lots of components, approx 150000 JTables. This takes too much memory and processing time. So what ive done is to load the scrollpane with 150000 TextFields and only displaying 100 of the tables at a time. If you scroll to textfield 200 it loads the next 100 tables, replacing the previously created text fields with the tables. If you move to textfield 300 it loads the next 100 tables, and removes the first 100, again replacing it with textfields. My thinking here is that the textfields are going to be much less resource intensive than the tables, and so far this thinking seems to be correct. So this all seems to work pretty well (on my smaller test file - about 5000 records). The problem is that to create the 150000 textfields also takes lots of time. I thought of a solution for this. To not create a textfield for every table, but to rather create 1 textfield for every 100 tables. The idea is that when you scroll to textfield 2 (instead of 200), it loads the next 100 tables, replacing textfield nr 2 with tables nr 200 - 299. So somehow i need to move textfield 3 and all those below it down in order to insert all the tables. Phew, hope this makes sense
So looking at the example below what i effectively want to achieve is to replace say textfield 20 with 5 new textfields, but not repalce textfields 21 - 25.
import javax.swing.*;
import java.awt.*;
public class Example {
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ExampleScreen ex = null;
ex = new ExampleScreen();
ex.setVisible(true);
}
});
}
private static class ExampleScreen extends JFrame{
GridBagConstraints gc = new GridBagConstraints();
JPanel panel = new JPanel(new GridBagLayout());
JScrollPane scrollPane = new JScrollPane(panel);
public ExampleScreen() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
this.add(scrollPane, BorderLayout.CENTER);
setupFields();
createExtraFieldsInbetween(20);
this.pack();
scrollPane.getVerticalScrollBar().setUnitIncrement(16);
scrollPane.getViewport().setScrollMode(JViewport.BLIT_SCROLL_MODE);
}
//after this happens i need to add components somewhere in the middle, like after textfield 20 for example
private void setupFields() {
gc.gridy = 0;
gc.gridx = 0;
for(int k = 0; k < 50; k++) {
JTextField textField = new JTextField();
textField.setPreferredSize(new Dimension(1500,36));
textField.setText(new Integer(k).toString());
panel.add(textField, gc, k);
gc.gridy++;
}
}
//this is to create extra fields inbetween those already created
//this does not work, it overwrites the existing components, doesnt move them up
private void createExtraFieldsInbetween(int i) {
gc.gridy = i;
//create 5 extra
for(int k = i; k < i + 5; k++) {
JTextField textField = new JTextField();
textField.setPreferredSize(new Dimension(1500,36));
textField.setText("Extra Field : " + new Integer(k).toString());
panel.add(textField, gc, k);
gc.gridy++;
}
}
}
}
Anytime I see a number like 150,000 I think the user can't be bothered to scroll through all those components so you need a better UI.
Anyway if you have a problem with a GridBagLayout, then don't use a GridBagLayout.
It looks to me like you are just displaying the components vertically so try a BoxLayout. Then you can just use the add(...) method to specify the index where you want to remove/add components.
I want to know how to nest JPanels using a GridLayout. This is how it should look like.
I approached this problem by 2 ways so far,
using JPanels and
using JLabels,
and none of them worked (only the first panel created is shown).
Here is the code for the JPanel approach:
int x=20, y=20;
JPanel [] panels = new JPanel[3];
JLabel animal = new JLabel(new ImageIcon(getClass().getResource("Pictures/animal.gif")));
JLabel map = new JLabel(new ImageIcon(getClass().getResource("Pictures/map.gif")));
JLabel mountain = new JLabel(new ImageIcon(getClass().getResource("Pictures/mountain.gif")));
for(int i=0;i<panels.length;i++)
{
if(i>0)
{
x+=x;
y+=y;
}
panels[i] = new JPanel(new GridLayout(2,2));
panels[i].setPreferredSize(new Dimension(x,y));
if(i==0)
panels[i].add(new JPanel());
else
panels[i].add(panels[i-1]);
panels[i].add(mountain);
panels[i].add(map);
panels[i].add(animal);
}
add(panels[2]);
One option is to create a class that will represent a panel divided into the grid with the images. The only issue would be the top left quadrant which would usually contain the nested panel, at some point you want this to contain just a blank panel. So maybe something like this (barring various optimizations):
class GridPanel extends JPanel{
JLabel mountain, map, animal;
public GridPanel(JPanel panel){
super();
setLayout(new GridLayout(2, 2));
animal = new JLabel(new ImageIcon(getClass().getResource("pictures/animal.gif")));
map = new JLabel(new ImageIcon(getClass().getResource("pictures/map.gif")));
mountain = new JLabel(new ImageIcon(getClass().getResource("pictures/mountain.gif")));
add(panel);
add(mountain);
add(map);
add(animal);
}
}
Notice that it accepts the panel that is to be displayed in the top left corner of the grid. This coud then be called with the panel specified. So at the point where you want to create the main panel:
JPanel grid = new GridPanel(new JPanel()); //initial
for(int i = 1; i <= 5; i++){
grid = new GridPanel(grid);
}
add(grid);
The initial grid is created with a blank JPanel. And every subsequent grid will contain the previous one as the top left panel. You have to resize your images and such and maybe even avoid loading the images multiple times etc. But that is another question. This example shows 5 nested panels.
Just to be clear, you should use ImageIO to load the images once and reuse the images. For example You can create a BufferedImage like this in your main class:
BufferedImage mointainImg = ImageIO.read(new File("pictures/mountain.gif"));
And when you want to create the JLabel you can do this:
mountain = new JLabel(new ImageIcon(mountainImg));
And the advantage is that you can manipulate the image a bit if you want.
One issue that you have is that the images are not scaled. To scale images, use Image.getScaledInstance(). Proper scaling would at least fix the problem of the visible images being cut off. It also might cause the other images to be shown as they might just be hiding behind the visible images because they are too big.
I have to do make a program that has to be able to show a matrix in form of Jtextfields or Jtextareas, so that the user can write in them(a number of the matrix for each Jtextfield).
The problem is that I have no idea how to create a custom JPanel with the size and quantity of Jtextfields that the user specifies(a different each time).
I have already googled the question, to no avail.
Take a look at GridLayout. It is pretty simple to put together a grid together with a couple params (row and col count). To paraphrase from the JavaDoc:
public static void main (String[] args) {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(3,2));
panel.add(new JTextField("1"));
panel.add(new JTextField("2"));
panel.add(new JTextField("3"));
panel.add(new JTextField("4"));
panel.add(new JTextField("5"));
panel.add(new JTextField("6"));
would result in a 3 row by 2 col grid of JTextFields
EDIT:
here is some more, in a class named Demo:
public class Demo {
public static void main(final String[] args) {
if (args.length < 2) {
System.out.print("please enter row and col on commandline");
}
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new Demo(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
}
});
}
public Demo(int colCnt, int rowCnt) {
JFrame frame = new JFrame("Demo");
frame.setSize(600, 600);
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(colCnt,rowCnt));
for (int i =0; i < rowCnt*colCnt; i++) {
panel.add(new JTextField(""+i));
}
frame.setContentPane(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
First of all, I would build a 2d array of the text fields\areas. When you get the user input you can then initialize the arrays and "new up" all of the widgets. After adding all of them to the parent panel\frame you may have to do some calculation based on the used up size and resize your top level window. Aside from that, as already suggested, GridLayout will be a good choice for the direct parent component.
Hope this helps.
To change the appearance at runtime, all you need to do is replace the content with new components.
So depending on how you get your input, you replace the numbers given in akf's answer with the input.
panel.setLayout(new GridLayout( rows, columns));
and then to show the new panel you add it in the JFrame with
add( panel );
Your assignment however might be something along the lines of subclassing a JPanel to show a matrix. I get that impression in your wording. Then its a whole different solution.
If your only goal is to implement a matrix where you can edit values a JTable is by far the simplest. Put the following in a JFrame
DefaultTableModel data = new DefaultTableModel(3 , 3);// rows, cols
JTable table = new JTable(data);
add(table);
pack();
setVisible(true);
This would also simplify modifying the height and width of the matrix by using data.setRowCount( newValue ).
I have task to prepare two windows with swing. One contains grid of squares, with random numbers in them. In second I need to load pieces of tiled image and then show them in the correct order, forming tiled image.
Windows should look like this :
alt text http://img535.imageshack.us/img535/3129/lab8a.jpg
Okay so how to bite this? I've used swing only few times to draw some 2d polylines, so basically I just theoretically now what to do.
Ok, so window number 1:
I start with creating Jframe for the window. Then I do for loop and in it create 16 JLabels with random numbers in them? How to set margins between each tile and the whole window?
Window number 2:
So I start the same, but instead of loading numbers I add images? Now, how can I load image from file and then set it as background?
The following code lays out the JLabels using the GridLayout. The arguments to the GridLayout are the following: rows, cols, horizontal gap, vertical gap. In the example below I have 3 pixels wide gap between labels both vertically and horizontally.
To use images instead of numbers, you could pass an ImageIcon to the constructor of the JLabel instead of the text.
However, it looks like your doing a game where the user should be able to click on the tiles. This suggests that you perhaps should use buttons instead of labels, but it's up to you :-)
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.border.BevelBorder;
public class FrameTest {
public static void main(String[] args) {
final JFrame f = new JFrame("Frame Test");
JPanel panel = new JPanel(new GridLayout(4, 4, 3, 3));
for (int i = 0; i < 16; i++) {
JLabel l = new JLabel("" + i, JLabel.CENTER);
//JLabel l = new JLabel(new ImageIcon("image_file.png"), JLabel.CENTER);
l.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
l.setFont(l.getFont().deriveFont(20f));
panel.add(l);
}
f.setContentPane(panel);
f.setSize(200, 200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}