Pixel perfect graphics in Java swing/awt - java

Is there a way to achieve pixel perfect graphics in Swing/AWT components?
I am implementing a custom border for a button by extending AbstractBorder. For demonstration purpose thickness is always equal to 3 and each line is painted using different color.
import javax.swing.*;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.plaf.basic.BasicLookAndFeel;
import java.awt.*;
public class ExampleFrame extends JFrame {
public ExampleFrame() {
init();
}
public static void main(String[] args) throws UnsupportedLookAndFeelException
{
UIManager.setLookAndFeel(new CustomLookAndFeel());
ExampleFrame frame = new ExampleFrame();
frame.setVisible(true);
}
private void init() {
setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
getContentPane().add(panel, BorderLayout.NORTH);
GridBagConstraints constraints = new GridBagConstraints();
JButton button1 = new JButton("aaa");
button1.setVerticalTextPosition(JButton.BOTTOM);
button1.setHorizontalTextPosition(JButton.CENTER);
button1.setFocusable(false);
constraints.insets = new Insets(2, 2, 2, 2);
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 3;
constraints.fill = GridBagConstraints.BOTH;
constraints.anchor = GridBagConstraints.LINE_START;
panel.add(button1, constraints);
JButton button2 = new JButton("bbb");
button2.setHorizontalAlignment(JButton.CENTER);
button2.setFocusable(false);
constraints.gridx = 1;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.fill = GridBagConstraints.BOTH;
constraints.anchor = GridBagConstraints.LINE_START;
panel.add(button2, constraints);
JButton button3 = new JButton("eee");
button3.setHorizontalAlignment(JButton.CENTER);
button3.setFocusable(false);
constraints.gridx = 1;
constraints.gridy = 1;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.LINE_START;
panel.add(button3, constraints);
JButton button4 = new JButton("ddd");
button4.setHorizontalAlignment(JButton.CENTER);
button4.setFocusable(false);
constraints.gridx = 1;
constraints.gridy = 2;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.LINE_START;
panel.add(button4, constraints);
pack();
}
private static final class CustomBorder extends AbstractBorder {
private final int thickness;
public CustomBorder() {
this(1);
}
public CustomBorder(int thickness) {
this.thickness = thickness;
}
#Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
// ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
((Graphics2D) g).setStroke(new BasicStroke(1.0f));
for (int i = 0; i < thickness; i++) {
switch (i) {
case 0: {
g.setColor(Color.RED);
}
break;
case 1: {
g.setColor(Color.GREEN);
}
break;
case 2: {
g.setColor(Color.BLUE);
}
break;
}
// top-left -> top-right
g.drawLine(x, y + i, x + width, y + i);
// top-left > bottom-left
g.drawLine(x + i, y, x + i, y + height);
}
}
#Override
public Insets getBorderInsets(Component c, Insets insets) {
insets.top = thickness;
insets.bottom = thickness;
insets.left = thickness;
insets.right = thickness;
return insets;
}
}
private static final class CustomLookAndFeel extends BasicLookAndFeel {
#Override
protected void initComponentDefaults(UIDefaults table) {
super.initComponentDefaults(table);
// button
Border buttonBorder = new CustomBorder(3);
table.put("Button.border", buttonBorder);
table.put("Button.background", new Color(150, 150, 182));
table.put("Button.foreground", Color.BLACK);
}
#Override
public String getName() {
return "custom";
}
#Override
public String getID() {
return "custom";
}
#Override
public String getDescription() {
return "custom";
}
#Override
public boolean isNativeLookAndFeel() {
return false;
}
#Override
public boolean isSupportedLookAndFeel() {
return true;
}
}
}
Even though all 3 buttons have the same border parameters I get different border width and colors displayed.
VALUE_STROKE_PURE and VALUE_STROKE_NORMALIZE hints give different results, but none of them is pixel accurate.
Buttons "bbb", "eee" and "ddd" on the right side have the same width and height, but still the colors and total width of the border is different. Additionally, the default stroke that is being used has width of 1.0f.
I assume this happens, because Java 2D geometry operates on floating point numbers. Is there any way to overcome this limitation?
I tried different routines e.g. drawRect and different anti-aliasing hints, but no luck.

Related

Vertical alignment of a bottom panel

