I know there are many questions on here already about positioning components using absolute layout, but they don't seem to answer my questions.
I'm working on creating a Solitaire game in java to better learn GUI components. I seem to understand the various ActionListener classes I'm using, but I'm having trouble positioning components where I want them in the window.
I'm trying to set up the window in a format that resembles the basic solitaire layout (deck, discard pile, 4 suit stacks at the top and 7 solitaire stacks below them). My thought process was that I'd need to use an absolute layout in my JFrame component to manually place the different stack elements where they should go (maybe this isn't the best approach?). In doing so, I've tried using setLocation(x, y), setLocation(Point), setBounds(x, y, width, height), etc and nothing seems to work. I just get a blank window.
Here's an example of my code trying to manually place components in the window:
public class SolitaireTable extends JFrame {
public static final int SUIT_STACK_CNT = 4;
public static final int SOL_STACK_CNT = 7;
public static final Point DECK_POS = new Point(5,5);
public static final Point DISCARD_POS = new Point(73+10, 5); //73 is width of card images and 10 gives it a 5 px border to left and right
public static final Point SUIT_STACK_POS = new Point(DISCARD_POS.x + 73 + 92, 5);
public static final Point SOL_STACK_POS = new Point(DECK_POS.x, DECK_POS.y + 97 + 5); //97 is heigh of card image. 5 gives it a border of 5
public SolitaireTable()
{
setLayout(null);
ImageIcon cardImg = new ImageIcon("images/2c.gif");
Card card1 = new Card(cardImg);
add(card1);
card1.setBounds(50, 50, card1.getWidth(), card1.getHeight());
}
public static void main(String[] args)
{
SolitaireTable table = new SolitaireTable();
table.setTitle("Solitaire");
table.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
table.setVisible(true);
table.setSize(800, 600);
}
}
public class Card extends JComponent {
private ImageIcon img;//current image displayed (either front or back of card)
private Point coords;
private String suit;
private int face; //use ints instead of strings to make descending face pattern calculations easier. 0 = ace, 1= 2, 10 = j, 12 = k, etc
private String color;
private boolean revealed; //whether see face or back of card
private ImageIcon frontImage; //image of card's face side (set in constructor)
public Card(ImageIcon img){
this.img = img;
}
public void moveTo(Point p) {
coords = p;
}
//================================================================= getWidth
public int getWidth() {
return img.getIconWidth();
}
//================================================================ getHeight
public int getHeight() {
return img.getIconHeight();
}
}
I've been scouring the internet for days trying to figure out how to position components in an absolute layout but haven't found much. Any help would be greatly appreciated. Thanks.
You create your Card class with an ImageIcon, but you never paint the Icon anywhere so there is nothing to paint and you get an empty screen.
You need to add painting code to your class. See the section from the Swing tutorial on Custom Painting for more information and working examples.
Or maybe you create a JLabel and add the Icon to the label and then add the label to the component. (Don't forget to set the layout manager of your card component if you use this approach).
Or you could just create a JLabel with the Icon. Then you would extend JLabel, instead of JComponent to add your custom methods.
Are you using Absolute layout ?
Absolute Layout is DEPRECATED ..... Have a look
here
From docs: Absolute layouts are less flexible and harder to maintain than other types of layouts without absolute positioning.
However, if it fits your special purpose, just use it.
You can use RelativeLayouts as described here:
Set the absolute position of a view
Related
I'm currently coding a java project and I need a JPanel named board to contain some Tiles that are themselves JPanel, I add them to a layout of appropriate size, but when I add everything with buttons in a grouplayout in a JFrame, only the first Tile is at an appropriate size, and others have ridiculous size like being 1p width and 50p height or 1*1p.
I don't feel like I miss anything, everything is added in the JFrame and added to correct layout, with correct number of rows and lines, and sizes are set to be 50*50p in the Tile class. Here are some code snippets containing the graphic settings, especially the constructors:
Tile.java :
class Case extends JPanel
{
private int _color;
private boolean _star;
private Case _casePere;
private ArrayList<Case> _fils;
private int _x,_y;
public Case(int x, int y)
{
_color = 0;
_star = false;
_casePere = null;
_fils = new ArrayList<Case>();
_x = x;
_y = y;
setPreferredSize(new Dimension(50,50));
setMinimumSize(new Dimension(50,50));
setBackground(Color.WHITE);
}
Board.java :
class Board extends JPanel{
private Case[][] _grid;
private Case[] _starsp1;
private Case[] _starsp2;
// constructeur
public Board(int nbStars, int length){
_grid = new Case[length][length];
_starsp1 = new Case[nbStars];
_starsp2 = new Case[nbStars];
//graphisme
Dimension d = new Dimension(50*length, length*50);
setBackground(Color.BLACK);
setPreferredSize(d);
GridLayout layout = new GridLayout(Constante.length, Constante.length,2 ,2);
setLayout(layout);
for(int y=0; y<length; ++y)
{
for(int x=0; x<length; ++x)
{
_grid[x][y] = new Case(x,y);
add(_grid[x][y]);
}
}
if you want, I can add some snippets of the Window class, but beside the Board, other components don't hav any issues, and it's mainly adding and grouping some components. Here's a screenshot of the output so you can see how the Board's drawing behaves
Edit : I was overriding getX and getY in my Case class overriding JPanel's one, kinda dumb issue again, thanks for the answers
setPreferredSize(d);
Don't set the preferred size of the board. The layout manager of the board will determine the preferred size based on the number of components added to the grid and the size of the largest component added.
Note, (based on the line below) you want a spacing of 2 pixels between each component which your calculation doesn't include. So let the layout manager do its job.
GridLayout layout = new GridLayout(Constante.length, Constante.length,2 ,2);
We don't know what Constante.length is. You pass in a length variable to your class so use that variable.
Also, why does your Case class have so many instance variables? Those variables are never used in the posted code. So maybe you have other methods that are causing problems with the layout. For example don't override getX() or getY() those methods are used by the layout manager.
I have seen this thread which asked the exact same question I have now, but find the answers a bit unsatisfactory:
Android's LinearLayout for Swing
I created a class WeightedPanel like so:
public class WeightedPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 6844740568601141924L;
private boolean mVertical;
private double mLastWeight = 1;
private GridBagConstraints mConstraints;
private int mLastGrid = 0;
public WeightedPanel(boolean vertical) {
mVertical = vertical;
mConstraints = new GridBagConstraints();
setLayout(new GridBagLayout());
}
#Override
public Component add(Component comp) {
return add(comp, mLastWeight);
}
public Component add(Component comp, double weight) {
if (mVertical) {
mConstraints.weighty = weight;
mConstraints.weightx = 1;
mConstraints.gridy = mLastGrid;
mConstraints.gridx = 0;
} else {
mConstraints.weightx = weight;
mConstraints.weighty = 1;
mConstraints.gridx = mLastGrid;
mConstraints.gridy = 0;
}
mConstraints.fill = GridBagConstraints.BOTH;
add(comp, mConstraints);
mLastWeight = weight;
mLastGrid += weight;
return comp;
}
public Component add(Component comp, int weight) {
return add(comp, (double) weight);
}
}
This kind of works, but I have two problems with it:
1) In my application, I have a login screen:
#Override
protected void addComponents(WeightedPanel jPanel) {
mUpdateListener = new UpdateListener() {
#Override
public void onUpdate() {
LoginFrame.this.onUpdate();
}
};
WeightedPanel panel = getUserPanel();
jPanel.add(panel);
panel = getPasswordPanel();
jPanel.add(panel);
mLoginButton = getLoginButton();
jPanel.add(mLoginButton);
}
private WeightedPanel getPasswordPanel() {
WeightedPanel result = new WeightedPanel(false);
JLabel label = new JLabel("Password");
result.add(label);
mPasswordField = new PasswordField(mUpdateListener);
result.add(mPasswordField);
return result;
}
private WeightedPanel getUserPanel() {
WeightedPanel result = new WeightedPanel(false);
JLabel label = new JLabel("User");
result.add(label);
mUserTextField = new TextField(mUpdateListener);
result.add(mUserTextField);
return result;
}
which in practice looks like this:
Click to view
Why aren't the labels and text fields all the same size here? I figure it's got something to do with the fact that "Password" is a longer string than "User", but that's obviously not what I want!
2) My second problem is this. I have another screen like so:
#Override
protected void addComponents(WeightedPanel jPanel) {
WeightedPanel scrollPanePanel = getOrdersScrollPane();
jPanel.add(scrollPanePanel);
WeightedPanel buttonPanel = getButtonPanel();
jPanel.add(buttonPanel);
}
private WeightedPanel getOrdersScrollPane() {
WeightedPanel result = new WeightedPanel(true);
JPanel filterPanel = getFilterPanel();
result.add(filterPanel, 1);
mTableModel = new OrdersTableModel();
mTable = new JTable(mTableModel);
mTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent arg0) {
checkEnabled();
}
});
JScrollPane scrollPane = new JScrollPane(mTable);
result.add(scrollPane, 40);
return result;
}
It really doesn't look bad in practice:
Click to view
But have a look at the getOrdersScrollPane() function. The call to functions result.add(filterPanel, 1); and result.add(scrollPane, 50); say that the proportion between the filter panel and the scroll pane should be 1:50, but looking at the scroll pane, it's definitely not 50 times the size of the filter panel. Obviously, I am exaggerating to make my point, I don't really want a proportion of 1:50; it just strikes me that it makes no difference whether I do result.add(scrollPane, 10); or result.add(scrollPane, 50);
Both questions stem from an incorrect understanding of GridBagLayout. A bit more reading and experimenting should help) To answer the question at hand:
1) The problem here is that you want a single GridBagLayout, but instead are adding 2 independent panels.
The result: The columns in the top grid bag are independent of the columns in the bottom grid bag.
To rectify this, there are 2 things you can try:
Add both labels and both text fields to a single GridBag panel. That way the columns will align.
Make a minimum and preferred size for the labels so that their width matches and set their weightx to 0 (and weightx of text fields non-zero). That way you are making the GridBags allocate the same amount of space for the labels and text fields.
The first method is preferred, but not always possible. The second method is hacky and will likely break as soon as you change the label string, a user set a different default font etc, etc.
2) Here you are misunderstanding what weighty does.
It does not make your components of the specified proportion. That should be clear enough since you can mix 0 and non-0 weight components in a single layout.
What it does, is it allocates the preferred (or minimum) sizes for components, and distributes the remaining space in that proportion. Which means if you make your panel 100 pixels higher by resizing the window, 2 will go to the top panel adding spacing, and 98 will go to the table.
What you likely wanted is to make the weighty of the top filter 0 (so that there is no awkward spacings in large windows) and control its actual height with setPreferred and setMinimum size (or by setting those on the embedded components).
EDIT
As docs for Linear Layout state, to achieve a fixed proportion of sizes of components (the initial problem), one has to set their preferred sizes to 0, and then set weights (then all space is remaining space, and is distributed according to weights only). This also works for the GridBag variant.
I have created a GUI via Netbeans Java but whenever I maximize the GUI window the text boxes become misaligned. I used the Netbeans drag and drop function to create the GUI. I was wondering why the text boxes become misaligned whenever I maximize the GUI
It's an issue with the layout.
Read up on using layouts: http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
If you are going to use Netbean's GUI builder, run through a tutorial. It'll help you better understand how the builder is to be used. https://netbeans.org/kb/docs/java/quickstart-gui.html
I Understood your problem because I already had the same problem, My advice to you is that you can use the drag and drop of netbeans but before using it first make a panel and in that panel drop all your stuff then using the JFrame Component re sized function just put the panel wherever you want to put it for e.g if you want to center align it.
//if your class is extending JFrame
public static int getWIDTH(){
return WIDTH;
}
public static int getHEIGHT() {
return HEIGHT;
}
private void formComponentResized(java.awt.event.ComponentEvent evt) {
// this is for setting panel in the middle of the JFrame horizontally
int a = getWIDTH();
int b = Panel.getWidth();
a = a/2;
b = b/2;
int centerForX = b - a;
// This is for setting the panel in the middle vertically
int x = getHEIGHT();
int y = Panel.getHeight();
x = x/2;
y = y/2;
int centerForY = x - y;
// Making a 'Point' object and then setting location of the Panel.
Point p = new Point(centerForX , centerForY);
Panel.setLocation(p);
}
Some Keypoints:
1) getWIDTH and getHEIGHT methods will be generated by netbeans write outside all methods but in the class the getwidth and getheight and press ctrl + space and press enter and the getWIDTH and getHEIGHT methods will be generated respectively.
2) the formComponentResized method will be generated by netbeans, just go to design tab and then in the Navigator which is mostly in the bottom left right click on JFrame and then goto events then components and then componentResized.
3) If you have any question don't hesitate to ask, as I have detailed knowledge of this topic.
I have a certain text, that i want to annotate for the user. Unfortunately i don't even see where to begin. My algorithm gives as output a range on a string. What i am going for is something like this:
I would need two ways to marker the Characters ( Blue line, red line), maybe also to invert the character (Give a character a different background), or make them fat. What is especially difficult is aligning the Pictures (here designated by two black dots) with the characters. Since the characters should be in Courier New, i could know where to put the offset, but i cannot seem to do it.
Lastly i would have to apply a break after X characters and begin a new line, just like in the picture. I have not found any example how to approach this with java yet. With python i could use ImageDraw, but i am out of the water with java.
is it possible to show this in a canvas on the screen and export this as svg or pdf? I dont know any libraries that could do this. So i would be happy to receive some suggestions/examples also along this way.
The key is to deal with the FontMetrics API. The best you can do is looking at this reference doc.
Here is a sample code demonstrating this usage. It draws red and blue lines around an "Hello world" text according a range of chars.
The text is inside a JLabel, but you can adapt the paint method on any component (but you will have to call graphics.drawChars to paint the text.)
(the code is not very nice, but it demonstrates the usage of FontMetrics)
package com.example.swing;
import javax.swing.*;
import java.awt.*;
public class DemoFontMetrics {
public static void main(String[] args){
JFrame frame = new JFrame();
DecoratedLabel label = new DecoratedLabel("hello world !",new int[]{2,4}, new int[]{6,9});
JPanel textContainer = new JPanel(new FlowLayout());
textContainer.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
textContainer.add(label);
frame.getContentPane().add(textContainer);
frame.pack();
frame.setVisible(true);
}
private static class DecoratedLabel extends JLabel{
int startBlue;
int endBlue;
int startRed;
int endRed;
private DecoratedLabel(String text, int[] blueRange, int[] redRange) {
super(text);
startBlue = blueRange[0];
endBlue = blueRange[1];
startRed = redRange[0];
endRed = redRange[1];
}
#Override
public void paint(Graphics g) {
super.paint(g); //draw text
//set line with : 3
Stroke stroke = new BasicStroke(3f);
((Graphics2D)g).setStroke(stroke);
FontMetrics fm = g.getFontMetrics();
int h = fm.getHeight();
//compute blue line coordonate
fm.stringWidth(getText().substring(0,startBlue));
int x1 = fm.stringWidth(getText().substring(0, startBlue));
int x2 = fm.stringWidth(getText().substring(0, endBlue));
g.setColor(Color.BLUE);
g.drawLine(x1,0,x2,0);// draw blue line
//compute red line coordonates
int x3 = fm.stringWidth(getText().substring(0,startRed));
int x4 = fm.stringWidth(getText().substring(0, endRed));
g.setColor(Color.RED);
g.drawLine(x3,h-1,x4,h-1); // draw redline
}
}
}
If the text is displayed via JTextPane you can conveniently define a custom HighlightPainter, that draws lines above or under the text.
Then you can add highlights to the text pane programmatically by calling:
textPane.getHighlighter().addHighlight(startPos, endPos,
myLineHighlightPainter);
Images may as well easily be added to the pane, by:
textPane.setIcon(myImageIcon);
You can create svg directly for example: http://xmlgraphics.apache.org/batik/ it's an xml based vector graphics format.
EDIT: You can display svg in java, you can create pdf's with it from java. You can publish it in the web (simply as svg).
SOLVED:
Just found out what the problem was, after trying to make an SSCCE.
It had to do with my cell class, I didn't realise I was overriding getX() and getY() from the JComponent class.
After renaming these accessors it all works as expected
========================================
I have a JPanel with a GridLayout set at 3 rows x 3 cols.
I'm trying to add JPanels to each cell in the gridlayout to fill up all 9 cells.
Each one of these JPanels has an overriden paintChildren method which will paint some kind of rectangle starting at the top left of the JPanel - the end result will be each cell has a rectangle in it starting at the top left of the cell.
After adding all the JPanels to the gridlayout, they all appear in the top left corner overlapping each other (I have confirmed they are overlapping), instead of being laid out in a 3x3 grid.
How can I get them arranged in the 3x3 grid?
(Simplified) Code:
public class Panel extends JPanel {
public Panel(int x, int y) {
layout = new GridLayout(x, y, 2, 2);
setLayout(layout);
populateGrid();
}
public void populateGrid() {
removeAll();
for (int i = 0; i < 9; i++)
add(new Cell(50,50));
}
}
public class Cell extends JPanel {
public Cell(int x, int y) {
// x/y values used to define rectangle
setBorder(BorderFactory.createLineBorder(new Color(0,0,0)));
setBackground(Color.WHITE);
}
public void paintChildren(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(0, 0, x, y);
}
}
Make sure you import the right Panel. (And not the java.awt.Panel class.) Or, better, rename your class to GridPanel or something similar to avoid confusion / clashes.
You probably don't want to override paintChildren. Overriding paintComponent sounds like a better option in this case.
You may want to set minimum / preferred / maximum size for the Cell class.
Hard to make more observations without the actual code that instantiates and uses your Panel class.