JButton's icon becomes pixelated on hovering - java

I'm making a Java Swing app and I came across a problem. I want to make a button with an icon on it.
To make it, I first make a rescaled ImageIcon:
studentIcon = new ImageIcon(new ImageIcon(
"D:\\Programming\\Java\\ELearningDesktop\\src\\com\\core\\student.png")
.getImage().getScaledInstance(64, 64, Image.SCALE_SMOOTH));
Then I make a button and set the icon on it:
JButton studentButton = new JButton();
studentButton.setIcon(studentIcon);
studentButton.setFocusable(false);
It works fine, but for some reason the icon on button becomes pixelated every time I hover mouse onto it. After hovering it never becomes smooth unless I rescale the JFrame, so that it probably calls repaint() somewhere and it repaints it.
I use a downloaded look-and-feel but the problem remains if I use the default look-and-feel. Using ImageIcon the same way, but without rescaling does not help - pixilation still appears.
What could be the solution?
Starting point of the program
public class Starter {
public static void main(String[] args) {
FlatLightLaf.install();
EventQueue.invokeLater(()->{
AuthFrame frame = new AuthFrame();
frame.setVisible(true);
});
}
}
AuthFrame
public class AuthFrame extends JFrame {
private JPanel mainPanel;
private LoginPanel loginPanel;
private ImageIcon studentIcon;
private ImageIcon teacherIcon;
public AuthFrame() {
setLayout(new GridBagLayout());
ImageIcon imageIcon = new ImageIcon(new ImageIcon(
"D:\\Programming\\Java\\ELearningDesktop\\src\\com\\core\\tileBackground.jpg")
.getImage().getScaledInstance(321, 333, Image.SCALE_SMOOTH));
mainPanel = new BackgroundPanel(imageIcon.getImage(), BackgroundPanel.TILED,
0f, 0.5f);
add(mainPanel, new GBC(0, 0).setFill(BOTH).setWeights(1, 1));
mainPanel.setLayout(new GridBagLayout());
studentIcon = new ImageIcon(new ImageIcon(
"D:\\Programming\\Java\\ELearningDesktop\\src\\com\\core\\student.png")
.getImage().getScaledInstance(64, 64, Image.SCALE_SMOOTH));
teacherIcon = new ImageIcon(new ImageIcon(
"D:\\Programming\\Java\\ELearningDesktop\\src\\com\\core\\teacher.png")
.getImage().getScaledInstance(64, 64, Image.SCALE_SMOOTH));
loginPanel = new LoginPanel();
mainPanel.add(loginPanel);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(FRAME_WIDTH, FRAME_HEIGHT);
setExtendedState(Frame.MAXIMIZED_BOTH);
}
LoginPanel - a private inner class that is used in the AuthFrame
private class LoginPanel extends JPanel {
private JTextField usernameField;
private JPasswordField passwordField;
private JLabel errorLabel;
private JButton studentButton;
private JButton teacherButton;
private JLabel titleLabel;
private boolean forStudent = true;
public LoginPanel() {
setLayout(new GridBagLayout());
setBackground(new Color(255, 255, 255, 181));
studentButton = new JButton();
studentButton.setIcon(studentIcon);
studentButton.setFocusable(false);
//add(studentButton, new GBC(0, 0).setAnchor(GBC.WEST).setInsets(10));
teacherButton = new JButton(teacherIcon);
teacherButton.setFocusable(false);
add(teacherButton, new GBC(0, 0).setAnchor(GBC.WEST).setInsets(10));
titleLabel = new JLabel("<html>Signing in as <b>student</b></html>");
Utils.deriveFontForTo(titleLabel, 24f);
add(titleLabel, new GBC(1, 0).setAnchor(GBC.EAST).setInsets(10));
titleLabel.setVerticalAlignment(JLabel.BOTTOM);
JLabel usernameLabel = new JLabel("Username");
Utils.deriveFontForTo(usernameLabel, 24f);
add(usernameLabel, new GBC(0, 1).setAnchor(GBC.WEST).setInsets(10));
usernameLabel.setHorizontalAlignment(LEFT);
JLabel passwordLabel = new JLabel("Password");
Utils.deriveFontForTo(passwordLabel, 24f);
add(passwordLabel, new GBC(0, 2).setAnchor(GBC.WEST).setInsets(10));
usernameField = new JTextField(15);
Utils.deriveFontForTo(usernameField, 24f);
add(usernameField, new GBC(1, 1).setInsets(10));
passwordField = new JPasswordField(15);
Utils.deriveFontForTo(passwordField, 24f);
add(passwordField, new GBC(1, 2).setInsets(10));
errorLabel = new JLabel();
Utils.deriveFontForTo(errorLabel, 16f);
errorLabel.setForeground(Color.RED);
add(errorLabel, new GBC(1, 3).setAnchor(GBC.WEST).setInsets(2));
errorLabel.setHorizontalAlignment(LEFT);
JButton loginButton = new JButton("Log in");
loginButton.setFocusable(false);
Utils.deriveFontForTo(loginButton, 24f);
add(loginButton, new GBC(1, 4, 1, 1)
.setFill(GridBagConstraints.HORIZONTAL).setInsets(10));
JButton registerButton = new JButton("Sign Up");
loginButton.setFocusable(false);
Utils.deriveFontForTo(registerButton, 24f);
add(registerButton, new GBC(1, 5, 1, 1)
.setInsets(10));
}
}
GBC - a covenience class to use GridBagLayout
package com.core.helpers.graphics;
import java.awt.*;
public class GBC extends GridBagConstraints {
public GBC(int gridX, int gridY){
super.gridx = gridX;
super.gridy = gridY;
}
public GBC(int gridX, int gridY, int gridWidth, int gridHeight){
super.gridx = gridX;
super.gridy = gridY;
super.gridwidth = gridWidth;
super.gridheight = gridHeight;
}
public GBC setAnchor(int anchor){
super.anchor = anchor;
return this;
}
public GBC setWeights(double weightX, double weightY){
super.weightx = weightX;
super.weighty = weightY;
return this;
}
public GBC setFill(int fill){
super.fill = fill;
return this;
}
public GBC setInsets(int k){
this.insets = new Insets(k,k,k,k);
return this;
}
}
Thanks
Screenshots:
first - two buttons are smooth
second - hovered button becomes pixilated
third - button becomes smooth again after resizing frame

it never becomes smooth unless I rescale the JFrame, so that it probably calls repaint() somewhere and it repaints it.
setBackground(new Color(255, 255, 255, 181));
I would guess the above is related to the problem. Swing does not support transparent backgrounds.
Swing expects the component to be opaque, in which case the component is responsible for painting its opaque background.
Or, the component can be non-opaque in which case the parent component is painted first to make sure an opaque background is painted.
If you have transparency then you need to make the component non-opaque and override the paintComponent method and paint the background yourself.
Basic code is:
JPanel panel = new JPanel()
{
protected void paintComponent(Graphics g)
{
g.setColor( getBackground() );
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
};
panel.setOpaque(false); // background of parent will be painted first
panel.setBackground( new Color(255, 0, 0, 20) );
frame.add(panel);
See Backgrounds With Transparency for more information and a reusable solution so you don't need to use custom painting every time.

Edit:
Looks like my problem was connected to windows scaling, as the marked answer here Java Swing, all images appear pixelated
suggests. Using the marked answer from this link helped me. Here it is: https://stackoverflow.com/a/50566705/12538636
Brute force approach :
By repainting the button's panel every time there's a change in button makes it look smooth all the time. Here's code fragment:
teacherButton.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
mainPanel.repaint();
}
});
studentButton.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
mainPanel.repaint();
}
});
The only little issue remained is that now there's a tiny gap between hovering and button's respond to hovering (changing a color slightly to denote it's hovered).

Related

Jframe is setting everything in the middle of the screen

My Jframe is setting every component at the middle of the screen even if i set a specific location...
for exmaple, the button and the searchbar are right next to each other.
Here is the image of the program:
thanks!
It really seems easy but i can't figure out why it doesn't work
private String msg;
private JFrame frame;
private JButton button;
private JPanel panel;
private JTextField searchBar;
private JLabel name;
private JLabel logo;
public GUI() {
this.frame = new JFrame();
this.panel = new JPanel();
this.panel.setBorder(BorderFactory.createEmptyBorder(300, 300, 300, 300));
//this.panel.setLayout(new GridLayout(10, 10));
this.frame.add(panel, BorderLayout.CENTER);
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame.setTitle("emdb (ezon's movie database)");
Icon icon = new ImageIcon("images/search.png");
this.button = new JButton(icon);
this.button.setBounds(100, 20, 25, 25);
this.button.addActionListener(this);
this.searchBar = new JTextField(30);
this.searchBar.setBounds(100, 20, 200, 25);
this.name = new JLabel();
this.name.setBounds(50, 20, 80, 25);
ImageIcon log = new ImageIcon("images/logo.png");
this.logo = new JLabel(log);
this.logo.setBounds(100, 0, 300, 150);
}
public void clearScreen() {
this.panel.removeAll();
this.panel.revalidate();
this.panel.repaint();
}
public void searchScreen() {
this.panel.add(this.searchBar);
this.panel.add(this.button);
this.panel.add(this.logo);
this.frame.pack();
this.frame.setVisible(true);
}
public void searchedScreen() {
this.panel.add(this.name);
}
public String getMsg() {
return(this.msg);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Searched: " + this.searchBar.getText());
this.name.setText(this.searchBar.getText());
this.msg = this.searchBar.getText();
clearScreen();
searchedScreen();
}
Swing was designed to be used with layout managers.
even if i set a specific location.
The job of the layout manager is to set the size and location of the component based on the rules of the layout manager. So your setBounds() statements will be overridden. Don't attempt to use setBounds().
the button and the searchbar are right next to each other
The default layout manager for a JPanel is the FlowLayout, which does exactly that.
this.frame.add(panel, BorderLayout.CENTER);
Change the above statement to:
this.frame.add(panel, BorderLayout.PAGE_START);
to see a difference.
Read the section from the Swing tutorial on Layout Managers for more information and examples. Decide which layout manager (or combination of layout managers on different panels) will achieve your desired layout.

Background contentPane of a jframe interferes with the jpanel on top of it

I have a jframe with a jpanel on top of it and the panel often has to change its contents with repaint() and revalidate(). I have managed to place the images, texts and buttons just the I way I want them to be on this jpanel. Everything is working, but now I am trying to set a background to the jframe, that does not interfere with the contents above it. For example, if there is a drawing of a tree, it should appear behind the text of the jpanel, without disrupting it. I found that the thing that semi works is to use setContentPane() on the jframe, adding a class, that has extended jpanel and has overrode paintComponent(). Everything appears on the screen, but the text is squashed vertically and the elements are moved towards the top of the frame.
If instead of using setContentPane() I just add the background class to the frame, it doesn't appear, no matter the setOpaque() of the jpanel.
I also tried using jLayeredPane, since the things I read on the internet suggest that this is the right answer. However, I couldn't make it work and the background remained hidden.
private final int WIDTH = 1024;
private final int HEIGHT = 768;
Frame()
{
JFrame frame = new JFrame();
panel = new JPanel();
gbc = new GridBagConstraints();
//Unrelated elements
//font = new Font(Font.MONOSPACED, Font.PLAIN, 20);
//border = BorderFactory.createEmptyBorder();
//imageResizer = new ImageResizer();
frame.setTitle("Shady Path");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH, HEIGHT);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setIconImage(new ImageIcon("res/human.png").getImage());
frame.setContentPane(new DrawPanel());
panel.setLayout(new GridBagLayout());
panel.setOpaque(false);
gbc.anchor = GridBagConstraints.PAGE_START;
frame.add(panel);
frame.setVisible(true);
}
//One of the two methods that change the contents of the jpanel
void appendMain(String mainImage, JTextArea mainText, JButton button)
{
panel.removeAll();
image = new JLabel(imageResizer.resize(200, 200, mainImage));
gbc.insets = new Insets(0, 0, 30, 0);
gbc.gridwidth = GridBagConstraints.REMAINDER;
panel.add(image, gbc);
formatText(mainText);
panel.add(mainText, gbc);
button.setFont(font);
button.setForeground(Color.WHITE);
button.setBackground(Color.BLACK);
button.setBorder(border);
gbc.fill = GridBagConstraints.VERTICAL;
gbc.insets = new Insets(50, 0, 70, 0);
panel.add(button, gbc);
panel.revalidate();
panel.repaint();
}
//This is for the text formating
private void formatText(JTextArea baseText)
{
baseText.setEditable(false);
baseText.setForeground(Color.WHITE);
baseText.setFont(font);
baseText.setLineWrap(true);
baseText.setWrapStyleWord(true);
baseText.setMargin(new Insets(0, 300, 0, 300));
baseText.setOpaque(false);
gbc.insets = new Insets(30, 0, 0, 0);
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
}
//The following code is for the paintComponent() class
//The imageResizer is another class that I made, but it just resizes images and it is unrelated.
public class DrawPanel extends JPanel
{
private Image image;
public DrawPanel()
{
ImageResizer imageResizer = new ImageResizer();
ImageIcon imageIcon = imageResizer.resize(1024, 768, "res/test.png");
image = imageIcon.getImage();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
}
Well... It looks like #HovercraftFullOfEels was right with his comment. Literally, I just had to set the layout of the DrawPanel to a BorderLayout and everything was fixed.
public DrawPanel()
{
this.setLayout(new BorderLayout());
ImageResizer imageResizer = new ImageResizer();
ImageIcon imageIcon = imageResizer.resize(1024, 768, "res/test.png");
image = imageIcon.getImage();
}

Java add a JLabel in the middle of a JButton

I would like to simply add a JLabel into a JButton and center it horizontally. I've tried many thing but the text stay left side... Here is my code :
JLabel labeltest = new JLabel("Simple test");
JButton myButton = new JButton();
myButton.setHorizontalTextPosition(SwingConstants.CENTER);
myButton.add(labeltest);
Create an Icon of the text and give the Icon the foreground/background colors that you want.
I tried this but the problem comes when I had to do button.setenabled(false); after this the text becomes almost invisible
You can set the disabled Icon to be the same as the Icon and it will not be painted in the disabled state:
JButton button = new JButton( new ImageIcon(...) );
button.setDisabledIcon( button.getIcon() );
button.setEnabled(false);
Of course the problem with this approach is that the user doesn't know the button is disabled. So in reality you would need 2 icons:
one for the normal state
one for the disabled state
A JButton is also a java.awt.Container. Thus you can set a layout manager. E.g. you can use a GridBagLayout.
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
JToggleButton toggleButton = new JToggleButton();
JLabel jLabel = new JLabel("3");
JToggleButton.ToggleButtonModel toggleButtonModel = (JToggleButton.ToggleButtonModel) toggleButton.getModel()
ToggleForegroundAction toggleForegroundAction =
new ToggleForegroundAction(toggleButtonModel, Color.WHITE, Color.RED);
toggleForegroundAction.setComponent(jLabel);
toggleButton.setAction(toggleForegroundAction);
toggleButton.setLayout(new GridBagLayout());
toggleButton.add(jLabel, new GridBagConstraints());
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.setLayout(new BorderLayout());
panel.add(toggleButton, BorderLayout.CENTER);
Container contentPane = frame.getContentPane();
contentPane.add(panel);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
}
An action that toggles the label's foreground color might look like this
public class ToggleForegroundAction extends AbstractAction {
private JComponent component;
private JToggleButton.ToggleButtonModel toggleButtonModel;
private final Color selectedColor;
private final Color unselectedColor;
public ToggleForegroundAction(JToggleButton.ToggleButtonModel toggleButtonModel, Color selectedColor, Color unselectedColor) {
this.toggleButtonModel = toggleButtonModel;
this.selectedColor = selectedColor;
this.unselectedColor = unselectedColor;
}
public void setComponent(JComponent component) {
this.component = component;
setForeground(component, toggleButtonModel.isSelected());
}
#Override
public void actionPerformed(ActionEvent e) {
JComponent targetComponent = this.component;
if (targetComponent == null) {
targetComponent = (JComponent) e.getSource();
}
setForeground(targetComponent, toggleButtonModel.isSelected());
}
private void setForeground(JComponent targetComponent, boolean isSelected) {
Color foreground;
if (isSelected) {
foreground = selectedColor;
} else {
foreground = unselectedColor;
}
targetComponent.setForeground(foreground);
}
}
=>