I want to vertically align 3 buttons in a bottom panel.
Here's what I wrote:
ClientWindow(){
pickBtn = new JButton();
attackBtn = new JButton();
placeBtn = new JButton();
JPanel userPanel = new JPanel();
userPanel.setPreferredSize(new Dimension(100,100));
userPanel.setBackground(Color.red);
JFrame frame = new JFrame();
frame.setTitle("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setResizable(false);
frame.setSize(1280,720);
frame.setLocationRelativeTo(null);
frame.add(userPanel,BorderLayout.SOUTH);
userPanel.add(pickBtn);
userPanel.add(attackBtn);
userPanel.add(placeBtn);
frame.setVisible(true);
}
How could I align them vertically?
Take a look at Laying Out Components Within a Container
For example, this following uses a GridBagLayout
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JLabel label = new JLabel("This is just here to make some content");
label.setBorder(new EmptyBorder(32, 32, 32, 32));
JFrame frame = new JFrame();
frame.add(label);
frame.add(new UserPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class UserPanel extends JPanel {
public UserPanel() {
JButton pickBtn = new JButton("Pick");
JButton attackBtn = new JButton("Attack");
JButton placeBtn = new JButton("Place");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(pickBtn, gbc);
add(attackBtn, gbc);
add(placeBtn, gbc);
}
}
}
Make a ButtonLayout
Important
Please note: The following is intended to replace the GridBagLayout from the above example, as GridBagLayout is complicated and might be a little overkill for this purpose
A REALLY long time ago, I can across a really neat concept of a ButtonLayout, it basically provided a simple layout manager to layout buttons similar to how most OS'es do it (ie, the buttons are of equal size).
The following is a VERY basic example of that concept.
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.LayoutManager2;
import java.awt.Point;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new UserPanel());
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class UserPanel extends JPanel {
public UserPanel() {
JButton pickBtn = new JButton("Pick");
JButton attackBtn = new JButton("Attack");
JButton placeBtn = new JButton("Place");
setBorder(new LineBorder(Color.BLACK));
setLayout(new ButtonLayout(ButtonLayout.Alignment.VERTICAL, ButtonLayout.Anchor.TRAILING));
add(pickBtn);
add(attackBtn);
add(placeBtn);
}
}
public class ButtonLayout implements LayoutManager2 {
public enum Alignment {
VERTICAL, HORIZONTAL
}
public enum Anchor {
LEADING, CENTER, TRAILING
}
private Alignment alignment;
private Anchor anchor;
private int padding;
private Dimension virtualBounds;
public ButtonLayout() {
this(Alignment.HORIZONTAL, Anchor.TRAILING, 0);
}
public ButtonLayout(Alignment alignment, Anchor anchor) {
this(alignment, anchor, 0);
}
public ButtonLayout(Alignment alignment, Anchor anchor, int padding) {
this.alignment = alignment;
this.padding = padding;
this.anchor = anchor;
}
public Alignment getAlignment() {
return alignment;
}
public Anchor getAnchor() {
return anchor;
}
protected int getPadding() {
return padding;
}
protected int getTotalPadding(Container parent) {
int padding = getPadding();
return (padding * parent.getComponentCount()) - padding;
}
#Override
public void addLayoutComponent(Component comp, Object constraints) {
}
#Override
public void addLayoutComponent(String name, Component comp) {
}
#Override
public void removeLayoutComponent(Component comp) {
}
#Override
public void invalidateLayout(Container target) {
virtualBounds = null;
}
protected Dimension virtualLayout(Container parent) {
if (virtualBounds != null) {
return virtualBounds;
}
int maxWidth = 0;
int maxHeight = 0;
for (Component component : parent.getComponents()) {
Dimension preferredSize = component.getPreferredSize();
maxHeight = Math.max(maxHeight, preferredSize.height);
maxWidth = Math.max(maxWidth, preferredSize.width);
}
int padding = 0;
int width = 0;
int height = 0;
int componentCount = parent.getComponentCount();
switch (alignment) {
case HORIZONTAL:
width = (maxWidth * componentCount) + getTotalPadding(parent);
height = maxHeight;
break;
case VERTICAL:
width = maxWidth;
height = (maxHeight * componentCount) + getTotalPadding(parent);
break;
}
virtualBounds = new Dimension(width, height);
return virtualBounds;
}
#Override
public Dimension maximumLayoutSize(Container parent) {
return virtualLayout(parent);
}
#Override
public Dimension preferredLayoutSize(Container parent) {
return virtualLayout(parent);
}
#Override
public Dimension minimumLayoutSize(Container parent) {
return virtualLayout(parent);
}
#Override
public float getLayoutAlignmentX(Container target) {
return 0.5f;
}
#Override
public float getLayoutAlignmentY(Container target) {
return 0.5f;
}
#Override
public void layoutContainer(Container parent) {
int maxWidth = 0;
int maxHeight = 0;
for (Component component : parent.getComponents()) {
Dimension preferredSize = component.getPreferredSize();
maxHeight = Math.max(maxHeight, preferredSize.height);
maxWidth = Math.max(maxWidth, preferredSize.width);
}
Dimension defaultSize = new Dimension(maxWidth, maxHeight);
Point point = offsetForAnchor(parent, defaultSize);
int xDelta = 0;
int yDelta = 0;
switch (alignment) {
case HORIZONTAL:
xDelta = getPadding() + defaultSize.width;
break;
case VERTICAL:
yDelta = getPadding() + defaultSize.height;
break;
}
for (Component component : parent.getComponents()) {
component.setSize(defaultSize);
component.setLocation(point);
point = new Point(point.x + xDelta, point.y + yDelta);
}
}
protected Point offsetForAnchor(Container parent, Dimension defaultSize) {
switch (anchor) {
case LEADING:
return leadingOffSet(parent, defaultSize);
case TRAILING:
return trailingOffSet(parent, defaultSize);
case CENTER:
return centerOffSet(parent, defaultSize);
}
return new Point(0, 0);
}
protected Point leadingOffSet(Container parent, Dimension defaultSize) {
Point point = new Point(0, 0);
switch (alignment) {
case HORIZONTAL:
point.x = padding;
point.y = (parent.getHeight() - defaultSize.height) / 2;
break;
case VERTICAL:
point.x = (parent.getWidth() - defaultSize.width) / 2;
point.y = padding;
break;
}
return point;
}
protected Point trailingOffSet(Container parent, Dimension defaultSize) {
Point point = new Point(0, 0);
int componentCount = parent.getComponentCount();
switch (alignment) {
case HORIZONTAL:
int totalWidth = (defaultSize.width * componentCount) + getTotalPadding(parent);
point.x = parent.getWidth() - totalWidth;
point.y = (parent.getHeight() - defaultSize.height) / 2;
break;
case VERTICAL:
int totalHeight = (defaultSize.height * componentCount) + getTotalPadding(parent);
point.x = (parent.getWidth() - defaultSize.width) / 2;
point.y = parent.getHeight() - totalHeight;
break;
}
return point;
}
protected Point centerOffSet(Container parent, Dimension defaultSize) {
Point point = new Point(0, 0);
int componentCount = parent.getComponentCount();
switch (alignment) {
case HORIZONTAL: {
int totalWidth = (defaultSize.width * componentCount) + getTotalPadding(parent);
point.x = (parent.getWidth() - totalWidth) / 2;
point.y = (parent.getHeight() - defaultSize.height) / 2;
}
break;
case VERTICAL: {
int totalHeight = (defaultSize.height * componentCount) + getTotalPadding(parent);
point.x = (parent.getWidth() - defaultSize.width) / 2;
point.y = (parent.getHeight() - totalHeight) / 2;
}
break;
}
return point;
}
}
}

