Java GridBayLayout replacing components - java

I am trying to delete multiple components in a JPanel using a GridBagLayout
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
GamePanel gamePanel = new GamePanel(frame.getContentPane());
gamePanel.delBlock(0, 0);
gamePanel.delBlock(0, 1);
frame.setContentPane(gamePanel);
}
});
This is is the method for deleting a block
public void delBlock(int x, int y){
int location = x * row + y;
this.remove(location);
this.revalidate();
this.repaint();
}
As you can see the 2 blocks should be next to one another but this is what i get as a result.

gamePanel.delBlock(0, 0);
gamePanel.delBlock(0, 1);
First you remove component at location 0. Then all the components shift 1 position in the Container.
Then you remove component at location 1. However, this the component that was at location 2, before you removed the first component.
Try:
gamePanel.delBlock(0, 1);
gamePanel.delBlock(0, 0);
to reverse the order in which you remove the components.
That is always remove components from the end of the container first.
As you can see the 2 blocks should be next to one another
I can't tell if you mean "next to" one another in a vertical sense or horizontal sense.
Given that the two components have been removed from the first column, it appears that you are building your grid in column order. That is you add all the components for column 1 and then column 2 and then column 3 etc.
If you want your components to be "beside" one another (in a horizontal sense) then you need to add the components in row order.

Related

Java - textField for loop not completing

I've been trying to make a 9x9 grid of textFields, where each textField is allocated to an element in a 2d array. For example the top left textField is field[0][0], the one right of that is field[1][0], and the bottom right textField is field[8][8].
So far I have
TextField[][] fields = new TextField[9][9]; {
for (Y=0;Y<9;Y++) {
XPosition=0;
for (X=0;X<9;X++) {
fields[X][Y] = new TextField(1);
fields[X][Y].setColumns(1);
fields[X][Y].setBounds(XPosition, YPosition ,32, 32);
frame.getContentPane().add(fields[X][Y]);
XPosition=XPosition+32;
}
YPosition = YPosition+32;
}
}
For some reason when I run the program, only the first 5 textFields in the top row get created. I have a feeling all the other textFields are placed under the visible ones. The frame is definitely big enough, and I can't figure out any issues with the code.
Use a grid layout : https://docs.oracle.com/javase/tutorial/uiswing/layout/grid.html
Absolute positioning is not reliable.
Adding
frame.getContentPane().setLayout(null);
has fixed the error

Empty JTabbedPane

