Resizing panel with flow layout does not invoke the scroll bars - swing

I have a containing panel JPanel with a Flow Layout, the containing panel is in a JScrollPane, the containing panel holds a bunch of other JPanels, inner panels, in it. All the inner panels have the same dimensions. If there are more panels then the containing panel can hold in its width then they are gridded downwards, and if there are more panels then the containing panel can hold in its height, then the inner panels are aligned in the same grid with the exception of the last row which is centered with the row before last.
While I resize the dialog the containing panel extends and the layout flow layout perform its duty, but the scroll bars do not appear although the size of the panel exceeds the bounds of the JScrollPane.
How do I control the appearance of the scroll bars when I resize the containing panel dynamically?
as for the images, they should sum it up:
After extending the dialog width:
Adam

You need to make the containing panel implement Scrollable, and set the preferred scrollable viewport size according to the width.
import java.awt.*;
import javax.swing.*;
public class Test
{
public static void main(String args[])
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JPanel container = new ScrollablePanel();
container.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
for( int i = 0; i < 20; ++i )
{
JPanel p = new JPanel();
p.setBorder(BorderFactory.createLineBorder(Color.RED));
p.setPreferredSize(new Dimension(50, 50));
p.add(new JLabel("" + i));
container.add(p);
}
JScrollPane scroll = new JScrollPane(container);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(scroll);
f.pack();
f.setSize(250, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
class ScrollablePanel extends JPanel implements Scrollable
{
public Dimension getPreferredSize()
{
return getPreferredScrollableViewportSize();
}
public Dimension getPreferredScrollableViewportSize()
{
if( getParent() == null )
return getSize();
Dimension d = getParent().getSize();
int c = (int)Math.floor((d.width - getInsets().left - getInsets().right) / 50.0);
if( c == 0 )
return d;
int r = 20 / c;
if( r * c < 20 )
++r;
return new Dimension(c * 50, r * 50);
}
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
{
return 50;
}
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
{
return 10;
}
public boolean getScrollableTracksViewportHeight()
{
return false;
}
public boolean getScrollableTracksViewportWidth()
{
return getParent() != null ? getParent().getSize().width > getPreferredSize().width : true;
}
}
All numbers are hardcoded for simplicity, but you should get the idea.

Related

Scala swing all button same width

I have a BoxPanel of buttons in my scala swing app that looks ugly to me because the buttons are all different sizes. I had changed it to a GridPanel but then they filled the panel vertically aswell which I found uglier. How can I have all buttons fill the width of the BoxPanel but stay their perferred height?
I tried a work around, shown below, where the panel sets all the contents to the max width but it had no effect.
val buttons = new BoxPanel(Orientation.Vertical) {
contents += new Button("Normal Button")
contents += new Button("small")
contents += new Button("Significantly larger button than the rest")
val maxWidth = contents map {
(button: Component) => button.preferredSize
} maxBy { _.width }
contents foreach {
(button: Component) => button.preferredSize = maxWidth
}
}
Is there a way to make the above workaround work or a way that isn't a workaround?
A s discussed in Box Layout Features, "if all the components have identical X alignment, then all components are made as wide as their container." Override the button's getMaximumSize() implementation as shown below to return an arbitrary width and the button's preferred hight. Change setHorizontalAlignment() and/or resize the frame to see the effect.
#Override
public Dimension getMaximumSize() {
return new Dimension(
Short.MAX_VALUE, getPreferredSize().height);
}
Code as shown:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/a/34443937/230513 */
public class ButtonBoxTest {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JPanel() { //arbitrary filler
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
});
f.add(createButtonPanel(), BorderLayout.EAST);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel btnPanel = new JPanel();
btnPanel.setLayout(new BoxLayout(btnPanel, BoxLayout.Y_AXIS));
btnPanel.add(createButton("Button 1"));
btnPanel.add(createButton("Button 2"));
btnPanel.add(createButton("Long Button 3"));
btnPanel.add(createButton("Button 4"));
btnPanel.add(createButton("Button 5"));
return btnPanel;
}
private JButton createButton(String name) {
final JButton b = new JButton(name) {
#Override
public Dimension getMaximumSize() {
return new Dimension(
Short.MAX_VALUE, getPreferredSize().height);
}
};
b.setAlignmentX(0.5f);
b.setHorizontalAlignment(JButton.RIGHT);
return b;
}
public static void main(String[] args) {
EventQueue.invokeLater(new ButtonBoxTest()::display);
}
}
TrashGod is right but I figured I would post an answer translated to Scala as his is in Java. I got my original workaround to work by replacing preferredSize with maximumSize, thanks to reading trashgod's answer.
val buttons = new BoxPanel(Orientation.Vertical) {
contents += new Button("Normal Button")
contents += new Button("small")
contents += new Button("Significantly larger button than the rest")
val maxWidth = contents map {
(button: Component) => button.maximumSize
} maxBy { _.width }
contents foreach {
(button: Component) => button.maximumSize = maxWidth
}
}
I was also able to get a solution translated from trashgod's answer with just
private def equalButtons(name: String): Button = {
new Button(name) {
maximumSize = new Dimension(Short.MaxValue, maximumSize.height)
}
}
val buttons = new BoxPanel(Orientation.Vertical) {
contents += new equalButtons("Normal Button")
contents += new equalButtons("small")
contents += new equalButtons("Significantly larger button than the rest")
}