How to change color of paintComponent from another class

I have an oval component on a panel in OvalShape.java that I would like to change the color of
public class OvalShape extends JPanel {
private int diameter = 100;
private Color myColor = Color.RED;
//create the actual panel
public OvalShape() {
Dimension size = getPreferredSize();
size.width = 300;
size.height= 300;
setPreferredSize(size);
setBorder(BorderFactory.createLoweredSoftBevelBorder());
}
public void setMyColor(Color theColor) {
this.myColor = theColor;
}
//create the oval
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(0, 0, diameter, diameter);
g.setColor(myColor);
}
//method for setting the diameter
public void setDiameter(int newDiameter) { //method to create diameter
//if diam invalid set to 10
diameter = (newDiameter >=0 ? newDiameter : 10);
repaint(); //repaint panel
}
}
from OvalPanel.java I want to be able to change the color. Here is what that class looks like
public class OvalPanel extends JPanel {
public int diameter = 100; //default diameter of the oval
public JSlider diamSlider;
public JLabel diamLabel;
public OvalShape ovalShape;
public OvalPanel() {
Dimension size = getPreferredSize();
size.width = 500;
setPreferredSize(size);
//create components
diamSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 300, 100);
diamSlider.setSnapToTicks(true);
diamSlider.setMajorTickSpacing(100);
diamSlider.setMinorTickSpacing(25);
diamSlider.setPaintTicks(true);
diamSlider.setPaintLabels(true);
diamLabel = new JLabel("Diameter = " + diameter);
ovalShape = new OvalShape();
ovalShape.setDiameter(diamSlider.getValue());
ovalShape.setMyColor(Color.RED);
//set listeners
diamSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
diamLabel.setText("Diameter = " + diamSlider.getValue());
ovalShape.setDiameter(diamSlider.getValue());
}
});
//set layout
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
////////first row////////
gbc.gridx = 0;
gbc.gridy = 0;
add(ovalShape, gbc);
////////second row////////
gbc.gridx = 0;
gbc.gridy = 1;
add(diamSlider, gbc);
////////third row////////
gbc.gridx = 0;
gbc.gridy = 2;
add(diamLabel, gbc);
}
but I don't really understand why this isn't working. The oval just comes out black. I have tried to apply the same theory that I did when creating the diameter slider as I am able to change the diameter of the oval using the slider in OvalPanel.java
Thanks for any help
g.fillOval(0, 0, diameter, diameter);
g.setColor(myColor);
You need to set the color BEFORE you paint the oval.