I'm having an issue creating an empty JTabbedPane where the only portion to be seen on the GUI are the row of tabs.
Everytime I add a new tab with an "empty" component, the height of the JTabbedPane increases, but why?
The current workaround is to override getPreferredSize(), but it seems kludgy to me. Comment out the overridden method to see what I mean.
Am I missing something obvious?
Background:
We need a JTabbedPane where the tabbed pane starts off with 2 tabs, but the user can add more tabs as needed, up to 10. In addition, each tab contains the same components, but with different data. The decision was made to fake the look of a JTabbedPane, by implementing an empty JTabbedPane solely for the look, and to use a single fixed JPanel whose contents will be refreshed based on the tab clicked.
(Normally, I could just recreate the JPanel n-times, but that would nightmarish for the presenter classes who control the UI, which is beyond the scope of my question.)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CustomTabbedPane implements Runnable
{
static final int MAX_TABS = 11; // includes the "add" tab
JPanel pnlTabs;
JTabbedPane tabbedPane;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new CustomTabbedPane());
}
public void run()
{
JPanel p = buildPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(p);
frame.setSize(800,400);
frame.setVisible(true);
}
private JPanel buildPanel()
{
tabbedPane = new JTabbedPane()
{
#Override
public Dimension getPreferredSize()
{
Dimension dim = super.getPreferredSize();
dim.height = getUI().getTabBounds(this, 0).height + 1;
return dim;
}
};
tabbedPane.addTab("Tab 1", getEmptyComp());
tabbedPane.addTab("Tab 2", getEmptyComp());
tabbedPane.addTab("+", new TabCreator());
tabbedPane.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
addTab();
}
});
JScrollPane scroll = new JScrollPane(new JTable(5,10));
JPanel p = new JPanel(new BorderLayout());
p.add(tabbedPane, BorderLayout.NORTH);
p.add(scroll, BorderLayout.CENTER);
p.setBorder(BorderFactory.createLineBorder(Color.BLUE.darker(), 1));
return p;
}
private void addTab()
{
if (tabbedPane.getSelectedComponent() instanceof TabCreator)
{
int selIndex = tabbedPane.getSelectedIndex();
if (tabbedPane.getComponentCount() < MAX_TABS)
{
if (selIndex == tabbedPane.getComponentCount()-1)
{
String title = "Tab " + (selIndex + 1);
tabbedPane.insertTab(title, null, getEmptyComp(), "", selIndex);
tabbedPane.setSelectedIndex(selIndex);
if (tabbedPane.getComponentCount() == MAX_TABS)
{
tabbedPane.setEnabledAt(MAX_TABS-1, false);
}
}
}
}
}
private Component getEmptyComp()
{
return Box.createVerticalStrut(1);
}
class TabCreator extends JLabel {}
}
Great question! But it's fairly straightforward to get a hint on what's happening.
The problem is that your content does not have a minimum width, preferred size is not set, tab placement is top/bottom and the UI is default.
Since preferred size is not set, then when the layout is revalidated the calculations of space required go into the BasicTabbedPaneUI method Dimension calculateSize(false).
That reads:
int height = 0;
int width = 0;
<other vars>
// Determine minimum size required to display largest
// child in each dimension
<actual method>
Here it calculates the minimum size to accommodate any child and stores it into height/width. In your case this yields something like 10,10 (because of the single Label tab creator I think, I didn't follow that one).
Then happens the magic:
switch(tabPlacement) {
case LEFT:
case RIGHT:
height = Math.max(height, calculateMaxTabHeight(tabPlacement));
tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
width += tabExtent;
break;
case TOP:
case BOTTOM:
default:
width = Math.max(width, calculateMaxTabWidth(tabPlacement));
tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
height += tabExtent;
}
What happens here is it sets the preferred width to be the maximum of the largest tab width and the largest child width. In your case it's around 44 for the tab text. The tabExtent is then calculated to see just how many rows of tabs are needed to support this preferred width. In your case - it's 1 extra row of tabs for each tab. That's where the extra height in preferredSize().height comes from. Essentially because for horizontal tab placement it cares about width first, then height.
How to fix:
Set a preferred size :) I know a lot of people say don't set the preferred size, but in this case this will just work. Since a preferred size is set (via actually setting it, not overriding getPreferredSize()), the code will never get to counting tabs.
Give at least one of your children a size (via setPreferredSize or overriding getPreferredSize). If one of the childrens width is that of the frame, or, say, the table at the bottom the TabbedPane will not be allocating an extra row for each tab, since a single row will fit everything.
Make your own UI for the tabbed pane. It may be easier to make your own tabbed pane though really, I've never done this.
EDIT:
After thinking about this a bit more, I realized that solution number 1 AND your own solution suffer from the flaw that, if the tabbed pane actually does require multiple rows for the tabs (hello frame resizes), bad things will happen. Don't use it.

JButton and its Location methods acting very weird

my problem is, that when I create a JButton, whithin its constructor, I set its location to some relative coordinates, say x = 5, and y = 6, using the following code:
this.setLocation(new Point(x, y));
but after I am trying to get its location using the getLocation() method, it always returns 0,0. Please note that this happens for every JButton I am trying to place on a grid layout powered JFrame, and during the debugging process, I have also noted that their location is being properly instantiated.
Can someone explain to me why this happens, and if I can fix it somehow?
EDIT:
The constructor (The brick class that I made, extends JButton):
public Brick(int posx, int posy) {
this.setLocation(new Point(posx, posy));
this.setVisible(true);
}
I make about 100+ of them in 2 for loops:
for (int row = 0; row < 15; row++) {
for (int column = 0; column < 15; column++) {
Brick brickie = new Brick(row, column);
}
}
But afterwards, if I wanna pick a brick and check its location like this:
Point brickLocation = brickie.getLocation();
both brickLocation.x == 0 and brickLocation.y == 0
You are trying to change button location when it is automatically assigned by layout manager (your GridLayout). That is why you are always getting the same value back - layout just overwrites it.
To be able to change any component bounds/location (including buttons) manually - you have to set "null" as the container's layout. After that just change the location/size/bounds as you like and it will affect the components positions.
Also you don't need to use "setVisible(true)" - by default that flag is true for all components (even those that aren't displayed yet).
JComponent(s) can returns its coordinates, getBounds or getLocation only
if container is already visible on the screen
after pack()

How to lock JSplitPane divider when swapping components?