Libgdx ScrollPane adding space at the top

I'm creating a store for my game and I'm having issues with the libgdx ScrollPane. It seems to be moving the table within it down by 155px. The effect it's having is that the table appears roughly 155px down from the top, and the last button is off the screen no matter how far down I scroll.
Here is the window when the "Store" first appears.
As you can see the top of the table is quite a bit below the top of the window. Code below, but the table only has 10px of padding.
Here is the window when I've scrolled down as far as I could go - sorry, the scroll pars disappeared before I could grab the screenshot.
You can see that the "Main Menu" button is about half way off the screen.
Here is the code that lays out the "Store".
table = new Table();
table.setFillParent(true);
table.defaults().expandX().fill().space(5f);
table.pad(10f);
// characters
table.add(new Label("Characters: ", skin, "default")).left().colspan(2);
table.row();
ButtonGroup characterGroup = new ButtonGroup();
characterGroup.setMaxCheckCount(1);
Button boyButton = createImageTextButton("Plain Boy", boyTexture);
Button catButton = createImageTextButton("Cat Girl", catTexture);
Button hornButton = createImageTextButton("Horn Girl", hornTexture);
Button pinkButton = createImageTextButton("Pink Girl", pinkTexture);
Button queenButton = createImageTextButton("Queen", queenTexture);
float minHeight = 130f; // only used to force scrolling.
table.add(boyButton).minHeight(minHeight);
table.add(catButton).minHeight(minHeight).row();
table.add(hornButton).minHeight(minHeight);
table.add(pinkButton).minHeight(minHeight).row();
table.add(queenButton).minHeight(minHeight).colspan(2);
table.row();
characterGroup.add(boyButton, catButton, hornButton, pinkButton, queenButton);
characterGroup.setChecked("Plain Boy");
// drills
ButtonGroup drillGroup = new ButtonGroup();
Button flappyButton = createImageTextButton("Flappy", null);
Button tappyButton = createImageTextButton("Tappy", null);
Button tiltyButton = createImageTextButton("Tilty", null);
table.add(new Label("Drills:", skin, "default")).left().padTop(20).colspan(2);
table.row();
table.add(flappyButton).minHeight(minHeight);
table.add(tappyButton).minHeight(minHeight).row();
table.add(tiltyButton).minHeight(minHeight);
drillGroup.add(flappyButton, tappyButton, tiltyButton);
drillGroup.setChecked("Flappy");
// main menu button
table.row();
Button mainMenuButton = createImageTextButton("Main Menu", null);
table.add(mainMenuButton).minHeight(minHeight).padTop(40).padBottom(10f).colspan(2);
ScrollPane scrollPane = new ScrollPane(table, skin);
scrollPane.setBounds(0, 0, stage.getWidth(), stage.getHeight());
addActor(scrollPane);
I debugged the desktop version and saw that the table's y position was set to -155 before I scroll, and set to 0 after I scroll to the bottom. However, even when it's set to 0, the last button is always partly off the screen.
As requested here is the code where I create the viewport and camera:
package com.example;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.example.stages.GameStage;
public class MainGame extends ApplicationAdapter {
public static final int MIN_WIDTH = 480;
public static final int MIN_HEIGHT = 800;
public static SpriteBatch batch;
public static GameStage game;
public static Skin skin;
public static Viewport viewport;
public static Stage stage;
#Override
public void create() {
viewport = new ExtendViewport(MIN_WIDTH, MIN_HEIGHT);
batch = new SpriteBatch();
initSkin();
game = new GameStage(viewport, batch, skin);
setStage(game);
}
#Override
public void dispose() {
super.dispose();
game.dispose();
skin.dispose();
}
public void initSkin() {
skin = new Skin(Gdx.files.internal("skins/uiskin.json"));
}
#Override
public void render() {
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
#Override
public void resize(int width, int height) {
viewport.update(width, height, true);
}
public Skin getSkin() {
return skin;
}
public static void setStage(Stage stage) {
MainGame.stage = stage;
Gdx.input.setInputProcessor(stage);
}
}
Ok, finally figured it out. Turns out the Table within the ScrollPane cannot be set to fill parent.
table.setFillParent(true);
Causes the issue. Not sure why it's doing that, but removing the setFillParent(true) fixes the issue.

Gui - JPanel adding JComponent into GridLayout cell

I'm trying to display a frame using GridLayout, but one of my panels isn't displaying.
The JPanel that I'm having trouble with (gridPanel) is supposed to have a 50 by 50 GridLayout and each cell in that grid is supposed to have a Square object added to it. Then that panel is supposed to be added to the frame, but it doesn't display.
import java.awt.*;
import javax.swing.*;
public class Gui extends JFrame{
JPanel buttonPanel, populationPanel, velocityPanel, gridPanel;
JButton setupButton, stepButton, goButton;
JLabel populationLabel, velocityLabel;
JSlider populationSlider, velocitySlider;
Square [] [] square;
public Gui() {
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
//Set up JButtons
buttonPanel = new JPanel();
setupButton = new JButton("Setup");
stepButton = new JButton("Step");
goButton = new JButton("Go");
buttonPanel.add(setupButton);
buttonPanel.add(stepButton);
buttonPanel.add(goButton);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 2; //2 columns wide
add(buttonPanel, c);
//Set up populationPanel
populationPanel = new JPanel();
populationLabel = new JLabel("Population");
populationSlider = new JSlider(JSlider.HORIZONTAL,0, 200, 1);
populationPanel.add(populationLabel);
populationPanel.add(populationSlider);
c.fill = GridBagConstraints.LAST_LINE_END;
c.gridx = 0;
c.gridy = 2;
c.gridwidth = 2; //2 columns wide
add(populationPanel, c);
//Set up velocityPanel
velocityPanel = new JPanel();
velocityLabel = new JLabel(" Velocity");
velocitySlider = new JSlider(JSlider.HORIZONTAL,0, 200, 1);
velocityPanel.add(velocityLabel);
velocityPanel.add(velocitySlider);
c.fill = GridBagConstraints.LAST_LINE_END;
c.gridx = 0;
c.gridy = 3;
c.gridwidth = 2; //2 columns wide
add(velocityPanel, c);
//Set up gridPanel
JPanel gridPanel = new JPanel(new GridLayout(50, 50));
square = new Square[50][50];
for(int i = 0; i < 50; i++){
for(int j = 0; j < 50; j++){
square[i][j] = new Square();
gridPanel.add(square[i][j]);
}
}
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 2; //2 columns wide
add(gridPanel, c);
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
Gui frame = new Gui();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Square class
import java.awt.*;
import javax.swing.*;
public class Square extends JComponent{
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(10, 10, 10, 10);
}
}
The reason you are not seeing anything are two-fold (though not entirely sure if the second is intentional, guessing on that part :)
JComponent is a bare-bone container, it has no ui delegate and no inherent size - it's up to your code to return something reasonable for min/max/pref
the painting rect is hard-coded to "somewhere" inside, which might or might be inside the actual component area
Following is showing (border just to see the boundaries)
public static class Square extends JComponent {
public Square() {
setBorder(BorderFactory.createLineBorder(Color.RED));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
#Override
public Dimension getMaximumSize() {
return getPreferredSize();
}
#Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
}

Why does part of my RichJLabel text look covered/hidden?

I've been reading the Swing Hacks book and have used some of their code for the RichJLabel part. I understand what the code does, but not why some of the word is covered or looks hidden. It's not that it's not drawing all of the text because even half of the 'a' in horizontal is missing.
//imported libraries for the GUI
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.*;
import java.awt.Toolkit;
import java.awt.Dimension;
import java.awt.Container;
import java.awt.BorderLayout;
import java.awt.*;
//Rich JLabel
import java.awt.GraphicsEnvironment;
import java.awt.Graphics2D;
import java.awt.FontMetrics;
import java.awt.RenderingHints;
import java.awt.font.*;
import javax.swing.ListSelectionModel;
import java.awt.Color;
import java.awt.Font;
public class nameInterfaceOne
{
//Declared components
static JFrame frame;
static JPanel TotalGUI, northP, southP, eastP, centerP, westP;
static JButton buttons;
//Frame method
public nameInterfaceOne()
{
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} //For a different look & feel, change the text in the speech marks
catch (ClassNotFoundException e) {}
catch (InstantiationException e) {}
catch (IllegalAccessException e) {}
catch (UnsupportedLookAndFeelException e) {}
frame = new JFrame("Interface");
frame.setExtendedState(frame.NORMAL);
frame.getContentPane().add(create_Content_Pane());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500); //Size of main window
frame.setVisible(true);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
// gets & sets frame size/location
int fw = frame.getSize().width;
int fh = frame.getSize().height;
int fx = (dim.width-fw)/2;
int fy = (dim.height-fh)/2;
//moves the frame
frame.setLocation(fx, fy);
}
public JPanel create_Content_Pane()
{
TotalGUI = new JPanel();
TotalGUI.setLayout(new BorderLayout(5,5)); //set layout for the Container Pane
northP = new JPanel();
northP.setBorder(new TitledBorder(new EtchedBorder(), "Label"));
TotalGUI.add(northP, BorderLayout.NORTH);
RichJLabel label = new RichJLabel("Horizontal", 1);
label.setLeftShadow(1,1,Color.white);
label.setRightShadow(1,1,Color.gray);
label.setForeground(Color.black);
label.setFont(label.getFont().deriveFont(20f));
Box top = Box.createHorizontalBox();
top.add(Box.createHorizontalStrut(10));
top.add(label);
top.add(Box.createHorizontalStrut(10));
northP.add(top);
//EAST Panel
eastP = new JPanel();
eastP.setBorder(new TitledBorder(new EtchedBorder(), "Boxes"));
TotalGUI.add(eastP, BorderLayout.EAST);
Box right = Box.createVerticalBox();
right.add(Box.createVerticalStrut(20));
right.add(new JLabel("EAST SIDE!"));
eastP.add(right);
//WEST Panel
westP = new JPanel();
westP.setBorder(new TitledBorder(new EtchedBorder(), "Buttons"));
TotalGUI.add(westP, BorderLayout.WEST);
Box left = Box.createVerticalBox();
left.add(Box.createVerticalStrut(10));
ButtonGroup JbuttonGroup = new ButtonGroup();
JButton buttons;
JbuttonGroup.add(buttons = new JButton("One"));
buttons.setToolTipText("This is Button One");
left.add(buttons);
left.add(Box.createVerticalStrut(10));
JbuttonGroup.add(buttons = new JButton("Two"));
buttons.setToolTipText("This is Button Two");
left.add(buttons);
left.add(Box.createVerticalStrut(10));
JbuttonGroup.add(buttons = new JButton("Three"));
buttons.setToolTipText("This is Button Three");
left.add(buttons);
left.add(Box.createVerticalStrut(10));
westP.add(left);
TotalGUI.setOpaque(true);
return(TotalGUI);
}
//Main method calling a new object of
public static void main(String[] args)
{
new nameInterfaceOne();
}
}
//RICH JLABEL CLASS
class RichJLabel extends JLabel
{
private int tracking;
public RichJLabel(String text, int tracking) {
super(text);
this.tracking = tracking;
}
private int left_x, left_y, right_x, right_y;
private Color left_color, right_color;
public void setLeftShadow(int x, int y, Color color) {
left_x = x;
left_y = y;
left_color = color;
}
public void setRightShadow(int x, int y, Color color) {
right_x = x;
right_y = y;
right_color = color;
}
public Dimension getPreferredSize()
{
String text = getText();
FontMetrics fm = this.getFontMetrics(getFont());
int w = fm.stringWidth(text);
w += (text.length())*tracking;
w += left_x + right_x;
int h = fm.getHeight();
h += left_y + right_y;
return new Dimension(w,h);
}
public void paintComponent(Graphics g) {
((Graphics2D)g).setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
char[] chars = getText().toCharArray();
FontMetrics fm = this.getFontMetrics(getFont());
int h = fm.getAscent();
int x = 0;
for(int i=0; i<chars.length; i++)
{
char ch = chars[i];
int w = fm.charWidth(ch) + tracking;
g.setColor(left_color);
g.drawString(""+chars[i],x-left_x,h-left_y);
g.setColor(right_color);
g.drawString(""+chars[i],x+right_x,h+right_y);
g.setColor(this.getForeground());
g.drawString(""+chars[i],x,h);
x+=w;
}
((Graphics2D)g).setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
} // end paintComponent()
}
Thank you in advance for any help :)
Empirically, the problem disappears when adding the label directly to the (default) FlowLayout of northP.
northP.add(label);
Addendum: An alternative is to override getMaximumSize(), as suggested in How to Use BoxLayout: Specifying Component Sizes.
#Override
public Dimension getMaximumSize() {
return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
}