Java Swing JPanel paintComponents never called

I'm having an issue with a JPanel and I don't get what's going on. So I have a JFrame with a init function, that create a custom JPanel called GamePanel, and the strange thing is it never goes in the paintComponents function, even if I use repaint on the object.
Here is my code when I initialize the JPanel (in a JFrame):
this.gamePanel = new GamePanel(this.grid, this);
this.panel.add(this.gamePanel, constraints);
And the JPanel itself:
public class GamePanel extends JPanel {
private final int SQUARE_SIZE = 50;
private Grid grid;
private final GameView gameView;
public GamePanel(Grid grid, GameView gameView) {
this.gameView = gameView;
this.setPreferredSize(new Dimension(200, 200));
}
public void setGrid(Grid grid) {
this.grid = grid;
this.setPreferredSize(new Dimension(grid.getSizeX() * SQUARE_SIZE, grid.getSizeY() * SQUARE_SIZE));
}
#Override
public void paintComponents(Graphics g) {
System.out.println("test");
if (this.grid != null) {
Graphics2D g2 = (Graphics2D) g;
double thickness = 3;
g2.setStroke(new BasicStroke((float) thickness));
g2.setColor(Color.BLACK);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
int x = SQUARE_SIZE * i;
int y = SQUARE_SIZE * j;
g2.drawRect(x, y, SQUARE_SIZE, SQUARE_SIZE);
if(this.grid.getSquareState(x, y) != 0) {
char[] tmp = ("" + this.grid.getSquareState(x, y)).toCharArray();
g2.drawChars(tmp, 0, 1, x, y);
}
}
}
}
}
}
EDIT: (the whole JFrame)
public class GameView extends JFrame {
private CustomSocket socket;
private JPanel panel;
private GamePanel gamePanel;
private JLabel listPlayers;
private JLabel playerPlaying;
private Grid grid;
public GameView(CustomSocket socket) {
this.socket = socket;
this.setTitle("TicTacToe - Client");
this.setSize(600, 480);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.init();
this.pack();
this.setVisible(true);
this.play();
}
private void init() {
this.panel = new JPanel();
this.panel.setLayout(new GridBagLayout());
GridBagConstraints constraints =new GridBagConstraints();
constraints.fill = GridBagConstraints.CENTER;
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
// Grid
this.gamePanel = new GamePanel(this.grid, this);
this.panel.add(this.gamePanel, constraints);
// Labels
constraints.gridy += 1;
this.listPlayers = new JLabel();
this.panel.add(this.listPlayers, constraints);
constraints.gridy += 1;
this.playerPlaying = new JLabel();
this.panel.add(this.playerPlaying, constraints);
this.setContentPane(this.panel);
}
private void play() {
String[] tmp = this.socket.getData().split(";");
this.grid = new Grid(Integer.parseInt(tmp[0]), Integer.parseInt(tmp[1]));
String players = "";
for(int i = 2; i < tmp.length; i++) {
players += tmp[i] + " ";
}
this.listPlayers.setText(players);
boolean notFinished = true;
while(notFinished) {
String[] gridData = this.socket.getData().split(";");
for(int i = 1; i < gridData.length; i++) {
String[] gridRow = gridData[i].replace("(", "").replace(")", "").split(",");
for(int j = 0; j < gridRow.length; j++) {
this.grid.setSquareState(i - 1, j, Integer.parseInt(gridRow[j]));
}
}
this.gamePanel.repaint();
String playerPlaying = this.socket.getData().split(";")[0];
if(playerPlaying != this.socket.getUsername()) {
}
notFinished = true;
}
}
}
Thank you in advance.
this.panel.add(this.gamePanel, constraints);
You add the component to a panel, but the panel doesn't have a preferred size. Since its size is (0, 0) there is nothing to paint so the method is never called.
All Swing components are responsible for determining their own preferred size. Override the getPreferredSize() method of your custom component. Then the layout manager can set the proper size/location of the component.
And paintComponent(...) is the proper method to override and don't forget the super.paintComponent(...) as the first statement to make sure the background gets cleared.

