tooltip text erases panel drawing in java - java

I have a JPanel on which some drawing is performed using paintComponent method and after that when ever a user clicks on the JPanel a string is drawn (or any drawing) on it and as the user moves mouse over the JPanel it shows the coordinates in the tooltip of the JPanel.
1) The problem is that when the tooltip comes over the drawn string it erases it but this tooltiptext has no erasing effect on the drawing part which I performed in paintComponent method. I am not able to understand that why this is happening.
2) And also when I draw string on click and then minimize and restore my application my drawn strings are gone.
Hope u all understand what I mean to say.
Here is the code :
#Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D graphics2D = (Graphics2D) graphics;
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawBorder(graphics2D);
drawGrid(graphics2D);
}
private void drawBorder(Graphics2D graphics2D) {
graphics2D.setColor(Color.ORANGE);
graphics2D.setStroke(new BasicStroke(borderStroke));
graphics2D.drawRoundRect(panelStartLoc.x, panelStartLoc.y, panelBorder.width,
panelBorder.height, borderRoundness, borderRoundness);
}
private void drawGrid(Graphics2D graphics2D) {
graphics2D.setColor(Color.ORANGE);
graphics2D.setStroke(new BasicStroke(gridCellStroke));
for (int row = gridStartLoc.x; row < panelBorder.getWidth(); row += cellWidth + cellHorGap) {
for (int col = gridStartLoc.y; col < panelBorder.getHeight(); col += cellHeight + cellVerGap) {
graphics2D.drawRoundRect(row, col, cellWidth, cellHeight, cellRoundness, cellRoundness);
}
}
}
public void drawSubjectAtClickLoc(int subjectCode) {
Color color = getBackground();
String drawString = null;
int subjectDrawXLoc = cellClickLoc.x + 4;
int subjectDrawYLoc = (cellClickLoc.y + cellHeight) - 3;
Graphics2D graphics2D = (Graphics2D) getGraphics();
if (subjectCode == SUBJECT_CLEAR) {
graphics2D.setColor(getBackground());
graphics2D.fillRoundRect(cellClickLoc.x + 2, cellClickLoc.y + 2, cellWidth - 4,
cellHeight - 4, cellRoundness, cellRoundness);
return;
}
if (subjectCode == SUBJECT_HUMAN) {
color = Color.WHITE;
drawString = "H";
}
if (subjectCode == SUBJECT_RESOURCE) {
color = Color.GREEN;
drawString = "R";
}
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.setFont(new Font(null, Font.BOLD, 26));
graphics2D.setColor(color);
graphics2D.drawString(drawString, subjectDrawXLoc, subjectDrawYLoc);
}
thanx in advance....

When your screen is covered, Java calls paintComponent() to fix the screen. If you draw outside of the paintComponent() method, then when the screen is fixed up, your extra drawings will be erased.
So don't do it that way: do all of your drawing in paintComponent(). When the user clicks somewhere, add the string you want to draw and the coordinates to a data structure of some kind (i.e., a list of objects, each object containing a String and some coordinates), then call repaint(). In your paintComponent() method, look in that data structure and draw the strings.

Related

PaintComponent disrupting grid drawing

I'm new to Java as well as user interfaces, and I have a problem with Java Graphics. What I'm trying to achieve is drawing a grid on a JPanel, and then paint custom components into the grid.
Here is the class I want to draw the grid on (its base extends JPanel).
public class RectGridPanel extends GridPanel
{
List<Rectangle> rects;
public RectGridPanel(Simulator sim)
{
super(sim);
this.rects = new ArrayList<Rectangle>();
this.setLayout(new GridLayout(20,20));
for(int x = 1; x < 801; x += 40)
{
for(int y = 2; y < 801; y += 40)
{
Cell newCell = new RectCell(x, y, sim);
this.add(newCell);
}
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.BLACK);
for(int x = 1; x < 801; x += 40)
{
for(int y = 2; y < 801; y += 40)
{
Rectangle rect = new Rectangle(x, y, 40, 40);
g2.draw(rect);
rects.add(rect);
}
}
}
}
Here are the cells I want to draw inside the grid:
public class RectCell extends Cell
{
Rectangle shape;
public RectCell(int x, int y, Simulator sim)
{
super(x, y, sim);
shape = new Rectangle(x, y, CELL_SIZE, CELL_SIZE);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.BLACK);
g2.fill(shape);
}
}
So the grid on its own draws out fine, but as soon as I try to create cells inside it it gets disrupted like that:
public class RectCell extends Cell
{
Rectangle shape;
public RectCell(int x, int y, Simulator sim)
{
super(x, y, sim);
shape = new Rectangle(x, y, CELL_SIZE, CELL_SIZE);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.BLACK);
g2.fill(shape);
}
}
The Graphics object that is passed to this paintComponent() function defines the screen space where the current RectCell can draw. The coordinate sysetem is relative to the upper left corner of the RectCell, so drawing the background should always start at x = 0 and y = 0 or some other meaningful value. The coordinates here are not relative to the parent component as you seem to assume.
Better yet, set the background color and let Swing take care of painting it rather than painting a rectangle yourself.
It is not clear what you try to achive.
What is the reason for RectCell? Why not paint the shapes direct in the RectGridPanel?
The nested loop in the paintComponent it not a good idea. Be aware that the paintComponent is called very often and every time you in crease the rects list with new objects. The former painted rectangles get lost (qraphicaly) and new rectangles with the same parameters are blowing the list.