How to add 3 rectangles to Jpanel in java?

I'm trying to add three rectangles to the center of BorderLayout and I'm completely lost. My finished program needs to increase the height of the rectangle as the sliders move but I'm trying to figure out how to intitally draw these three rectangles to the jpanel. I'm so lost. My code is below.
import java.awt.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ShowColors extends JPanel
{
public static void main(String args[])
{
JFrame frame = new JFrame();
JPanel main = new JPanel(new BorderLayout());
main.setSize(2000, 1000);
frame.setContentPane(main);
JPanel jp1 = new JPanel(new GridLayout(0, 1));
JPanel jp2 = new JPanel(new GridLayout(2,3));
JPanel jp3 = new JPanel(new GridLayout(1, 3));
jp1.setPreferredSize(new Dimension(90, 800));
jp2.setPreferredSize(new Dimension(1000, 150));
jp3.setPreferredSize(new Dimension(800, 600));
JRadioButton rb1 = new JRadioButton("Decimal", true);
JRadioButton rb2 = new JRadioButton("Binary");
JRadioButton rb3 = new JRadioButton("Hex");
JRadioButton rb4 = new JRadioButton("Octal");
JButton jb1 = new JButton("RESET");
ButtonGroup group = new ButtonGroup();
group.add(rb1);
group.add(rb2);
group.add(rb3);
group.add(rb4);
JSlider jRed = new JSlider(0,255);
JSlider jGreen = new JSlider(0,255);
JSlider jBlue = new JSlider(0,255);
jRed.setPaintLabels(true);
jRed.setPaintTicks(true);
jRed.setMinorTickSpacing(5);
jRed.setMajorTickSpacing(50);
jRed.setValue(0);
jGreen.setPaintLabels(true);
jGreen.setPaintTicks(true);
jGreen.setMinorTickSpacing(5);
jGreen.setMajorTickSpacing(50);
jGreen.setValue(0);
jBlue.setPaintLabels(true);
jBlue.setPaintTicks(true);
jBlue.setMinorTickSpacing(5);
jBlue.setMajorTickSpacing(50);
jBlue.setValue(0);
JLabel labelR = new JLabel("Red", JLabel.CENTER);
JLabel labelG = new JLabel("Green", JLabel.CENTER);
JLabel lableB = new JLabel("Blue", JLabel.CENTER);
jp1.add(rb1);
jp1.add(rb2);
jp1.add(rb3);
jp1.add(rb4);
jp1.add(jb1);
jp2.add(labelR);
jp2.add(labelG);
jp2.add(lableB);
jp2.add(jRed);
jp2.add(jGreen);
jp2.add(jBlue);
main.add(jp1, BorderLayout.WEST);
main.add(jp2, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics g)
{
super.paint(g);
g.drawRect(0, 0, 10, 20);
g.setColor(Color.RED);
g.fillRect(0, 0, 10, 20);
g.drawRect(10, 0, 10, 20);
g.setColor(Color.GREEN);
g.fillRect(10, 0, 10, 20);
g.drawRect(20, 0, 10, 20);
g.setColor(Color.BLUE);
g.fillRect(20, 0, 10, 20);
}
}
here is my layout and i want the rectangles in the center.
I think you just need a simple subclass of JPanel and override paintComponent(). Something like this should get you going:
import javax.swing.*;
import java.awt.*;
public class Canvas extends JPanel {
// TODO member variables for rectangle size/color
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect(10,10,100,50);
g.drawRect(10,80,100,50);
}
}
EDIT:
Actually, I guess you really don't need a "Canvas" class, you can just use a plain JPanel as #MadProgrammer suggests. What you do need is a class that will encapsulate the Rectangle behavior, which can just be a simple JComponent that gets added to the JPanel that holds your three rectangles.
Here's a working solution, imports excluded for brevity:
public class ShowColors {
class Rectangle extends JComponent implements ChangeListener {
private JSlider slider;
private Color color;
public Rectangle(JSlider slider, Color color) {
this.slider = slider;
this.color = color;
this.setPreferredSize(new Dimension(250, 250));
slider.addChangeListener(this);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int value = slider.getValue();
g.setColor(color);
g.fillRect(10,10,100,value);
}
#Override
public void stateChanged(ChangeEvent arg0) {
this.repaint();
}
}
public ShowColors() {
JFrame frame = new JFrame();
JPanel main = new JPanel(new BorderLayout());
main.setSize(2000, 1000);
frame.setContentPane(main);
JPanel jp1 = new JPanel(new GridLayout(0, 1));
JPanel jp2 = new JPanel(new GridLayout(2, 3));
jp1.setPreferredSize(new Dimension(90, 800));
jp2.setPreferredSize(new Dimension(1000, 150));
JRadioButton rb1 = new JRadioButton("Decimal", true);
JRadioButton rb2 = new JRadioButton("Binary");
JRadioButton rb3 = new JRadioButton("Hex");
JRadioButton rb4 = new JRadioButton("Octal");
JButton jb1 = new JButton("RESET");
ButtonGroup group = new ButtonGroup();
group.add(rb1);
group.add(rb2);
group.add(rb3);
group.add(rb4);
JSlider jRed = buildSlider();
JSlider jGreen = buildSlider();
JSlider jBlue = buildSlider();
JLabel labelR = new JLabel("Red", JLabel.CENTER);
JLabel labelG = new JLabel("Green", JLabel.CENTER);
JLabel lableB = new JLabel("Blue", JLabel.CENTER);
jp1.add(rb1);
jp1.add(rb2);
jp1.add(rb3);
jp1.add(rb4);
jp1.add(jb1);
jp2.add(labelR);
jp2.add(labelG);
jp2.add(lableB);
jp2.add(jRed);
jp2.add(jGreen);
jp2.add(jBlue);
JPanel canvas = new JPanel();
canvas.setLayout(new FlowLayout());
canvas.setPreferredSize(new Dimension(800, 600));
canvas.add(new Rectangle(jRed, Color.RED));
canvas.add(new Rectangle(jGreen, Color.GREEN));
canvas.add(new Rectangle(jBlue, Color.BLUE));
main.add(jp1, BorderLayout.WEST);
main.add(jp2, BorderLayout.SOUTH);
main.add(canvas, BorderLayout.EAST);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private static JSlider buildSlider() {
JSlider slider = new JSlider(0, 255);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setMinorTickSpacing(5);
slider.setMajorTickSpacing(50);
slider.setValue(50);
return slider;
}
public static void main(String args[]) {
new ShowColors();
}
}
And here's what it looks like:
Don't override paint, painting in Swing is achieved by a delicate and complicated chain of methods, which is easily broken. Instead, override it's paintComponent method instead. See Painting in AWT and Swing for more details
Having said that, don't add all your components to the same panel onto which you want to draw, you'll end up painting underneth all the components. Instead, create a separate JPanel which acts as the paint surface and another JPanel which acts as the controller (containing the controls and the paint surface). Use setters and getters to change the state of the paint surface. See Performing Custom Painting for more details.
Create some kind of "drawable" object, which knows how to paint itself and what it should use to paint itself (the color)
Create some kind of List (in the paint surface class) which can hold the objects which you want to paint. Within in it's paintComponent you will will loop through the list and request that each object paint itself, passing it the Graphics context. Take a look at 2D Graphics for more details
This previous answer may also help

How to absolutely position a JPanel over another JPanel with setLayout(null) and a background image?

I'm working with JFrames in Java, specifically with absolutely positioned elements that need to overlap. I understand that to overlay components, one should make a JPanel (with setOpacity(false);), and position it with either setBounds(x,y,x2,y2); or setPosition(x,y) & setSize(x,y). Unfortunately the panels act like CSS's inline-divs; they take up only the needed amount of room on their line, and do not stack.
This is the code I have so far, but it doesn't seem to act like I'd imagine it would:
class Login extends JFrame {
private JPanel backgroundpanel;
private JPanel panel;
private JPanel panel2;
private JTextField usernameBox;
private JPasswordField passwordBox;
private JButton button;
private int height = 319;
private int width = 452;
private ImageIcon ii = new ImageIcon("special-window-BG.png");
private JLabel image;
public Login() {
setLayout(null);
setTitle("Login");
setSize(width,height);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
buildPanel();
add(backgroundpanel);
setVisible(true);
}
private void buildPanel() {
usernameBox = new JTextField(20);
passwordBox = new JPasswordField(20);
button = new JButton("Login");
image = new JLabel(ii);
backgroundpanel = new JPanel();
panel = new JPanel();
panel2 = new JPanel();
backgroundpanel.add(panel);
backgroundpanel.add(panel2);
backgroundpanel.add(image);
panel.setBackground(Color.red);
panel.setBounds(0, 0, 10, 10);
panel.setOpaque(false);
panel2.setBackground(Color.blue);
panel2.setBounds(0, 0, 10, 10);
panel2.setOpaque(false);
panel.add(passwordBox);
panel2.add(button);
backgroundpanel.setOpaque(false);
backgroundpanel.isOptimizedDrawingEnabled();
backgroundpanel.setBounds(0, 0, width, height);
...cot'd, however unnecessary.
So basically, I'd like to know how to absolutely position JPanels (or JComponents, if that's simpler) over a JPanel with a background-image.
Thanks for taking a look at this question, I've spent far too much time on this method; the commented-out code extends nearly 500 lines passed what I posted, so I have nowhere else to turn to. The image below shows a crude illustration of what I'm trying to accomplish, I'm not sure if I'actually come close to getting it yet, because sometimes the JComponents seem to disappear as if they're behind the background image, however I'd like to find the simple solution that's most likely right in front of my eyes!
http://i.stack.imgur.com/revz8.jpg
I'd like to find the simple solution that's most likely right in front of my eyes!
Something like this?
import java.awt.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class LoginPanel extends JPanel {
private static final long serialVersionUID = 1L;
BufferedImage image;
LoginPanel(BufferedImage image) {
super(new GridBagLayout());
this.image = image;
JPanel controls = new JPanel(new BorderLayout(15,35));
controls.setOpaque(false);
controls.setBorder(new EmptyBorder(110,0,0,0));
JPanel fields = new JPanel(new GridLayout(0,1,30,30));
fields.setOpaque(false);
controls.add(fields, BorderLayout.CENTER);
fields.add(new JTextField(20));
fields.add(new JPasswordField(20));
JPanel button = new JPanel(new GridBagLayout());
button.setOpaque(false);
controls.add(button, BorderLayout.PAGE_END);
button.add(new JButton("Log In"));
Dimension prefSize = new Dimension(image.getWidth(),image.getHeight());
setPreferredSize(prefSize);
add(controls);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
public static void main(String[] args) throws Exception {
URL url = new URL("http://i.stack.imgur.com/revz8.jpg");
final BufferedImage image = ImageIO.read(url);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
LoginPanel p = new LoginPanel(image);
JOptionPane.showMessageDialog(null, p);
}
});
}
}
You're setting setLayout(null) on your JFrame but not on the "backgroundpanel" (which layout is default set to FlowLayout).
You shouldn't set layout of your Login frame - because it is default set to BorderLayout - and it's ok (you want the "backgroundpanel" to grow to match the parent).
Instead setLayout(null) on your JPanel - "backgroundpanel" - to which you add your arbitrary positioned panels.

Categories

Resources