Custom Scrollbar in JScrollpane//Java Swing

I've made my own ScrollbarUI and it works perfectly with JScrollbar. The problem is that I've got to connect this scrollbar with a JPanel to scroll it. I've already tried to use the JScrollPane but when I try to approach the vertical Scrollbar it just won'T respond. I've tried to change some easy things like the cursor, width, height, background to test if the UI is the problem but that didn't work either.
JPanel panel_1 = new JPanel();
JScrollPane scrollpane = new JScrollPane(panel_1);
scrollpane.setBorder(null);
scrollpane.getVerticalScrollBar().setUI(new MyScrollbarUI());
JTextArea txtrLorem = new JTextArea();
txtrLorem.setPreferredSize(new Dimension(400, 1500));
txtrLorem.setMaximumSize(new Dimension(400, 2147483647));
txtrLorem.setText("Lorem ipsum...");
panel_1.add(txtrLorem);
scrollpane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollpane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
GridBagConstraints gbc_panel_1 = new GridBagConstraints();
gbc_panel_1.gridwidth = 5;
gbc_panel_1.insets = new Insets(0, 0, 0, 5);
gbc_panel_1.fill = GridBagConstraints.BOTH;
gbc_panel_1.gridx = 1;
gbc_panel_1.gridy = 4;
add(scrollpane, gbc_panel_1);
JScrollBar scrollBar = new JScrollBar();
scrollBar.setPreferredSize(new Dimension(20, 96));
scrollBar.setMinimumSize(new Dimension(20, 5));
scrollBar.setMaximumSize(new Dimension(20, 32767));
scrollBar.setUI(new MyScrollbarUI());
GridBagConstraints gbc_scrollBar = new GridBagConstraints();
gbc_scrollBar.fill = GridBagConstraints.VERTICAL;
gbc_scrollBar.insets = new Insets(0, 0, 0, 5);
gbc_scrollBar.gridx = 6;
gbc_scrollBar.gridy = 4;
add(scrollBar, gbc_scrollBar);
the JScrollBar has the right design, but the Scrollbar of the JScrollPane hasn't.
Here is the UI:
public class MyScrollbarUI extends BasicScrollBarUI {
#Override
protected JButton createDecreaseButton(int orientation) {
JButton btnL = new JButton("");
btnL.setPressedIcon(new ImageIcon(KFO_baender_bracketsetz_einzel.class.getResource("/gui/icons/top_active.png")));
btnL.setBorderPainted(false);
btnL.setIcon(new ImageIcon(KFO_baender_bracketsetz_einzel.class.getResource("/gui/icons/top_on.png")));
btnL.setRolloverIcon(new ImageIcon(KFO_baender_bracketsetz_einzel.class.getResource("/gui/icons/top.png")));
btnL.setRolloverEnabled(true);
return btnL;
}
#Override
protected JButton createIncreaseButton(int orientation) {
JButton btnL = new JButton("");
btnL.setPressedIcon(new ImageIcon(KFO_baender_bracketsetz_einzel.class.getResource("/gui/icons/down_active.png")));
btnL.setBorderPainted(false);
btnL.setIcon(new ImageIcon(KFO_baender_bracketsetz_einzel.class.getResource("/gui/icons/down_on.png")));
btnL.setRolloverIcon(new ImageIcon(KFO_baender_bracketsetz_einzel.class.getResource("/gui/icons/down.png")));
btnL.setRolloverEnabled(true);
return btnL;
}
#Override
protected void paintDecreaseHighlight(Graphics g)
{
Insets insets = scrollbar.getInsets();
Rectangle thumbR = getThumbBounds();
g.setColor(new Color(137,144,144));
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
int x = insets.left+decrButton.getWidth()/2-2;
int y = decrButton.getY() + decrButton.getHeight();
int w = 4;
int h = thumbR.y - y;
g.fillRect(x, y, w, h);
}
else {
int x, w;
if (scrollbar.getComponentOrientation().isLeftToRight()) {
x = decrButton.getX() + decrButton.getWidth();
w = thumbR.x - x;
} else {
x = thumbR.x + thumbR.width;
w = decrButton.getX() - x;
}
int y = insets.top;
int h = scrollbar.getHeight() - (insets.top + insets.bottom);
g.fillRect(x, y, w, h);
}
}
#Override
protected void paintIncreaseHighlight(Graphics g) {
Insets insets = scrollbar.getInsets();
Rectangle thumbR = getThumbBounds();
g.setColor(new Color(202,207,203));
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
int x = insets.left+decrButton.getWidth()/2-2;
int y = thumbR.y;
int w = 4;
int h = incrButton.getY() - y;
g.fillRect(x, y, w, h);
}
}
#Override
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
{
g.setColor(Color.WHITE);
g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);
paintDecreaseHighlight(g);
paintIncreaseHighlight(g);
}
#Override
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
{
if(thumbBounds.isEmpty() || !scrollbar.isEnabled()) {
return;
}
int w = 16;
int h = 16;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.translate(thumbBounds.x, thumbBounds.y);
GradientPaint gr = new GradientPaint(2, 0, new Color(158,161,162), 18, 16, new Color(96,99,98));
g2.setPaint(gr);
g2.fill(new Ellipse2D.Double(2, 0, w-1, h-1));
g2.setPaint(new Color(203,207,203));
g2.fill(new Ellipse2D.Double(6, 4, 7, 7));
g.translate(-thumbBounds.x, -thumbBounds.y);
}
}
Try this:
JScrollPane table = new JScrollPane(content){
#SuppressWarnings("static-access")
#Override
public JScrollBar createVerticalScrollBar() {
return (new Scroler()).makeUI();
}
};
Worked for me