How can I change the foreground color of a text based on the background it is on?

I have a JCheckbox,this is in a JPanel, that is supposed to show an image in the JPanel, when you select it. The problem is this: as you can see in the screenshot, the text of the JCheckbox is difficult to read because of the image.
I was thinking if there was some way to contrast the text to the image, so that the color of the text is the opposite of the image.
I know that there're other ways to fix it, like setting the JCheckbox outside the image, but I'd have to change design of my program and structure code.
For example, here's how I want it to look:
.
This is all the code that the JCheckBox currently has, it is something simple:
final JCheckBox INFO_IMG = new JCheckBox("Ver img");
INFO_IMG.setFont(new Font("Dialog", 0, 12));
INFO_IMG.setBounds(-2, 2, 78, 13);
INFO_IMG.setOpaque(false);
INFO_IMG.setFocusable(false);
INFO_IMG.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(final ItemEvent ie) {
if (1 == ie.getStateChange()) {
INFO_IMG.setText("Ver info");
IMG.setVisible(true);
/* INFO_IMG.setForegound(ROBOT.getPixelColor(
(int)INFO_IMG.getLocationOnScreen.getX() + 12
,(int) INFO_IMG.getLocationOnScreen().getY() + 10));
This is another way that I had thought of,
although it does not work well,would also have to get
the opposite color from the one return.
*/
} else {
INFO_IMG.setText("Ver img");
IMG.setVisible(false);
}
}
});
add(INFO_IMG);
Well, I have found this way to do it, but unfortunately it only works for black and white, can you think of other ways?
Public Color contrast(Image image) {
BufferedImage buffered = toBufferedImage(image);
int black = 0, white = 0;
for (int x = 0; x < buffered.getWidth(); x++) {
for (int y = 0; y < buffered.getHeight(); y++) {
if(buffered.getRGB(x, y) == Color.BLACK.getRGB()) {
black++;
}else {
white++;
}
}
}
System.out.println("Blanco: " + white + ", Negro: " + black);
return (white > black) ? Color.BLACK : Color.WHITE;
}
private BufferedImage toBufferedImage(Image image) {
int width = image.getWidth(null);
int height = image.getHeight(null);
int argb = BufferedImage.TYPE_BYTE_BINARY;
BufferedImage buffered = new BufferedImage(width, height, argb);
Graphics2D g2 = buffered.createGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
// JOptionPane.showMessageDialog(null, new ImageIcon(buffered));
return buffered;
}

Java Graphics not drawing inside if else or recursion

I'm trying to draw a tree of life sort of thing using an Icon in java and it isn't drawing correctly.
Below is the code that my paintIcon method calls in order to draw out the tree. I later added the code
at the end that is outside of any if statements and that draws correctly as long as it is the paintIcon method that calls draw. The recursive call of draw and any g.drawString or g.drawLine lines inside the if statements do not draw anything. Is there any reason why this would happen? Sorry if this is too much or too little information.
public void draw(Taxon t, Graphics2D g, int xStep, int yStep) {
if(!t.isDrawn()) {
// does special action if drawing a leaf
if(t.getChildren().size() == 0) {
//leaves are all aligned on the right and get drawn in a column
t.setX(width - 100);
t.setY(50 + yStep * leafCount);
g.drawString(t.getName(), t.getX(), t.getY());
leafCount++;
t.setDrawn(true);
System.out.print("leaf");
}
else {
//draws all the children first
for(Taxon c : t.getChildren()) {
draw(c, g, yStep, xStep);
}
//x needs to be x step away from children while y is in the middle
t.setX(t.getChildren().get(0).getX() - xStep);
int average = 0;
for(Taxon c : t.getChildren()) {
average += c.getY();
}
t.setY(average / t.getChildren().size());
//draw connecting lines
for(Taxon c : t.getChildren()) {
g.drawLine(t.getX(), t.getY(), c.getX(), c.getY());
}
//getting info for filling up space behind node text since lines might overlap
FontMetrics fm = g.getFontMetrics();
Rectangle2D r = fm.getStringBounds(t.getName(), g);
//draw the highlight then the text itself
g.setColor(Color.WHITE);
g.fillRect(t.getX(), t.getY() - fm.getAscent(), (int)r.getWidth(), (int)r.getHeight());
g.setColor(Color.BLACK);
g.drawString(t.getName(), t.getX(), t.getY());
t.setDrawn(true);
System.out.print("node");
}
}
FontMetrics fm = g.getFontMetrics();
Rectangle2D r = fm.getStringBounds(t.getName(), g);
g.setColor(Color.WHITE);
g.fillRect(t.getX(), t.getY() - fm.getAscent(), (int)r.getWidth(), (int)r.getHeight());
g.setColor(Color.BLACK);
g.drawString(t.getName(), t.getX(), t.getY());
t.setDrawn(true);
}