Resize drawing to match frame size

I've written an app that custom draws everything inside paint() based on fixed pixel positions. Then I disabled resize of the frame so its always visible.
However, now I would like to be able to resize it but I dont want to change my drawling code. I was hoping I could grab the 300x300 square of the Graphics g object and resize it to the JFrame current size after all of my drawling code, but I have no idea what I'm doing.
Here sample code. In this I want the 100x100 square to remain in the middle, proportionate to the resized JFrame:
package DrawAndScale;
import java.awt.Color;
import java.awt.Graphics;
public class DASFrame extends javax.swing.JFrame {
public DASFrame() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
this.setSize(300, 300);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new DASFrame().setVisible(true);
}
});
}
#Override
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fill3DRect(100, 100, 100, 100, true);
}
}
Thanks.
Assuming you rename your method that paints for 300x300 as paint300, define a buffered image:
#Override public void paint(Graphics g) {
Image bufferImage = createImage(300, 300); // empty image
paint300(bufferImage.getGraphics()); // fill the image
g.drawImage(bufferImage, 0, 0, null); // send the image to graphics device
}
Above is when you want to draw at full size (300x300).
If your window is resized:
#Override public void paint(Graphics g) {
Image bufferImage = createImage(300, 300);
paint300(bufferImage.getGraphics());
int width = getWidth();
int height = getHeight();
CropImageFilter crop =
new CropImageFilter((300 - width)/2, (300 - height)/2 , width, height);
FilteredImageSource fis = new FilteredImageSource(bufferImage, crop);
Image croppedImage = createImage(fis);
g.drawImage(croppedImage, 0, 0, null);
}
The new classes are from from java.awt.image.*.
I didn't test this code. It's just to send you in the right direction.
if you want to painting Custom paint then look for JLabel or JPanel and including Icon/ImageIcon inside, simple example about that
import java.awt.*;
import javax.swing.*;
public class MainComponentPaint extends JFrame {
private static final long serialVersionUID = 1L;
public MainComponentPaint() {
setTitle("Customize Preffered Size Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
add(new CustomComponent());
pack();
setMinimumSize(getSize());
setPreferredSize(getSize());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setVisible(true);
}
});
}
public static void main(String[] args) {
MainComponentPaint main = new MainComponentPaint();
main.display();
}
}
class CustomComponent extends JComponent {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
for (int i = 0; i < Math.max(w, h); i += 20) {
g.drawLine(i, 0, i, h);
g.drawLine(0, i, w, i);
}
}
}
Not an expert, but you could just scale the Graphics2D object (the passed Graphics is in fact a Graphics2D instance), where the x and y ratios are the ratios of the fixed size you chose to draw and the actual size of the frame.
See http://download.oracle.com/javase/6/docs/api/java/awt/Graphics2D.html#scale%28double,%20double%29
You could do this with some math.
public void paint(Graphics g){
int height = 100;
int width = 100;
int x = (this.getWidth() / 2) - (width / 2);
int y = (this.getHeight() / 2) - (height / 2);
g.setColor(Color.BLACK);
g.fill3DRect(x, y, width, height, true);
}
Or if you wanted to keep the width and height of the box with the same proportion, use int width = this.getWidth() / 3; and int height = this.getHeight() / 3.
The other option is to use Graphics2D.scale(), as JB pointed out, the passed Graphics object is actually a Graphics2D object.