I have a simple extended JSplitPane that I set different panels to at different times when they are needed. Specifically, I split it into an upper and lower section, and I swap out the bottom section frequently. Each time I do, I reset the slider position to how I want it, but sometimes it jumps off at and re-positions itself to the top of the screen (not always).
Here's my code:
public class MainPanel extends JSplitPane{
public Screen screen;
public int height;
public ControlPanel curPanel;
public MainPanel(Screen screen, int height){
super(JSplitPane.VERTICAL_SPLIT);
this.screen = screen;
this.height = height;
setDividerSize(2);
setEnabled(false);
setTopComponent(screen);
setToInitControls();
}
public void setToInitControls(){
InitControls initCtrls = new InitControls(this);
setBottomComponent(initCtrls);
curPanel = initCtrls;
setDividerLocation(height / 4 * 3);
}
public void setToConfigControls(){
ConfigControls configCtrls = new ConfigControls(this);
setBottomComponent(configCtrls);
curPanel = configCtrls;
setDividerLocation(height / 4 * 3);
}
public void setToWaitControls(){
WaitControls waitCtrls = new WaitControls(this);
setBottomComponent(null);
setBottomComponent(waitCtrls);
curPanel = waitCtrls;
setDividerLocation(height / 4 * 3);
}
//and so on (I have more methods like these further down)
//OVERRIDES: I figured overriding these might help. It didn't.
#Override
public int getMinimumDividerLocation(){
return (height / 4 * 3);
}
#Override
public int getMaximumDividerLocation(){
return (height / 4 * 3);
}
}
Basically, I use the "setTo...Controls()" methods to swap bottom panels. Is there a way to tell the slider to stay put where I placed it regardless of the panel's preferred sizes, or if not, how do I make the panels know what to shape themselves to fit in? Thanks for any/all suggestions!
EDIT: I should note that these panels do not use layouts. They are custom panels that I use mouse/keyboard listeners on and use my own graphics to paint over them.
I found the solution, thanks to the links above. It's actually quite simple. Instead of using
setDividerLocation(height / 4 * 3);
for every time I added a component, I just replaced it with:
setResizeWeight(0.66);
Did that once inside the constructor, and it never bothered me again. 0.66 is the equivalent decimal position to h/4*3 (I just trial-and-errored it).

In Java, is there a way to obtain the component where an event is being handled?

Suppose I have 4 squares colored blue, white, red and green (myComponent) associated with the mouse press event. At one point, the mouse is pressed over one of them - say, the yellow one - and the event is activated.
Now, the control flux is inside the event handling function. How do I get the MyComponent - the yellow square - that caused this from here?
EDIT
I have another question. Is there a way to tell the position of the component? My problem is a bit more complicated than what I said.
Basically, I have a grid full of squares. When I click one of the squares, I have to know which one it is, so I can update my matrix. The thing is, if I calculate it myself, it only works on a given resolution.
I have a GridBagLayout, and inside it are the myComponents. I have to know which one of the components exactly - like, component[2][2] - caused the interruption.
I mean, I can tell which one of the components did it, but not where in the matrix it is located.
MouseEvent.getSource() returns the object on which the event initially occurred.
I have a GridBagLayout, and inside it
are the myComponents. I have to know
which one of the components exactly -
like, component[2][2] - caused the
interruption.
You could store the indices, e.g. (2,2), inside each myComponent when you add them to the matrix. That way, given the component, you can always identify its position in the matrix.
class MyComponent extends JButton
{
final int i; // matrix row
final int j; // matrix col
// constructor
MyComponent(String text, int i, int j)
{
super(text);
this.i = i;
this.j = j;
}
...
}
By adding a MouseListener (or alternatively, a MouseAdapter, if you don't need to override all the MouseListener' methods) to each of your colored boxes, when an event such as a mouse click occurs, theMouseListenerwill be called with a [MouseEvent`]3, which can be used to obtain the component that was clicked.
For example:
final MyBoxComponent blueBox = // ... Initialize blue box
final MyBoxComponent whiteBox = // ... Initialize white box
final MyBoxComponent redBox = // ... Initialize red box
final MyBoxComponent greenBox = // ... Initialize green box
MouseListener myListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e)
{
// Obtain the Object which caused the event.
Object source = e.getSource();
if (source == blueBox)
{
System.out.println("Blue box clicked");
}
else if (source == whiteBox)
{
System.out.println("White box clicked");
}
// ... and so on.
}
};
blueBox.addMouseListener(myListener);
whiteBox.addMouseListener(myListener);
redBox.addMouseListener(myListener);
greenBox.addMouseListener(myListener);

Categories

Resources