How can I remove the white background and how can i add a stick?

I am trying to create a Billiards game for my project and I am having difficulty in adding the stick and the balls at the same time. Also, when I add the balls the background of the JFrame goes white, it is actually supposed to be green (the color of the table).
Your help will be much appreciated.
I tried using Graphics. For example g2d.setBackground(Color.green). Which did not work
public class Practice_Window implements MouseListener, MouseMotionListener, KeyListener {
JFrame Practice_Mode = new JFrame();
Balls myBalls = new Balls();
Stick myStick = new Stick();
public void PracticeWindow()
{
Practice_Mode.setSize(1000, 500);
Practice_Mode.setVisible(true);
Practice_Mode.setResizable(false);
Practice_Mode.setTitle("Practice Mode");
Practice_Mode.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Practice_Mode.getContentPane().setBackground(new Color(0, 1, 0, 0.5f)); //Not visible after i add my balls
Practice_Mode.getRootPane().setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10, Color.GREEN));
Practice_Mode.add(myBalls);
//Practice_Mode.add(myStick);
//JPanel p = new JPanel();
//Timer t= new Timer(10, myBalls);
}
//BALL CLASS
public class Balls extends JPanel implements ActionListener
{
double Size = 35;
double REDxSpeed = 5;
double REDySpeed = 5;
double REDSpeed = 0;
double YELLOWxSpeed = 2;
double YELLOWySpeed = 3;
double YELLOWSpeed = 0;
double WHITExSpeed = 4;
double WHITEySpeed = 2;
double WHITESpeed = 0;
double friction = 0.2;
boolean Collision = false;
Ellipse2D.Double red = new Ellipse2D.Double(200, 200, Size, Size);
Ellipse2D.Double yellow = new Ellipse2D.Double(300, 300, Size, Size);
Ellipse2D.Double white = new Ellipse2D.Double(150, 400, Size, Size);
Timer t = new Timer(10, this);
Balls()
{
t.start();
}
#Override
public void actionPerformed(ActionEvent e) //Things are moving here
{
//RED BALL
red.x += REDxSpeed;
red.y += REDySpeed;
REDSpeed = Math.sqrt(REDxSpeed*REDxSpeed + REDySpeed*REDySpeed);
repaint();
if(red.x < 0 || red.x > getWidth() - red.width)
{
REDxSpeed = -REDxSpeed;
}
if(red.y < 0 || red.y > getHeight() - red.height)
{
REDySpeed = -REDySpeed;
}
//YELLOW BALL
yellow.x += YELLOWxSpeed;
yellow.y += YELLOWySpeed;
YELLOWSpeed = Math.sqrt(YELLOWxSpeed*YELLOWxSpeed + YELLOWySpeed*YELLOWySpeed);
repaint();
if(yellow.x < 0 || yellow.x > getWidth() - yellow.width)
{
YELLOWxSpeed = -YELLOWxSpeed;
}
if(yellow.y < 0 || yellow.y > getHeight() - yellow.height)
{
YELLOWySpeed = -YELLOWySpeed;
}
//WHITE BALL
white.x += WHITExSpeed;
white.y += WHITEySpeed;
WHITESpeed = Math.sqrt(WHITExSpeed*WHITExSpeed + WHITESpeed*WHITEySpeed);
repaint();
if(white.x < 0 || white.x > getWidth() - white.width)
{
WHITExSpeed = -WHITExSpeed;
}
if(white.y < 0 || white.y > getHeight() - white.height)
{
WHITEySpeed = -WHITEySpeed;
}
Collision_Detection();
}
public void paintComponent(Graphics g) //DRAWING MY BALLS
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
//g2d.setBackground(Color.green);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.red);
g2d.fill(red);
g2d.setColor(Color.yellow);
g2d.fill(yellow);
g2d.setColor(Color.black);
g2d.fill(white);
}
//STICK CLASS
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.*;
public class Stick extends JPanel implements MouseListener, MouseMotionListener, ActionListener {
int xLocation = 50;
int yLocation = 50;
int Width = 50;
int Height = 15;
Rectangle2D.Double stick = new Rectangle2D.Double(200, 200, Width, Height);
Timer t = new Timer(10, this);
Stick()
{
t.start();
}
public void PaintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.ORANGE);
g2d.fill(stick);
}
I am having difficulty in adding the stick and the balls at the same time.
So why are you trying to write your entire application at once without doing any testing along the way? Why do you have Timers in the code? Why do you have KeyListeners?
Learn to develop applications one step at a time. Write a little code do some testing. When it works add more functionality. Get the basic right first before adding more complicated logic.
Practice_Mode.add(myBalls);
//Practice_Mode.add(myStick);
The default layout manager of a JFrame is the BorderLayout. By default, when you add a component to the frame (and don't specify a constraint) the component goes to the CENTER. Only a single component can be added to the CENTER so only the last component added will be visible.
Your basic design is wrong. You don't want to have separate panels for the balls and stick. You want to have a single "BilliardsGameBoard" panel that will paint multiple objects. So this panel will paint all the balls and the stick. This way all the objects are managed by the same class.
You should also not be creating individual objects with variable names. This limits the number of object you can control. Instead, keep the objects you want to paint in an ArrayList, then the painting method iterates through the ArrayList and paints each object.
See: get width and height of JPanel outside of the class for a working example of this approach.
I add the balls the background of the JFrame goes white,
Practice_Mode.getContentPane().setBackground(new Color(0, 1, 0, 0.5f));
Don't use alpha values when you specify your color. For green you can just use:
practiceMode.getContentPane().setBackground( Color.GREEN );
Your stick and balls are extending JPanel, which is typically used as a container to group a bunch of JComponents, which are buttons and UI elements. The graphical errors you see is likely Java trying to line up your panels side by side using the default BorderLayout, because it thinks you want panels of buttons and stuff when you are just trying to achieve freeform shapes.
A better approach would be to to render your stick and balls as primitive shapes on a JPanel, rather than as JPanels themselves. This is accomplished by making them implement Shape or at least giving them draw methods; the Primitives tutorial would be of use. This question also has somebody in a somewhat similar situation as yourself, and has answers relevant to you.

understanding difficulties java swing

im trying to paint random (not yet) circles on a JPanel through click on a JMenu.
Im using a JTextField (and have to keep this) for some output.
Here is my Class:
class RandomDrawer extends JPanel implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
//double x = Math.random();
//double y = Math.random();
Random generator = new Random();
int x = generator.nextInt(100)+1;
int y = generator.nextInt(100)+1;
//System.out.printf("x = %d y = %d\n", x, y);
status.setText(String.format("rnd draw x: %d y: %d", x, y));
Graphics2D gg = (Graphics2D) canvas.getGraphics();
gg.setColor(Color.BLACK);
gg.drawOval(50, 50, 50, 50);
}
}
As long i let the line
status.setText(String.format("rnd draw x: %d y: %d", x, y));
stay in there i get nothing drawn. Without it i get my circle, im not sure what the problem is. I cant figure out why nothing is drawn.
Thanks a lot
EDIT:
Hello, i tried to understand the given informations.
Sadly I have to draw using the Graphics2D class, so i guess i can not draw using shapes. So i tried this, sadly it wont draw yet, can u give me some tips?
I tried to create a new class DrawShape, my thought was that i could keep track with those objects.
In my understanding there should be a drawn oval right now
gg.drawOval(100,100,100,100);
Thank you.
class DrawShape {
public DrawShape(String string) {
// TODO Auto-generated constructor stub
}
}
class RandomDrawer extends JPanel implements ActionListener {
/* (non-Javadoc)
* #see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
private List<DrawShape> shapes = new ArrayList<DrawShape>();
public void addShape(DrawShape s) {
shapes.add(s);
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
final Graphics2D gg = (Graphics2D) g;
gg.setColor(Color.BLACK);
gg.drawOval(100, 100, 100, 100);
}
#Override
public void actionPerformed(ActionEvent e) {
Random generator = new Random();
int x = generator.nextInt(100)+100;
int y = generator.nextInt(100)+100;
if (e.getActionCommand()==("Draw RandomCircle")) {
System.out.printf("x = %d y = %d\n", x, y);
status.setText(String.format("rnd draw x:%d y:%d ", x, y));
DrawShape circle = new DrawShape("Circle");
addShape(circle);
int count = shapes.size();
System.out.printf("objects in array: %d\n", count);
}
else if (e.getActionCommand()==("Draw RandomRectangle")) {
System.out.printf("x = %d y = %d\n", x, y);
//status.setText(String.format("rnd draw x: y: "));
//Graphics2D gg = (Graphics2D) canvas.getGraphics();
//gg.setColor(Color.BLACK);
//gg.drawRect(x, y, generator.nextInt(x), generator.nextInt(y));
}
}
}
Painting and such happens event-driven. If a a piece of a component needs to be redrawn its paintComponent method is called.
This means you need a component that nows how to draw by for instance:
public class DrawShape {
public final String text;
public final Color color;
public final Shape shape;
public DrawShape(String text, Color color, Shape shape) {
this.text = text;
this.color = color;
this.shape = shape;
}
}
public class CanvasWithShapes extends JPanel {
private List<DrawShape> shapes = new ArrayList<>();
public void addShape(DrawShape shape) {
shapes.add(shape);
}
#Override
public void paintComponent(Graphics g) {
final Graphics2D gg = (Graphics2D) g;
// Java 8: shapes.stream().forEach((shape) -> gg.draw(shape));
for (DrawShape drawShape : shapes) {
gg.setColor(drawShape.color);
gg.draw(drawShape.shape);
Rectangle bounds = shape.getBounds();
gg.drawString(shape.text, bounds.x+ 10, bounds.y + 20);
}
}
}
And then just add shapes to be redrawn a bit later.
Shape oval = ...;
c.add(oval);
c.repaint(50L); // A bit later
More detailed
A Shape has many implementations of interest like rectangle and oval.
Graphics2D can draw and fill a Shape. So in your case it would be ideal to add such a Shape. Maybe together with color and text. So I took your DrawShape class to hold these properties.
Random generator = new Random();
int x = generator.nextInt(100)+100;
int y = generator.nextInt(100)+100;
if (e.getActionCommand().equals("Draw RandomCircle")) {
System.out.printf("x = %d y = %d\n", x, y);
status.setText(String.format("rnd draw x:%d y:%d ", x, y));
int w = generator.nextInt(100) + 10;
int h = w;
Shape circle = new Ellipse2D.Double(x, y, w, h);
addShape(new DrawShape(text, Color.BLACK, circle));
int count = shapes.size();
System.out.printf("objects in array: %d\n", count);
} else if (e.getActionCommand().equals("Draw RandomRectangle")) {
System.out.printf("x = %d y = %d\n", x, y);
generator.nextInt(y));
int w = generator.nextInt(100) + 10;
int h = generator.nextInt(100) + 10;
Shape rect = Rectangle2D.Double(x, y, w, h)
addShape(new DrawShape(text, Color.BLACK, rect));
}
Graphics2D gg = (Graphics2D) canvas.getGraphics();
Don't use the getGraphics() method to do painting. The painting is temporary. It will be lost if you resize the frame for example.
Instead you need to override the paintComponent() method of your panel.
If you want to paint multiple objects then you need to keep track of each object. Check out Custom Painting Approaches for the two common ways to do this:
keep of List of Objects to paint and then iterate through the List each time the component is repainted.
paint the Object directly to a BufferedImage and then just paint the BufferedImage.
The example paints Rectangles. Basically you need a method like the addRectangle(...) method to add a new object to paint. So every time you click your button you add the new random shape.
Presumably, your problem arises from the setText() invocation modifying the Graphics object in some unexpected way. It is rarely appropriate to use getGraphics() in your own code. Instead, paint with the Graphics that is given to you.
Your approach is anyway flawed. If you manage to draw on a GUI component only once, as you are trying to do, then whatever you have drawn will disappear when the component is next repainted. Repainting can happen for a wide variety of reasons, many of them unrelated to the program's own behavior.
What you need to do is store some kind of data that the component's paintComponent() method will rely upon to do your custom painting every time. It follows that you will need to override the paintComponent() method of the component on which you want the circles to be drawn. For example, you might create a class that records all the needed drawing details for one circle, and give RandomDrawer a List of those objects as a member variable. The action listener manipulates that list appropriately and schedules a repainting, and paintComponent() is overridden to perform the actual painting.

Categories