JPanel appears in two locations

I am designing a grid-based game which uses the Java swing framework. I have a JFrame with two JPanel within it, but one of them appears in two places. Here is a screenshot:
The panel that says "Turn 1" and has the buttons is only supposed to appear to the right of the grid, but it strangely also appears in the upper-left hand corner. Here is my code:
public class ArenaPanel extends JPanel {
private final GridPanel gp;
private final InfoPanel ip;
private GameManager gm;
private int w, h;
private int cw, ch;
private double tw, th;
private Point p2;
private Point p1;
private int shotWidth;
private Color shotColor;
public ArenaPanel(int w, int h) {
Images.load();
setLayout(new GridBagLayout());
this.w = w;
this.h = h;
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 1;
c.weightx = 0;
c.weighty = 1;
gp = new GridPanel();
gp.setPreferredSize(new Dimension(700, 700));
this.add(gp, c);
c.gridx = 1;
c.weightx = c.weighty = 0;
ip = new InfoPanel();
add(ip, c);
}
public void setGameManager(GameManager g) {
gm = g;
}
public void start() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
gm.start();
}
});
t.start();
}
public void step() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
gm.doTurn();
}
});
t.start();
}
public void paint(Graphics g) {
g.setColor(Color.black);
int val = Math.min(getWidth() - 200, getHeight());
gp.setPreferredSize(new Dimension(val, val));
gp.revalidate();
g.fillRect(0, 0, getWidth(), getHeight());
paintComponents(g);
}
private class GridPanel extends JPanel {
public void paint(Graphics g) {
cw = getWidth();
ch = getHeight();
g.setColor(Color.gray);
g.fillRect(0, 0, cw, ch);
tw = (double) cw / w;
th = (double) ch / h;
g.setColor(Color.black);
for (int i = 0; i < w; i++) {
g.drawLine((int) (i * tw), 0, (int) (i * tw), ch);
}
for (int i = 0; i < h; i++) {
g.drawLine(0, (int) (i * th), cw, (int) (i * th));
}
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
Robot t = gm.getGrid()[i][j];
if (t != null) {
Point p = expand(i, j);
g.drawImage(t.getImage(), p.x + t.getOffsetX(),
p.y + t.getOffsetY(), (int) tw, (int) th, null);
}
}
}
if (p1 != null) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(shotColor);
g2.setStroke(new BasicStroke(shotWidth));
g2.drawLine(p1.x, p1.y, p2.x, p2.y);
p1 = null;
p2 = null;
}
}
}
private class InfoPanel extends JPanel implements ActionListener {
private JButton start, stop, step;
private JLabel turns;
private int numTurns = 0;
private GridBagConstraints gbc;
private ArrayList<TeamPanel> tpanels;
public InfoPanel() {
JPanel buttons = new JPanel();
setLayout(new GridBagLayout());
buttons.setLayout(new GridBagLayout());
gbc = new GridBagConstraints();
start = new JButton("Start");
gbc.gridy = 0;
gbc.gridx = 1;
turns = new JLabel("Turn 1");
buttons.add(turns, gbc);
start.addActionListener(this);
gbc.gridy = 1;
gbc.gridx = 0;
buttons.add(start, gbc);
step = new JButton("Step");
step.addActionListener(this);
gbc.gridx++;
buttons.add(step, gbc);
stop = new JButton("Stop");
stop.addActionListener(this);
gbc.gridx++;
buttons.add(stop, gbc);
gbc.gridx = 0;
gbc.gridy = 0;
add(buttons, gbc);
tpanels = new ArrayList<TeamPanel>();
}
#Override
public void actionPerformed(ActionEvent actionEvent) {
if (actionEvent.getSource() == start) {
start();
} else if (actionEvent.getSource() == stop) {
gm.stop();
} else if (actionEvent.getSource() == step) {
step();
}
}
public void incrementTurn() {
numTurns++;
turns.setText("Turn " + numTurns);
}
public void initializeTeams(Map<String, TeamInfo> m) {
Set<String> k = m.keySet();
for (TeamPanel tp : tpanels) {
this.remove(tp);
}
tpanels.clear();
gbc.gridy = 1;
for (String s : k) {
TeamPanel tp = new TeamPanel(m.get(s));
add(tp, gbc);
gbc.gridy++;
tpanels.add(tp);
}
this.revalidate();
}
}
private class TeamPanel extends JPanel {
private Color col;
private int score;
private JLabel scoreLabel;
private TeamInfo inf;
public TeamPanel(TeamInfo inf) {
this.inf = inf;
col = getColor(inf.c);
super.setLayout(new FlowLayout());
BufferedImage ico = new BufferedImage(20, 20, BufferedImage.TYPE_3BYTE_BGR);
Graphics g = ico.getGraphics();
g.setColor(col);
g.fillRect(0, 0, 20, 20);
add(new JLabel(new ImageIcon(ico)));
this.add(new JLabel(inf.team));
scoreLabel = new JLabel("" + inf.score);
add(scoreLabel);
}
public void paint(Graphics g) {
//g.setColor(col);
//g.fillRect(-5, 0, 10, 10);
scoreLabel.setText("Score: " + inf.score);
this.paintComponents(g);
}
}
public void initializeTeams(Map<String, TeamInfo> m) {
ip.initializeTeams(m);
}
}
I have looked on google and StackOverflow for a similar problem, but I was unable to find one. Any help would be greatly appreciated.
Thanks!
Don't override the paint(...) method
Override the JPanel's paintComponent(...) method instead.
Don't forget to call the super paintComponent(...) method within your override.
And never call the super's paintComponents(...) (note the trailing "s") from within either the paint or paintComponent method. This smells like it could be the cause of your problem.

Categories