I am trying to make a simple Java program with GUI using Java Swing.
I have painting panel (gPanel) in the center of the screen, panel with buttons (buttonSet) in the west and panel with labels (labelPanel) in the east. To paint over gPanel I use paintComponent method and since I have two buttons, which are supposed to draw different things (and change label on the right of the screen), I decided to put switch case in paintComponent method for it to choose the correct actual painting method.
When I run the program everything looks fine - program uses the first method to paint and there is a sampletext.png image shown in the middle of the screen with yellow background, as it should be. Button number 1 also uses this method to draw over gPanel, so pressing it draws the same thing.
Now Button number 2 uses the second painting method and this is where things go wrong. It draws sampleimage.png over the gPanel, but also parts of left and right panels (i.e. buttons from left buttonSet panel and orange colour that is background colour of side panels) are drawn, though it shouldn't happen. Also the whole gPanel becomes gray (I think it happens because of label on the right that becomes very long after pressing Button number 2, because when the label was shorter gPanel didn't turn gray and left the previously drawn things instead).
Pressing Button number 1 paints things from first method properly, so pressing it after pressing Button number 2 "reverts" the changes.
What do I have to do to make my second painting method work properly?
Also why adding border to buttonSet and labelPanel works but adding it to gPanel doesn't?
package com.inferjus.drawingproject;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.border.*;
/**
*
* #author inferjus
*/
public class DrawingProject
{
private JFrame frame;
private graphicPanel gPanel;
private JPanel buttonSet;
private JPanel labelPanel;
private JLabel label;
private int painter=0;
public static void main(String[] args)
{
DrawingProject program=new DrawingProject();
program.prepareGUI();
}
public int getPainter()
{
return painter;
}
public void setPainter(int x)
{
painter=x;
}
public void prepareGUI()
{
//setting JFrame and icon
frame=new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
try { frame.setIconImage(ImageIO.read(getClass().getResource("/resources/sampleicon.png")));}
catch (IOException e) { e.printStackTrace(); }
//border for components
Border bigBlackBorder=new LineBorder(Color.black, 3);
//setting JPanel (graphicPanel) for drawing images
gPanel=new graphicPanel();
gPanel.setBorder(bigBlackBorder); // <--- why it does not work?
//setting JPanel for buttons on the left of the screen
buttonSet=new JPanel();
buttonSet.setLayout(new BoxLayout(buttonSet, BoxLayout.Y_AXIS));
buttonSet.setBorder(bigBlackBorder);
//setting JButtons
JButton buttonOne=new JButton("Button number 1");
buttonOne.addActionListener(new buttonOneListener());
buttonSet.add(buttonOne);
buttonSet.setBackground(Color.orange);
JButton buttonTwo=new JButton("Button number 2");
buttonTwo.addActionListener(new buttonTwoListener());
buttonSet.add(buttonTwo);
//setting JLabels on the right of the screen
label=new JLabel("Default label");
label.setFont(new Font("Consolas", Font.PLAIN, 20));
labelPanel=new JPanel();
labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.Y_AXIS));
labelPanel.setBackground(Color.orange);
labelPanel.setBorder(bigBlackBorder);
JLabel popeLabelTitle=new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font("Consolas", Font.BOLD, 24));
//adding JLabels to labelPanel
labelPanel.add(BorderLayout.NORTH, popeLabelTitle);
labelPanel.add(BorderLayout.CENTER, label);
//adding components to JFrame
frame.getContentPane().add(BorderLayout.CENTER, gPanel);
frame.getContentPane().add(BorderLayout.EAST, labelPanel);
frame.getContentPane().add(BorderLayout.WEST, buttonSet);
frame.setVisible(true);
}
class graphicPanel extends JPanel
{
private BufferedImage sampletext=null;
private BufferedImage sampleimage=null;
#Override
public void paintComponent(Graphics g)
{
//for Button One paint sampletext.png, for Button Two paint sampleimage.png
switch (painter)
{
case 0:
paintSampletext(g);
break;
case 1:
paintSampleimage(g);
break;
}
}
//paint yellow background and put sampletext.png in the middle
private void paintSampletext(Graphics g)
{
if (sampletext==null)
{
gPanel.setSampletextPNG();
}
g.setColor(Color.yellow);
g.fillRect(0,0, gPanel.getWidth(), gPanel.getHeight());
g.drawImage(sampletext, gPanel.getWidth()/2-sampletext.getWidth()/2, gPanel.getHeight()/2-sampletext.getHeight()/2, this);
g.setColor(Color.black);
g.drawRect(gPanel.getWidth()/2-sampletext.getWidth()/2, gPanel.getHeight()/2-sampletext.getHeight()/2, sampletext.getWidth(), sampletext.getHeight());
g.dispose();
}
//paint sampleimage.png over what is already displayed
private void paintSampleimage(Graphics g)
{
if (sampleimage==null)
{
gPanel.setSampleimagePNG();
}
int x=(int)((Math.random()*gPanel.getWidth())-sampleimage.getWidth());
int y=(int)((Math.random()*gPanel.getHeight())-sampleimage.getHeight());
g.drawImage(sampleimage, x, y, gPanel);
g.dispose();
}
public void setSampletextPNG()
{
try { sampletext=ImageIO.read(getClass().getResource("/resources/sampletext.png")); }
catch (IOException ex) { System.out.println("Image error"); }
}
public void setSampleimagePNG()
{
try { sampleimage=ImageIO.read(getClass().getResource("/resources/sampleimage.png")); }
catch (IOException ex) { System.out.println("Image error"); }
}
}
class buttonOneListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
label.setText("Reaction to button number 1: change of label.");
setPainter(0);
gPanel.repaint();
}
}
class buttonTwoListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
label.setText("Reaction to button number 2: change of label + drawing images over gPanel.");
setPainter(1);
gPanel.repaint();
}
}
}
Tree of my project:
DrawingProject
-JRE System Library
-src
--com.inferjus.drawingproject
---DrawingProject.java
--resources
---sampleicon.png
---sampleimage.png
---sampletext.png
what shows after running the program by default or after pressing Button One
what shows after pressing Button Two one time
what shows after pressing Button Two a few times
Introduction
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.
I went ahead and created the following GUI. I created two BufferedImages for the text image and the plain image so I wouldn't have to read any external files.
Explanation
When I create a Swing GUI, I use the model-view-controller pattern. This pattern allows me to separate my concerns and focus on one part of the application at a time.
Model
I created a model class to hold the button flag and the two BufferedImages. This is the class where you would read the resources.
You can add the JFrame icon back to this class.
Model classes are plain Java getter/setter classes.
View
All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
Class names are written in camel case and start with an upper case character. Method names are written in camel case and start with a lower case character. Field names follow the same rules as method names.
I separated the creation of the JFrame from the creation of the JPanels. This helps me to separate my concerns and makes it much easier to visually verify whether or not the code is correct. Aim to write short methods that do one thing and do it well.
You have to manually draw a border on a graphic JPanel. I added the code to your paintComponent method to paint a partial border.
Your paintComponent method should paint. Period. Nothing else. It must also start with a call to the super.paintComponent method to maintain the Swing paint chain.
I changed your JLabel in the right JPanel to a JTextArea. A JTextArea allows for longer messages to word wrap on multiple lines and not make your JFrame change size.
Controller
Your JButton controller classes were fine, except for the class names.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post the code in one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class DrawingProject implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawingProject());
}
private final DrawingModel model;
private GraphicPanel graphicPanel;
private JTextArea textArea;
public DrawingProject() {
this.model = new DrawingModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
graphicPanel = new GraphicPanel(model);
frame.add(createButtonPanel(), BorderLayout.WEST);
frame.add(graphicPanel, BorderLayout.CENTER);
frame.add(createTextPanel(), BorderLayout.EAST);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.orange);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JButton buttonOne = new JButton("Button number 1");
buttonOne.addActionListener(new ButtonOneListener());
panel.add(buttonOne);
JButton buttonTwo = new JButton("Button number 2");
buttonTwo.addActionListener(new ButtonTwoListener());
panel.add(buttonTwo);
return panel;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JLabel popeLabelTitle = new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24));
panel.add(popeLabelTitle, BorderLayout.NORTH);
textArea = new JTextArea(4, 30);
textArea.setEditable(false);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
textArea.setText("Default label");
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
panel.add(textArea, BorderLayout.CENTER);
return panel;
}
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DrawingModel model;
public GraphicPanel(DrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Paint border
int width = getWidth();
int height = getHeight();
int lineThickness = 3;
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.YELLOW);
g.fillRect(0, lineThickness, width, height - 2 * lineThickness);
switch (model.getPainter()) {
case 0:
paintSampleText(g);
break;
case 1:
paintSampleImage(g);
break;
}
}
private void paintSampleText(Graphics g) {
BufferedImage image = model.getSampleText();
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g.drawImage(image, x, y, this);
}
private void paintSampleImage(Graphics g) {
BufferedImage image = model.getSampleImage();
int x = (int) ((Math.random() * getWidth()) - image.getWidth());
int y = (int) ((Math.random() * getHeight()) - image.getHeight());
g.drawImage(image, x, y, this);
}
}
public class ButtonOneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 1: change of label.");
model.setPainter(0);
graphicPanel.repaint();
}
}
public class ButtonTwoListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 2: change of label + "
+ "drawing images over gPanel.");
model.setPainter(1);
graphicPanel.repaint();
}
}
public class DrawingModel {
private int painter;
private final BufferedImage sampleText;
private final BufferedImage sampleImage;
public DrawingModel() {
this.painter = 0;
this.sampleText = createBufferedImage(Color.BLUE);
this.sampleImage = createBufferedImage(Color.MAGENTA);
}
private BufferedImage createBufferedImage(Color color) {
BufferedImage image = new BufferedImage(64, 64,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.dispose();
return image;
}
public int getPainter() {
return painter;
}
public void setPainter(int painter) {
this.painter = painter;
}
public BufferedImage getSampleText() {
return sampleText;
}
public BufferedImage getSampleImage() {
return sampleImage;
}
}
}
Update
In order to paint multiple images, you have to save the origin of the images in a List. I've modified the application model to hold a List of origin Point instances. I also corrected the code to create a random point.
Here's the GUI with multiple images.
Here's the modified code
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class DrawingProject implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawingProject());
}
private final DrawingModel model;
private GraphicPanel graphicPanel;
private JTextArea textArea;
public DrawingProject() {
this.model = new DrawingModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
graphicPanel = new GraphicPanel(model);
frame.add(createButtonPanel(), BorderLayout.WEST);
frame.add(graphicPanel, BorderLayout.CENTER);
frame.add(createTextPanel(), BorderLayout.EAST);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.orange);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JButton buttonOne = new JButton("Button number 1");
buttonOne.addActionListener(new ButtonOneListener());
panel.add(buttonOne);
JButton buttonTwo = new JButton("Button number 2");
buttonTwo.addActionListener(new ButtonTwoListener());
panel.add(buttonTwo);
return panel;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JLabel popeLabelTitle = new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24));
panel.add(popeLabelTitle, BorderLayout.NORTH);
textArea = new JTextArea(4, 30);
textArea.setEditable(false);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
textArea.setText("Default label");
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
panel.add(textArea, BorderLayout.CENTER);
return panel;
}
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DrawingModel model;
public GraphicPanel(DrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paintMyBorder(g);
if (model.getPainter() == 1) {
createSampleImage(g);
}
paintSampleText(g);
BufferedImage image = model.getSampleImage();
List<Point> origin = model.getImageOrigin();
for (Point point : origin) {
g.drawImage(image, point.x, point.y, this);
}
}
private void paintMyBorder(Graphics g) {
int width = getWidth();
int height = getHeight();
int lineThickness = 3;
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.YELLOW);
g.fillRect(0, lineThickness, width, height - 2 * lineThickness);
}
private void paintSampleText(Graphics g) {
BufferedImage image = model.getSampleText();
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g.drawImage(image, x, y, this);
}
private void createSampleImage(Graphics g) {
BufferedImage image = model.getSampleImage();
int x = (int) (Math.random() * (getWidth() - image.getWidth()));
int y = (int) (Math.random() * (getHeight() - image.getHeight()));
model.addNewImageOrigin(new Point(x, y));
}
}
public class ButtonOneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 1: change of label.");
model.setPainter(0);
graphicPanel.repaint();
}
}
public class ButtonTwoListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 2: change of label + "
+ "drawing images over gPanel.");
model.setPainter(1);
graphicPanel.repaint();
}
}
public class DrawingModel {
private int painter;
private final BufferedImage sampleText;
private final BufferedImage sampleImage;
private final List<Point> imageOrigin;
public DrawingModel() {
this.painter = 0;
this.sampleText = createBufferedImage(Color.BLUE);
this.sampleImage = createBufferedImage(Color.MAGENTA);
this.imageOrigin = new ArrayList<>();
}
private BufferedImage createBufferedImage(Color color) {
BufferedImage image = new BufferedImage(64, 64,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.dispose();
return image;
}
public void addNewImageOrigin(Point point) {
this.imageOrigin.add(point);
}
public int getPainter() {
return painter;
}
public void setPainter(int painter) {
this.painter = painter;
}
public BufferedImage getSampleText() {
return sampleText;
}
public BufferedImage getSampleImage() {
return sampleImage;
}
public List<Point> getImageOrigin() {
return imageOrigin;
}
}
}
Related
I am writing a Java Swing application for data processing.
one of the functions I need to add is to visualize the data in a graphical way.
For this I want to use the Graphics2D class.
I have a GUI created, integrated my program and also a panel that draws the graphics using the Graphics2D class.
But my problem is that I can't figure out how to call the drawLine method after selecting and loading a file from the GUI
Below is short code example, showing my issue.
It just contains a basic GUI with 2 panels and a menu with load option to explain my problem:
In de MyFrame.java file, I put a comment at line 87 to show exactly where I am stuck.
The appl is based on 3 files:
main: here it creates an instance of MyFrame of the GUI
Myframe: creates the GUI and further process of data
MyPanel: makes a Jpanel of the Graphics2D with a base blue rectangle frame as start view.
If Anyone could give me a hint on how to call this drawLine method from outside the MyFrame() constructor...
I still don't fully understand the whole point on how to interact between classes...
here is a picture of what the GUI looks:
Thank you for helping me on this
public class main {
public static void main(String[] args) {
new MyFrame();
}
}
MyFrame.java:
public class MyFrame extends JFrame {
JTextComponent tc;
String fileName;
MyFrame() {
this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
this.setLayout(null);
this.setBounds(0,0,464,312);
tc = new JTextPane();
tc.setBounds(0,520,450,50);
tc.setPreferredSize(new Dimension(450,50));
JScrollPane sp = new JScrollPane(tc);
JMenuBar mb = new JMenuBar();
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {tc.setText("loading"+"\n");
SDprocess();});
fm.add(loadItem);
mb.add(fm);
MyPanel p1 = new MyPanel();
p1.setBounds(0,0,450,200);
p1.setPreferredSize(new Dimension(450,200));
JPanel p2 = new JPanel();
p2.setBounds(0,200,450,50);
p2.setPreferredSize(new Dimension(450,50));
p2.add(sp);
this.setJMenuBar(mb);
this.add(p1);
this.add(p2);
this.setResizable(false);
this.setVisible(true);
}
public void SDprocess() {
File fr = null;
JFileChooser fc = new JFileChooser();
int result = fc.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
fr = fc.getSelectedFile();
fileName=fr.getName();
System.out.println(fileName);
}
try {
Scanner sc = new Scanner(fr);
tc.setText(fileName +" loading\n");
while (sc.hasNextLine()) {
String line = sc.nextLine();
// ...
// rest of code to get the x and y data for drawing
// lines using drawLine(x1,y1,x2,y2) method.
//
// at this point I need to call this drawLine method but how ???
// i just don't know how to call this method from this point and how to
// and update the graphics panel p1 after adding the data....
}
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MyPanel.java:
public class MyPanel extends JPanel {
Graphics2D g2D;
MyPanel() {
this.setPreferredSize(new Dimension (450,200));
}
public void paint (Graphics g) {
g2D = (Graphics2D) g;
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
g2D.drawLine(5, 5, 445,5);
g2D.drawLine(445, 5, 445,195);
g2D.drawLine(445, 195, 5,195);
g2D.drawLine(5, 195, 5,5);
}
}
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Netbeans section.
Here's the revised GUI before "reading the file".
Here's the revised GUI after "reading the file".
I created an application model to hold the line segments. This model is passed to the drawing JPanel so that the line segments can be drawn in the paintComponent method of the drawing JPanel.
I cleaned up your GUI. I used Swing layout managers to create the GUI. I separated the creation of the JPanels from the creation of the JFrame so the code is easier for people to read and understand.
Here's the complete runnable code. I made the additional classes inner classes so I could post this code as one block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
public class ExampleDrawingGUI {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new ExampleDrawingGUI().new MyFrame());
}
public class MyFrame extends JFrame {
private static final long serialVersionUID = 1L;
private ExampleDrawingModel model;
JTextComponent tc;
MyPanel p1;
public MyFrame() {
super("My Frame");
this.model = new ExampleDrawingModel();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setJMenuBar(createMenuBar());
p1 = new MyPanel(model);
this.add(p1, BorderLayout.CENTER);
this.add(createTextPanel(), BorderLayout.SOUTH);
this.pack();
this.setLocationByPlatform(true);
// this.setResizable(false);
this.setVisible(true);
}
private JMenuBar createMenuBar() {
JMenuBar mb = new JMenuBar();
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {
tc.setText("loading" + "\n");
model.readFile();
p1.repaint();
});
fm.add(loadItem);
mb.add(fm);
return mb;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
tc = new JTextPane();
tc.setPreferredSize(new Dimension(450, 50));
JScrollPane sp = new JScrollPane(tc);
panel.add(sp, BorderLayout.CENTER);
return panel;
}
public void repaint() {
p1.repaint();
}
}
public class MyPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ExampleDrawingModel model;
public MyPanel(ExampleDrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(450, 200));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
paintBorder(g2D);
for (LineSegment line : model.getLines()) {
Point startPoint = line.getStartPoint();
Point endPoint = line.getEndPoint();
g2D.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
}
}
private void paintBorder(Graphics2D g2D) {
int margin = 5;
int x1 = margin;
int x2 = getWidth() - margin;
int y1 = margin;
int y2 = getHeight() - margin;
g2D.setStroke(new BasicStroke(3f));
g2D.setPaint(Color.blue);
g2D.drawLine(x1, y1, x1, y2);
g2D.drawLine(x1, y1, x2, y1);
g2D.drawLine(x2, y1, x2, y2);
g2D.drawLine(x1, y2, x2, y2);
}
}
public class ExampleDrawingModel {
private List<LineSegment> lines;
public ExampleDrawingModel() {
this.lines = new ArrayList<>();
}
public void readFile() {
this.lines.clear();
// Here's where you'd read a file and create a list of lines.
lines.add(new LineSegment(new Point(100, 100), new Point(100, 150)));
}
public List<LineSegment> getLines() {
return lines;
}
}
public class LineSegment {
private final Point startPoint, endPoint;
public LineSegment(Point startPoint, Point endPoint) {
this.startPoint = startPoint;
this.endPoint = endPoint;
}
public Point getStartPoint() {
return startPoint;
}
public Point getEndPoint() {
return endPoint;
}
}
}
First off, some problems with your code:
class MyFrame extends JFrame {
//....
MyFrame() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null); // !! Good God, no don't do this!
this.setBounds(0, 0, 464, 312); // and don't do this
Avoid null layouts and setBounds like the plague as this makes for very inflexible GUI's that while they might look good on one platform look terrible on most other platforms or screen resolutions and that are very difficult to update and maintain. Instead you will want to study and learn the layout managers and then nest JPanels, each using its own layout manager to create pleasing and complex GUI's that look good on all OS's.
Also, don't forget to call pack() on your JFrame after adding components and before setting visible, in order for the layout managers to do their things.
And then:
class MyPanel extends JPanel {
// ...
Graphics2D g2D; //!! -- no, don't do this
If you create a Graphics or Graphics2D field, you are tempted to use it outside of a painting method, and this is a recipe for disaster since any Graphics obtained from a component is short lived and doing this risks creating a brittle graphic or throwing a NullPointerException
public void paint (Graphics g) {
g2D = (Graphics2D) g;
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
g2D.drawLine(5, 5, 445,5);
g2D.drawLine(445, 5, 445,195);
g2D.drawLine(445, 195, 5,195);
g2D.drawLine(5, 195, 5,5);
}
Don't override paint but rather paintComponent since this is less risky (paint has greater responsibilities that you don't want to mess with) and smoother animations if needed since paintComponent uses double buffering by default.
Also, you almost always should call the super's painting method in your own override, and so instead do:
#Override
protected void paintComponent(Graphics g) {
// first call the super's method:
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g.create();
Now as for your actual problem, creating images and drawing after the GUI has been rendered, probably the easiest way to do this is to create a BufferedImage and draw with it in your GUI. You can do this easily by calling Graphics method drawImage(...). And you can pass a BufferedImage into your drawing JPanel any time it is needed. So for instance, your code could look something like...
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Foo {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MainPanel mainPanel = new MainPanel();
MyMenu myMenu = new MyMenu();
myMenu.setMyPanel(mainPanel.getMyPanel());
myMenu.setMainPanel(mainPanel);
frame.add(mainPanel);
frame.setJMenuBar(myMenu);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
class MainPanel extends JPanel {
private static final int GAP = 5;
private JTextArea textArea = new JTextArea(4, 40);
private MyPanel myPanel = new MyPanel();
public MainPanel() {
textArea.setFocusable(false);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout(GAP, GAP));
add(myPanel);
add(scrollPane, BorderLayout.PAGE_END);
}
public MyPanel getMyPanel() {
return myPanel;
}
public void appendTextAreaText(String text) {
textArea.append(text);
}
public void setBuffImg(BufferedImage bImage) {
myPanel.setBuffImg(bImage);
}
}
class MyMenu extends JMenuBar {
private MainPanel mainPanel;
private MyPanel myPanel;
public MyMenu() {
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {
// Emulate reading file here in a background thread
if (myPanel != null) {
int width = MyPanel.MY_WIDTH;
int height = MyPanel.MY_HEIGHT;
BufferedImage bImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bImg.createGraphics();
// draw with g2 here using data from file
// emulating this:
g2.setColor(Color.RED);
float strokeWidth = (float) (2 + 6 * Math.random());
g2.setStroke(new BasicStroke(strokeWidth));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int x2 = (int) (MyPanel.MY_WIDTH * (1 + Math.random()) / 2);
int y2 = (int) (MyPanel.MY_HEIGHT * (1 + Math.random()) / 2);
g2.drawLine(5, 5, x2, y2);
g2.dispose();
myPanel.setBuffImg(bImg);
if (mainPanel != null) {
mainPanel.appendTextAreaText("adding image \n");
}
}
});
fm.add(loadItem);
add(fm);
}
public void setMyPanel(MyPanel myPanel) {
this.myPanel = myPanel;
}
public void setMainPanel(MainPanel mainPanel) {
this.mainPanel = mainPanel;
}
}
class MyPanel extends JPanel {
private static final int GAP = 5;
public static final int MY_WIDTH = 450;
public static final int MY_HEIGHT = 200;
private BufferedImage bImg = null;
// Graphics2D g2D; //!! -- no, never do this!!
MyPanel() {
// this.setPreferredSize(new Dimension(450, 200));
setBackground(Color.WHITE);
}
// better to override getPreferredSize
#Override
public Dimension getPreferredSize() {
return new Dimension(MY_WIDTH + 2 * GAP, MY_HEIGHT + 2 * GAP);
}
public void setBuffImg(BufferedImage bImg) {
this.bImg = bImg;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
// first call the super's method:
super.paintComponent(g);
if (bImg != null) {
g.drawImage(bImg, GAP, GAP, null);
}
Graphics2D g2D = (Graphics2D) g.create();
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
Rectangle rect = new Rectangle(GAP, GAP, getWidth() - 2 * GAP, getHeight() - 2 * GAP);
g2D.draw(rect);
}
// Don't override paint but rather paintComponent
// public void paint (Graphics g) {
}
I want to put an Image onto another Image using JLabels and ImageIcon. Everything works, but i couldnt find out how to make the Background of the Image transparent. Basically i want to program a Game, but i dont want the player to be a perfect rectangle. So here is my Code which works so far:
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Game {
JFrame frame = new JFrame();
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
public Game() {
frame.setVisible(true);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setTitle("just a test");
frame.setResizable(true);
frame.getContentPane().setLayout(null);
frame.setBounds(400, 200, 915, 600);
label1.setBounds(0, -195, 900, 890);
label2.setBounds(50, 200, 260, 240);
frame.getContentPane().add(label2);
frame.getContentPane().add(label1);
ImageIcon icon1 = new ImageIcon("icons/landscape.png");
ImageIcon icon2 = new ImageIcon("icons/mario1.png");
label1.setIcon(icon1);
label2.setIcon(icon2);
}
public static void main(String[] args) {
Game game = new Game();
}
}
I recommend the very same as Hovercraft Full Of Eels, though I'll explain the reasoning behind it.
According to your cuerrent setup, your JLabel fills in all transparent pixels with an opaque color. It is irrelevant whether mario1.png has a transparent background, as the JLabel in which it's implemented fills in the transparent pixels with the color of its background. There are two potential fixes to this. Use a singular JLabel, as Hovercraft suggests, or use JComponent instead.
I'd recommend using the latter, as if you are programming a game, then you wouldn't want your Mario sprite moving in conjunction to the background, and you have overall more control with the JComponent class in your current circumstances.
Here's the Oracle Doc for the JComponent class:
https://docs.oracle.com/javase/tutorial/uiswing/components/jcomponent.html
I'd be willing to give you simplified sample code, if you so desire.
A JLabel is by nature transparent -- that is its opaque property is by default false (unlike JPanel which is opaque by default), and so if you place a transparent image into an ImageIcon and make this a JLabel's Icon via .setIcon(...) and add the JLabel to a container such as a JPanel, the displayed icon's transparent regions will remain transparent, showing the background image. For example, given this image as a sprite, one that displays a filled circle with transparent pixels around it:
So if a mouse listener is added to the JLabel it can be dragged around the container.
e.g.,
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class SpriteOnBackground extends JPanel {
// Image attribution:
// By Adam Evans - M31, the Andromeda Galaxy (now with h-alpha)
// Uploaded by NotFromUtrecht, CC BY 2.0,
// https://commons.wikimedia.org/w/index.php?curid=12654493
public static final String ANDROMEDA_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/"
+ "thumb/9/98/Andromeda_Galaxy_%28with_h-alpha%29.jpg/"
+ "1280px-Andromeda_Galaxy_%28with_h-alpha%29.jpg";
public static final String SPRITE_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/"
+ "thumb/a/a1/Glossy_3d_blue_blue2.png/"
+ "120px-Glossy_3d_blue_blue2.png";
private Image background;
private JLabel spriteLabel = new JLabel();
public SpriteOnBackground(Image bg, Image spriteImg) {
background = bg;
spriteLabel.setIcon(new ImageIcon(spriteImg));
spriteLabel.setSize(spriteLabel.getPreferredSize());
setLayout(null);
add(spriteLabel);
MyMouse myMouse = new MyMouse();
spriteLabel.addMouseListener(myMouse);
spriteLabel.addMouseMotionListener(myMouse);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || background == null) {
return super.getPreferredSize();
}
// make JPanel the size of the image
int w = background.getWidth(this);
int h = background.getHeight(this);
return new Dimension(w, h);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// draw background image
g.drawImage(background, 0, 0, this);
}
// mouse listener to drag the JLabel around the GUI
private class MyMouse extends MouseAdapter {
private Point p1;
private Point pSprite;
#Override
public void mousePressed(MouseEvent e) {
p1 = e.getLocationOnScreen();
pSprite = spriteLabel.getLocation();
}
#Override
public void mouseDragged(MouseEvent e) {
if (p1 != null) {
moveSprite(e);
}
}
private void moveSprite(MouseEvent e) {
Point p2 = e.getLocationOnScreen();
int x = pSprite.x + p2.x - p1.x;
int y = pSprite.y + p2.y - p1.y;
Point newP = new Point(x, y);
spriteLabel.setLocation(newP);
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (p1 != null) {
moveSprite(e);
}
p1 = null;
}
}
private static void createAndShowGui() {
SpriteOnBackground mainPanel = null;
try {
URL backgroundUrl = new URL(ANDROMEDA_IMAGE);
Image backGroundImg = ImageIO.read(backgroundUrl);
URL spriteUrl = new URL(SPRITE_IMAGE);
Image spriteImg = ImageIO.read(spriteUrl);
mainPanel = new SpriteOnBackground(backGroundImg, spriteImg);
} catch (IOException e) {
e.printStackTrace();
}
JFrame frame = new JFrame("Sprite On Background");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
I am trying to make a simple game using JPanel.
I am using Graphics Draw to display all information, including text, but I need to add user input.
I was thinking about using JTextField with absolute positioning to make it work with what is being drawn, but I have heard that absolute positioning is not a good way set up a JPanel.
Is there a better way to use both graphics paint and JComponents in the same panel?
Solution: use layout managers
Why not simply have the drawing JPanel held by another JPanel, one that uses BorderLayout and held in the BorderLayout.CENTER position. You can then place JTextFields or other control components in the outer JPanel in other positions.
You can also add a layout manager to the drawing JPanel and then add components onto this using the layout. Just remember that if you add any JPanels on top of the drawing JPanel, the added JPanels should be transparent, that is myPanel.setOpaque(false) should be called on them so that the drawing underneath shows through.
For example -- run this program to see what I mean:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class GradientPaintEg extends JPanel {
private DrawingPanel drawingPanel = new DrawingPanel();
private JSlider hue1Slider = new JSlider(0, 100, 0);
private JSlider hue2Slider = new JSlider(0, 100, 0);
public GradientPaintEg() {
Color color = drawingPanel.getColor1();
float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
int value = (int) (hsb[0] * 100);
hue1Slider.setValue(value);
color = drawingPanel.getColor2();
hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
value = (int) (hsb[0] * 100);
hue2Slider.setValue(value);
hue1Slider.setMajorTickSpacing(20);
hue1Slider.setMinorTickSpacing(5);
hue1Slider.setPaintTicks(true);
hue1Slider.setPaintLabels(true);
hue1Slider.setPaintTrack(true);
hue1Slider.addChangeListener(new SliderListener(hue1Slider, drawingPanel, true));
hue1Slider.setOpaque(false);
hue2Slider.setMajorTickSpacing(20);
hue2Slider.setMinorTickSpacing(5);
hue2Slider.setPaintTicks(true);
hue2Slider.setPaintLabels(true);
hue2Slider.setPaintTrack(true);
hue2Slider.addChangeListener(new SliderListener(hue2Slider, drawingPanel, false));
hue2Slider.setOpaque(false);
JPanel sliderPanel = new JPanel(new GridLayout(0, 1, 4, 4));
sliderPanel.add(hue1Slider);
sliderPanel.add(hue2Slider);
sliderPanel.setOpaque(false);
setLayout(new BorderLayout());
// if you want to add the slider panel to the main JPanel:
// add(sliderPanel, BorderLayout.PAGE_START);
add(drawingPanel);
// if you want to add the sliderPanel to the drawing JPanel
drawingPanel.setLayout(new BorderLayout());
drawingPanel.add(sliderPanel, BorderLayout.PAGE_START);
}
private class SliderListener implements ChangeListener {
private JSlider slider;
private DrawingPanel drawingPanel;
private boolean color1Listener;
public SliderListener(JSlider slider, DrawingPanel drawingPanel, boolean color1Listener) {
this.slider = slider;
this.drawingPanel = drawingPanel;
this.color1Listener = color1Listener;
}
#Override
public void stateChanged(ChangeEvent e) {
int value = slider.getValue();
float hue = value / 100f;
Color c = Color.getHSBColor(hue, 1f, 1f);
if (color1Listener) {
drawingPanel.setColor1(c);
} else {
drawingPanel.setColor2(c);
}
}
}
private static void createAndShowGui() {
GradientPaintEg mainPanel = new GradientPaintEg();
JFrame frame = new JFrame("GradientPaintEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class DrawingPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final float X2 = 20;
private static final float Y2 = X2;
private Color color1 = Color.RED;
private Color color2 = Color.BLUE;
public Color getColor1() {
return color1;
}
public void setColor1(Color color1) {
this.color1 = color1;
repaint();
}
public Color getColor2() {
return color2;
}
public void setColor2(Color color2) {
this.color2 = color2;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(new GradientPaint(0, 0, color1, X2, Y2, color2, true));
g2.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
In this code example, I have a JPanel that draws, called class DrawingPanel and use within another main JPanel, the GradientPaintEg class:
public class GradientPaintEg extends JPanel {
private DrawingPanel drawingPanel = new DrawingPanel();
If I want to add components to the DrawingPanel, I first give it a layout, and then add the component(s). For instance, there is a JPanel that holds JSliders called sliderPanel that I add to the DrawingPanel instance using BorderLayout:
drawingPanel.setLayout(new BorderLayout());
drawingPanel.add(sliderPanel, BorderLayout.PAGE_START);
This adds the sliderPanel to the top of the drawingPanel.
But also note that I have to make sliderPanel non-opaque first:
JPanel sliderPanel = new JPanel(new GridLayout(0, 1, 4, 4));
sliderPanel.add(hue1Slider);
sliderPanel.add(hue2Slider);
sliderPanel.setOpaque(false);
I've also made the JSliders themselves non-opaque so that the underlying drawing shows through:
// ......
hue1Slider.setOpaque(false);
// ......
hue2Slider.setOpaque(false);
Here is a basic mcve of combining user input in JTextfield and painting, on a panel using a layout manager:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
class DrawingPanel extends JPanel {
private final JButton update;
private final JTextField input;
private final static int W = 300, H = 350, RADIUS = 100, GAP = 50;
private String text;
public DrawingPanel() {
setPreferredSize(new Dimension(W, H));
setOpaque(false);
setLayout(new BorderLayout());
update = new JButton("Update");
update.addActionListener(e->update());
add(update, BorderLayout.PAGE_START);
input = new JTextField();
add(input, BorderLayout.PAGE_END);
text = "Enter text and press button";
}
private void update() {
text = input.getText();
input.setText("");
repaint();
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponents(g);
final int width = getWidth();
final int height = getHeight();
g.setColor(Color.RED);
g.fillOval(width/2 - RADIUS, height/2 - RADIUS, RADIUS*2, RADIUS*2);
g.setColor(Color.BLUE);
g.drawString(text, height/2 - RADIUS - GAP, GAP);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(()->makeGui());
}
private static void makeGui() {
JFrame frame = new JFrame();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new DrawingPanel());
frame.pack();
frame.setVisible(true);
}
}
I have created a custom jTabbedPane class which extends BasicTabbedPaneUI and have successfully created my desired jTabbedPane but now the problem is that how can I set Hand cursor for each tab in my custom jTabbedPane?
I tried to set cursor with this
tabbedPane.setUI(new CustomMainMenuTabs());
tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
this sets the cursor for whole of jTabbedPane but I want to set the cursor when mouse hovers over any of tab in it only.
How can I set Hand cursor for tabs in my jTabbedPane?
My Code is
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
public class HAAMS
{
//My Custom class for jTabbedPane
public static class CustomMainMenuTabs extends BasicTabbedPaneUI
{
protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)
{
Graphics2D g2 = (Graphics2D) g;
Color color;
if (isSelected) { color = new Color(74, 175, 211); }
else if (getRolloverTab() == tabIndex) { color = new Color(45, 145, 180); }
else {color = new Color(68, 67, 67);}
g2.setPaint(color);
g2.fill(new RoundRectangle2D.Double(x, y, w, h, 30, 30));
g2.fill(new Rectangle2D.Double(x + 100,y,w,h));
}
}
public static void main(String[] args)
{
JFrame MainScreen = new JFrame("Custom JTabbedPane");
MainScreen.setExtendedState(MainScreen.getExtendedState() | JFrame.MAXIMIZED_BOTH);
//Setting UI for my jTabbedPane implementing my custom class CustomMainMenuTabs
JTabbedPane jtpane = new JTabbedPane(2);
jtpane.setUI(new CustomMainMenuTabs());
jtpane.add("1st Tabe", new JPanel());
jtpane.add("2nd Tabe", new JPanel());
jtpane.add("3rd Tabe", new JPanel());
MainScreen.getContentPane().add(jtpane);
MainScreen.setVisible(true);
}
}
How to set cursor to HAND_CURSOR cursor when mouse hovers over any tab only not jpanel or any other component. It would be great if done without a mouse listener.
I see a lot of answers here that are WAY too complicated (custom UIs, extra listeners, Graphics stuff, etc.).
Basically, camickr spelled it out for you. Here's a simple demo:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.*;
public class JTabbedPaneCursorDemo implements Runnable
{
JTabbedPane tabbedPane;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new JTabbedPaneCursorDemo());
}
public void run()
{
JPanel panelA = new JPanel();
JPanel panelB = new JPanel();
tabbedPane = new JTabbedPane();
tabbedPane.addTab("A", panelA);
tabbedPane.addTab("B", panelB);
tabbedPane.addMouseMotionListener(new MouseMotionListener()
{
public void mouseDragged(MouseEvent e) {}
public void mouseMoved(MouseEvent e)
{
adjustCursor(e);
}
});
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
frame.getContentPane().add(tabbedPane, BorderLayout.CENTER);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void adjustCursor(MouseEvent e)
{
TabbedPaneUI ui = tabbedPane.getUI();
int index = ui.tabForCoordinate(tabbedPane, e.getX(), e.getY());
if (index >= 0)
{
tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
}
else
{
tabbedPane.setCursor(null);
}
}
}
I want to set the cursor when mouse moves over any of tab in it.
I would guess you need to add a MouseMotionListener to the tabbed pane. Then when the mouseMoved(...) event is generated you check if the mouse is over a tab.
You should be able to use the tabForCoordinate(...) method of the BasicTabbePaneUI to determine if the mouse is over a tab or not.
Steps:
Create a MouseMotionListener and add it to your JTabbedPane
Inside the listener -> mouseMoved method, chec kif the current position of the mouse is inside the bounds of your tabs
If true, then change the cursor to a hand cursor
else show the default cursor
1.Method to check if the mouse is within the bounds of the tabs:
private static int findTabPaneIndex(Point p, JTabbedPane tabbedPane) {
for (int i = 0; i < tabbedPane.getTabCount(); i++) {
if (tabbedPane.getBoundsAt(i).contains(p.x, p.y)) {
return i;
}
}
return -1;
}
2.The mouse listener:
MouseMotionListener listener = new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
if (findTabPaneIndex(e.getPoint(), tabbedPane) > -1) {
tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
} else {
tabbedPane.setCursor(new Cursor((Cursor.DEFAULT_CURSOR)));
}
}
};
3.To add the listener to the JTabbedPane:
jtpane.addMouseMotionListener(listener);
Related Documentation:
MouseMotionListener
How to Write a Mouse-Motion Listener
The final code:
Putting all the peices together, you get the following:
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
public class HAAMS {
// My Custom class for jTabbedPane
public static class CustomMainMenuTabs extends BasicTabbedPaneUI {
protected void paintTabBackground(Graphics g, int tabPlacement,
int tabIndex, int x, int y, int w, int h, boolean isSelected) {
Graphics2D g2 = (Graphics2D) g;
Color color;
if (isSelected) {
color = new Color(74, 175, 211);
} else if (getRolloverTab() == tabIndex) {
color = new Color(45, 145, 180);
} else {
color = new Color(68, 67, 67);
}
g2.setPaint(color);
g2.fill(new RoundRectangle2D.Double(x, y, w, h, 30, 30));
g2.fill(new Rectangle2D.Double(x + 100, y, w, h));
}
}
public static void main(String[] args) {
JFrame MainScreen = new JFrame("Custom JTabbedPane");
MainScreen.setExtendedState(MainScreen.getExtendedState()
| JFrame.MAXIMIZED_BOTH);
JTabbedPane jtpane = new JTabbedPane(2);
jtpane.setUI(new CustomMainMenuTabs());
MouseMotionListener listener = new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
if (findTabPaneIndex(e.getPoint(), tabbedPane) > -1) {
tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
} else {
tabbedPane.setCursor(new Cursor((Cursor.DEFAULT_CURSOR)));
}
}
};
jtpane.add("1st Tabe", new JPanel());
jtpane.add("2nd Tabe", new JPanel());
jtpane.add("3rd Tabe", new JPanel());
jtpane.addMouseMotionListener(listener);
MainScreen.getContentPane().add(jtpane);
MainScreen.setVisible(true);
}
private static int findTabPaneIndex(Point p, JTabbedPane tabbedPane) {
for (int i = 0; i < tabbedPane.getTabCount(); i++) {
if (tabbedPane.getBoundsAt(i).contains(p.x, p.y)) {
return i;
}
}
return -1;
}
}
You can use:
public void setTabComponentAt(int index,
Component component)
And then you do
component.addMouseListener(yourListener)
I have changed main menthod according to your need that Hand cursor will be visible only on tab header . check if it solve your problem
Working Code
public static void main(String[] args)
{
JFrame MainScreen = new JFrame("Custom JTabbedPane");
MainScreen.setExtendedState(MainScreen.getExtendedState() | JFrame.MAXIMIZED_BOTH);
MouseListener listener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
JTabbedPane jp=(JTabbedPane)(e.getComponent().getParent().getParent());
jp.setSelectedIndex(jp.indexAtLocation(e.getComponent().getX(),e.getComponent().getY()));
}
#Override
public void mouseEntered(MouseEvent e) {
e.getComponent().setCursor(new Cursor((Cursor.HAND_CURSOR)));
}
};
JLabel jlabel1=new JLabel("1st Tabe");
jlabel1.addMouseListener(listener);
JLabel jlabel2=new JLabel("2nd Tabe");
jlabel2.addMouseListener(listener);
JLabel jlabel3=new JLabel("3rd Tabe");
jlabel3.addMouseListener(listener);
//Setting UI for my jTabbedPane implementing my custom class CustomMainMenuTabs
JTabbedPane jtpane = new JTabbedPane(2);
jtpane.setUI(new CustomMainMenuTabs());
jtpane.add("1st Tabe", new JPanel());
jtpane.setTabComponentAt( 0, jlabel1);
jtpane.add("2nd Tabe", new JPanel());
jtpane.setTabComponentAt(1, jlabel2);
jtpane.add("3rd Tabe", new JPanel());
jtpane.setTabComponentAt( 2, jlabel3);
MainScreen.getContentPane().add(jtpane);
MainScreen.setVisible(true);
}
Short
Just add this code to your CustomMainMenuTabs:
public static class CustomMainMenuTabs extends BasicTabbedPaneUI
{
protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)
{
// ...
}
private static final Cursor DEFAULT_CURSOR = Cursor.getDefaultCursor();
private static final Cursor HAND_CURSOR = new Cursor((Cursor.HAND_CURSOR));
protected void setRolloverTab(int index) {
tabPane.setCursor((index != -1) ? HAND_CURSOR : DEFAULT_CURSOR);
super.setRolloverTab(index);
}
}
Explanation
Since you're already extending BasicTabbedPaneUI you can simply extend the mechanics for painting the rollover tab, which is already implemented there without the need of using more listeners or calculating coordinates yourself.
The rolling over is a mechanic that has been present in the component since Java 5 and this is a proper extension, just need to override and extend the method. This method is called whenever the mouse moves in the tab component (it affects the tab area but does not affect the children) and and it's kept updated.
I've tried your code snippet with this addition and worked fine.
It's actually a lot easier than installing a custom UI delegate.
You can install your own labels as the tab components (the components inside the tab handles), which will have their own cursors. Following is a simple example with 3 tabs, and a different cursor for the body of the tabbed pane and each of the tabs:
import java.awt.*;
import javax.swing.*;
public class TestTabCursor extends JFrame {
private JTabbedPane contentPane;
public TestTabCursor() {
super("Test tab cursor");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(640, 480);
setLocation(100, 100);
createContentPane();
setCursors();
}
private void createContentPane() {
contentPane = new JTabbedPane();
addTab(contentPane);
addTab(contentPane);
addTab(contentPane);
setContentPane(contentPane);
}
private void addTab(JTabbedPane tabbedPane) {
int index = tabbedPane.getTabCount() + 1;
JLabel label = new JLabel("Panel #" + index);
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setFont(label.getFont().deriveFont(72f));
JPanel panel = new JPanel(new BorderLayout());
panel.setBackground(Color.white);
panel.add(label, BorderLayout.CENTER);
JLabel title = new JLabel("Tab " + index);
tabbedPane.add(panel);
tabbedPane.setTabComponentAt(index - 1, title);
}
private void setCursors() {
contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
contentPane.getTabComponentAt(0).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
contentPane.getTabComponentAt(1).setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
contentPane.getTabComponentAt(2).setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
}
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new TestTabCursor();
frame.setVisible(true);
}
});
}
}
A bit of context - I am creating a rudimentary implementation of Scrabble and the GUI relies on Java Swing and AWT. The code excerpt below contains the constructor for the Cell class (individual space on the Scrabble board). I am in the proof of concept phase and am testing the addition and removal of a hard-coded letter icon to an individual cell. Each cell is an individual JPanel with a JLabel (which, contains an ImageIcon of the letter). The code looks as though it works without error, BUT every 5-6 additions/removals (via mouse click) causes a class cast exception. The specific exception is:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: Cell cannot be cast to javax.swing.JLabel
I can't see where this exception would be caused, but more specifically why it only occurs after multiple successful additions and removals. Any insight greatly appreciated; I am a beginner to Java GUI.
public class Cell extends JPanel {
/*Tile Colors*/
public static Color twColor = new Color(255, 0, 0);
public static Color dwColor = new Color(255, 153, 255);
public static Color tlColor = new Color(0, 51, 255);
public static Color dlColor = new Color(102, 204, 255);
public static Color defaultColor = new Color(255, 255, 255);
private JLabel selected = null;
private JLabel clicked = null;
private JLabel letterIcon;
private ImageIcon defaultIcon;
private ImageIcon testImg;
public Cell(int xPos, int yPos, int premiumStatus) {
defaultIcon = new ImageIcon ("img/transparent.png");
testImg = new ImageIcon ("img/test.jpg"); // Letter image hard-coded for testing
letterIcon = new JLabel("", defaultIcon, JLabel.CENTER);
add(letterIcon);
letterIcon.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
JLabel clicked = (JLabel) getComponentAt(e.getPoint());
System.out.println(clicked);
if (clicked == null) {
return;
}
if (selected != null) {
selected.removeAll();
selected.revalidate();
selected.setIcon(defaultIcon);
selected.repaint();
System.out.println("Test");
selected = null;
return;
}
if (selected == null) {
selected = clicked;
selected.setIcon(testImg);
selected.revalidate();
selected.repaint();
}
}
});
}
The problem is being cause by calling getComponentAt(e.getPoint()); on the Cell, when the mouse coordinates have already been converted to the coordinate space of the letterIcon.
When a component is clicked, the MouseEvent's point is automatically converted to the coordinate space of the component that the listener is registered to.
In your case, that is the letterIcon. This means that a point at 0x0 is the top/left corner of the letterIcon (despite where it might physically be positioned).
So, calling getComponentAt(e.getPoint()) is ask the Cell to return the component that corresponds to a position which is actually relative only to the letterIcon, which will (in most cases) return the Cell itself.
Instead, you should be simply using MouseEvent#getComponent to return the component that triggered the event, which will be the letterIcon
Update with a simple example
This is a simple example that sets up a JLabel as a mouse target. When the mouse is clicked, both the label and it's parent container will paint a small dot based on the coordinates of the mouse click.
There is the added benefit that the parent container will also translate the click point to it's coordinate space and paint a second dot, which should be in the same click as the labels.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestMouseClicked {
public static void main(String[] args) {
new TestMouseClicked();
}
public TestMouseClicked() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel clickMe;
private Point clickPoint;
public TestPane() {
setLayout(new GridBagLayout());
clickMe = new JLabel("Click me") {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.MAGENTA);
// paintPoint(g, clickPoint);
}
};
add(clickMe);
clickMe.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
clickPoint = e.getPoint();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
paintPoint(g, clickPoint);
if (clickPoint != null) {
g.setColor(Color.BLUE);
// Convert the point from clickMe coordinate space to local coordinate space
paintPoint(g, SwingUtilities.convertPoint(clickMe, clickPoint, this));
}
}
protected void paintPoint(Graphics g, Point clickPoint) {
if (clickPoint != null) {
int size = 4;
g.fillOval(clickPoint.x - size, clickPoint.y - size, size * 2, size * 2);
}
}
}
}