I am trying to add text to my paint program, but I have some trouble with it.
This is what I want: a person clicks somewhere in the frame, an inputdialog shows up where the person can type in their text and after clicking OK, the text will show up at the clicked location.
First of all I get a copy of the dialog showing up at the left upper corner after I pressed OK. Like this: inputdialog
Second of all: the first time adding text works fine, but the second time the inputdialog shows up twice and I have to fill in the text twice before it shows up on the frame. The third time I have to do it 3 times and so on...
This are parts of my code:
public class MyTexfield implements drawable {
private double x1, y1, x2, y2;
public void MyTextfield() {
}
public void MyTextfield (double x1, double y1, double x2, double y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
#Override
public void draw(Graphics2D g) {
double x = getStartX();
double y = getStartY();
String text = JOptionPane.showInputDialog("Your text here", null);
g.drawString(text, (int)x, (int)y);
}
public class DrawPanel extends JPanel {
Color fillColor = null;
List<drawable> shapesList = new ArrayList<drawable>();
int prevx;
int prevy;
DrawPanel() {
super();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Cast the graphics object to type Graphics2D
Graphics2D g2d = (Graphics2D) g;
for (drawable s : shapesList)
s.draw(g2d);
}
public void addText(int x, int y, int z, int a)
{
MyTexfield textField = new MyTexfield();
shapesList.add(textField);
textField.setCoordinates(x,y,z,a);
repaint();
}
Hope someone can help!
Related
I am trying to create a grid in a JPanel with lines, and to do this, I draw evenly spaced horizontal and vertical lines until I reach the end of the JPanel. I use a class called Drawing which extends JPanel and is the object I add to the window. Below is my code.
public final class Drawing extends JPanel {
public Drawing() {
super();
setBackground(new Color(255, 255, 255));
setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
GroupLayout workspacePanelLayout = new GroupLayout(this);
setLayout(workspacePanelLayout);
workspacePanelLayout.setHorizontalGroup(workspacePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addGap(0, 343, Short.MAX_VALUE));
workspacePanelLayout.setVerticalGroup(workspacePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addGap(0, 400, Short.MAX_VALUE));
initWorkSpace();
}
private static class Line {
final int x1;
final int y1;
final int x2;
final int y2;
final Color color;
public Line(int x1, int y1, int x2, int y2, Color color) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.color = color;
}
}
private final LinkedList<Line> lines = new LinkedList<>();
public void addLine(int x1, int x2, int x3, int x4) {
addLine(x1, x2, x3, x4, Color.black);
}
public void addLine(int x1, int x2, int x3, int x4, Color color) {
lines.add(new Line(x1, x2, x3, x4, color));
repaint();
}
public void clearLines() {
lines.clear();
repaint();
}
#Override
private void paintComponent(Graphics g) {
super.paintComponent(g);
for (Line line : lines) {
g.setColor(line.color);
g.drawLine(line.x1, line.y1, line.x2, line.y2);
}
}
public void initWorkSpace() {
int x = 0;
int y = 0;
while (x < this.getWidth()) {
addLine(x, 0, x, this.getHeight(), new Color(153, 153, 153));
x += getSpacing();
}
while (y < this.getHeight()) {
addLine(0, y, this.getWidth(), y, new Color(153, 153, 153));
y += getSpacing();
}
}
}
The problem is that 'this.getHeight()' and 'this.getWidth()' both return 0 so the grid doesn't get drawn. drawing the lines works fine, its just that the panel apparently has no dimension. How do I solve this.
Not the main problem but you need to override the getPreferredSize() method of your class to return the size.
Each component is responsible for determining its own preferred size.
Then the layout manager can use this information when the panel is added to a parent panel.
Of course this assumes the parent panel is using a layout manager which you should be doing.
For more information and working examples read the section from the Swing tutorial on Custom Painting
The real problem is when you invoke the following method:
initWorkSpace();
All components have a zero size when the component is created. So when the above method is invoked from the constructor the size will always be zero.
If your painting code is dynamic which means it changes as the frame is resized, then you need to invoke that logic inside the paintComponent() method.
Or if your logic is too complex to execute every time the component is repainted, you can add a ComponentListener to the panel and handle the componentResized method and invoke that method.
Also, I'm not sure why you are using a GroupLayout when you are doing custom painting.
I am making a gui program with the mouseListener and mouseMotionListener. I have the following Line class
public class Line {
private int x1, x2, y1, y2;
private Color color;
public Line(int x1, int x2, int y1, int y2, Color color)
{
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
this.color = color;
}
public void draw(Graphics page)
{
page.drawLine(x1, y1, x2, y2);
page.setColor(color);
}
}
here is my mouseReleased where i get the final points of the desired line.
public void mouseReleased (MouseEvent event)
{ // ending points
moving = false;
Point p2 = event.getPoint();
x2 = p2.x;
y2 = p2.y;
line = new Line(x1,x2,y1,y2,currentColor);
lineList.add(line);
canvas.paintComponent(??????????);
Here is the canvas method that should draw all of these lines in the array list "lineList". to the canvas
private class CanvasPanel extends JPanel
{
//this method draws all shapes specified by a user
public void paintComponent(Graphics page)
{
super.paintComponent(page);
setBackground(Color.WHITE);
for(int i = 0; i <lineList.size()-1;i++)
{
line.draw(page);
}
However I do not know how to pass the graphics object to the canvas class in order to actually draw my lines on the JPanel. Assuming i have all other info correct(initial line points, JPanel set correctly, and buttons set up) how do i pass these to actually make it draw the lines to the canvas. Thank you!
No, you don't want to pass a Graphics object anywhere, and in fact you don't paint from within the MouseListener or MouseMotionListener. Instead you change fields from within those classes, call repaint() and then use the field results in your paintComponent method.
In fact in your code, if CanvasPanel has access to the lineList, all you need to do is call repaint() on it after adding a new line into the lineList collection. That's it.
Also, don't set background within paintComponent but rather within the constructor. Also you need to swap your method calls in the Line's draw method. You need to set the color before drawing the line.
e.g.,
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Drawing extends JPanel {
public static final Color BG = Color.WHITE;
public static final Color LINE_COLOR = Color.RED;
public static final Color CURRENT_LINE_COLOR = Color.LIGHT_GRAY;
public static final int PREF_W = 800;
public static final int PREF_H = PREF_W;
private List<Line> lineList = new ArrayList<>();
private Line currentLine = null;
private CanvasPanel canvasPanel = new CanvasPanel();
public Drawing() {
MyMouse myMouse = new MyMouse();
canvasPanel.addMouseListener(myMouse);
canvasPanel.addMouseMotionListener(myMouse);
setLayout(new BorderLayout());
add(canvasPanel);
}
private class CanvasPanel extends JPanel {
public CanvasPanel() {
setBackground(BG);
}
public void paintComponent(Graphics page) {
super.paintComponent(page);
// setBackground(Color.WHITE); // !! no, not here
for (Line line : lineList) {
line.draw(page);
}
if (currentLine != null) {
currentLine.draw(page);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
private class MyMouse extends MouseAdapter {
private int x1;
private int y1;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
x1 = e.getX();
y1 = e.getY();
currentLine = null;
canvasPanel.repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
Line line = createLine(e, LINE_COLOR);
lineList.add(line);
currentLine = null;
canvasPanel.repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
currentLine = createLine(e, CURRENT_LINE_COLOR);
repaint();
}
private Line createLine(MouseEvent e, Color currentColor) {
int x2 = e.getX();
int y2 = e.getY();
return new Line(x1, x2, y1, y2, currentColor);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Drawing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Drawing());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class Line {
private int x1, x2, y1, y2;
private Color color;
public Line(int x1, int x2, int y1, int y2, Color color) {
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
this.color = color;
}
public void draw(Graphics page) {
// swap these calls!
page.setColor(color); //!! This first!
page.drawLine(x1, y1, x2, y2); // **Then** this
// !! page.setColor(color);
}
}
I have a problem in my program with the custom drawing part.
I want the user to click on the interface and then drag while the program draw a line which follow the cursor.
But the problem is, I can barely see it. Also, the line won't stay after the cursor's button release.
Custom draw line code:
public void drawTemporaryLine(int x1,int y1,int x2,int y2,ArrayList<Line> lines){
repaint();
g2d = (Graphics2D) getGraphics();
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.black);
for(Line l:lines){
g2d.drawLine(l.getX1(),l.getY1(),l.getX2(),l.getY2());
}
g2d.drawLine(x1, y1, x2, y2);
}
Mouse listener code:
#Override
public void mousePressed(MouseEvent e){
if(draw_on){
x = e.getX();
y = e.getY();
}
}
#Override
public void mouseDragged(MouseEvent e){
if(draw_on){
drawPanel.drawTemporaryLine(x, y, e.getX(), e.getY(),lines);
}
}
#Override
public void mouseReleased(MouseEvent e){
if(draw_on){
lines.add(new Line(x,y,e.getX(),e.getY()));
optionButtons[0].setSelected(false);
draw_on = false;
}
}
Is there any way to fix it? Thanks.
Try to override paintComponent.
I tried to reproduce it myself:
Try this :)
DrawPanel (extends JPanel)
private ArrayList<Line> lines = new ArrayList<Line>();
private Line tmpLine = null;
public DrawPanel() {
initComponents();
}
public void drawTemporaryLine(int x1, int y1, int x2, int y2) {
tmpLine = new Line(x1, y1, x2, y2);
}
public void setTemporaryLine(int x1, int y1, int x2, int y2) {
lines.add(new Line(x1, y1, x2, y2));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.black);
for (Line l : lines) {
g2d.drawLine(l.getX1(), l.getY1(), l.getX2(), l.getY2());
}
if (tmpLine != null) {
g2d.drawLine(tmpLine.getX1(), tmpLine.getY1(), tmpLine.getX2(), tmpLine.getY2());
}
}
NewJFrame (extends JFrame):
private DrawPanel draw = new DrawPanel();
private int x = 0;
private int y = 0;
public NewJFrame() {
initComponents();
setSize(800,600);
add(draw);
draw.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseReleased(java.awt.event.MouseEvent evt) {
draw.setTemporaryLine(x, y, evt.getX(), evt.getY());
draw.repaint();
}
public void mousePressed(java.awt.event.MouseEvent evt) {
x = evt.getX();
y = evt.getY();
}
});
draw.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
draw.drawTemporaryLine(x, y, evt.getX(), evt.getY());
draw.repaint();
}
});
}
So here is my problem, my professor is having us make a paint program in which we pick an item and draw on it. However, the issue is that he wants us to draw on a buffered image and then have that on top of the JPanel.
As of right now almost everything works, you can pick one of the buttons I have set up, you can draw a rectangle and resize the frame. When you do this the image of the rectangle will not go away and the JPanel will extend. However the buffered image will not extend, you can see in the code that in my paintComponent method I make a buffered image if the grid == null, and if it is then it creates a image the width and height of my board. When I try and add a call to this method any other time it does not resize since the grid is no longer equal to null. I don't know how to call a resize on a buffered image.
Any ideas would be great!
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
import java.awt.event.*;
import java.awt.image.BufferedImage;
/**
*
* #author Calvin Moss
*/
public class Drawing extends JPanel implements MouseListener
{
public int x1, x2 ,y1, y2;
public static Drawing instance;
BufferedImage grid;
static Graphics2D gc;
Drawing()
{
setBackground(Color.RED);
addMouseListener(this);
}
public static Drawing getInstance()
{
if(instance == null)
instance = new Drawing();
return instance;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
int w = Board.getInstance().getWidth();
int h = Board.getInstance().getHeight();
if(grid == null)
{
grid = (BufferedImage)(this.createImage(w,h));
gc = grid.createGraphics();
gc.setColor(Color.BLUE);
BufferedImage grid;
Graphics2D gc;
}
g2.drawImage(grid, null, 0, 0);
check();
}
public void draw()
{
Graphics2D g = (Graphics2D)getGraphics();
int w = x2 - x1;
if (w<0)
w = w *(-1);
int h = y2-y1;
if (h<0)
h= h*(-1);
switch(Main.choice)
{
case 1:
{
System.out.println("double gay");
gc.drawLine(x1, y1, x2, y2);
repaint();
break;
}
case 2:
{
check();
System.out.println("quad gay");
gc.drawRect(x1, y1, w, h);
repaint();
break;
}
}
}
public void check()
{
if (x1 > x2)
{
int z = 0;
z = x1;
x1 = x2;
x2 =z;
}
if (y1 > y2)
{
int z = 0;
z = y1;
y1 = y2;
y2 = z;
}
}
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseClicked(MouseEvent e){
System.out.println("Gay");
}
public void mousePressed(MouseEvent evt)
{
x1 = evt.getX();
y1= evt.getY();
}
public void mouseReleased(MouseEvent evt)
{
x2 = evt.getX();
y2 = evt.getY();
draw();
}
}
Make the BufferedImage the size of the desktop,then you don't have to worry about resizing.
Or, add a ComponentListener to the panel. When the component resizes, create a new BufferedImage to reflect the new panel size. Then draw old buffered image on to the new one. Of course with this approach data will be lost if the panel shrinks and then grows again.
The issue I'm having is issue with is I'm trying to get the paintComponent to draw the circle only when the mouse is clicked, dragged, then let go. However inside my paintPanel class I have to initialize the object I've created (ex. movedCircle myCircle = new movedCircle(0,0,0,0);) just creating the object movedCircle myCircle; gives an error until I actually fully initialize the object with a value.
What I'm looking for:
What's considered the best practice for this issue. I don't want to draw anything unnecessary before it is needed.
The way I know how to fix it:
boolean values inside of paintComponent so that way it doesn't draw until somethings actually there.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class drawCircle extends JFrame{
private JPanel myPanel = new paintPanel();
public drawCircle(){
add(myPanel);
}
private class paintPanel extends JPanel{
private int x1, y1, x2, y2;
movedText myText = new movedText(0,0,0,0);
movedCircle myCircle = new movedCircle(0,0,0,0);
public paintPanel(){
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
x1 = e.getX();
y1 = e.getY();
myCircle = new movedCircle(x1, y1, 0, 0);
repaint();
}
public void mouseReleased(MouseEvent e){
x2 = e.getX();
y2 = e.getY();
myCircle = new movedCircle(x1, y1, x2, y2);
repaint();
}
});
addMouseMotionListener(new MouseMotionAdapter(){
public void mouseDragged(MouseEvent e){
x2 = e.getX();
y2 = e.getY();
myText = new movedText(x1, y1, x2, y2);
myCircle = new movedCircle(x1, y1, x2, y2);
repaint();
}
public void mouseMoved(MouseEvent e){
x1 = e.getX();
y1 = e.getY();
x2 = 0;
y2 = 0;
myText = new movedText(x1, y1, x2, y2);
repaint();
}
});
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
//draw oval after mouse released
myText.paintText(g);
myCircle.paintCircle(g);
}
}
class movedCircle{
private int x1, y1, x2, y2;
public movedCircle(int x1, int y1, int x2, int y2){
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
public void paintCircle(Graphics g){
g.drawOval(x1, y1, x2 - x1, y2 - y1);
}
}
class movedText{
private int x1, y1, x2, y2;
public movedText(int x1, int y1, int x2, int y2){
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
public void paintText(Graphics g){
g.drawString("x1: "+x1+" y1: "+y1+" x2: "+x2+" y2: "+y2, x1, y1);
}
}
class RedSquare{
private int xPos = 50;
private int yPos = 50;
private int width = 20;
private int height = 20;
public void setX(int xPos){
this.xPos = xPos;
}
public int getX(){
return xPos;
}
public void setY(int yPos){
this.yPos = yPos;
}
public int getY(){
return yPos;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
public void paintSquare(Graphics g){
g.setColor(Color.RED);
g.fillRect(xPos,yPos,width,height);
g.setColor(Color.BLACK);
g.drawRect(xPos,yPos,width,height);
}
}
public static void main(String[] args){
JFrame frame = new drawCircle();
frame.setTitle("Is in ellipse? Demo");
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
I would go with the ifs, but the question is where.
you can add them to paintPanel.paintComponent()
you can add them to movedCircle.paint() and do not draw anything if its coordinates are dummy (for instance <0). The same with movedText
Alternatively, position your figures at sensible start place.
(There is one more solution: to subclass movedCircle with nullMovedCircle that doesn't draw anything and create that at first in your paintPanel. However, creating new class for such a little alteration of behaviour seems overkill for me.)
Not sure if I missed the point, but if you don't want to draw something until it is initialized then don't! Can you not just check if it has been created yet rather than creating a new instance that is not in a usable state after the constructor completes? e.g.
super.paintComponent(g);
myText.paintText(g);
if (myCircle != null) {
myCircle.paintCircle(g);
}