MouseListener not giving accurate mouse location - java

I am attempting to have a circle appear on the screen and follow the mouse around. (Eventually I'm going to turn it into a game with ray casting) I am using a MouseMotionListener and am trying to use the mouseMoved method to get accurate mouse location within my JPanel. The problem is though that the farther I move my mouse down the screen, the less accurate it becomes. By the time my mouse gets to the bottom, it is drawing circles about 20 pixels above. It is not a lagging thing because it never catches up, it is always a few pixels above where it should be.
I've tried using different methods that call from MouseEvents and have tried using MousePointerInfo but none work correctly. It does seem to work when I have the JFrame set to undecorated, but obviously that does not look good for a program and thus I want to avoid that.
public class Driver {
public static void main(String[] args) {
JFrame frame = new JFrame("Moonlight");
frame.setSize(700, 700);
frame.setLocation(350, 50);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new MoonlightPanel());
frame.setVisible(true);
}
}
public class Panel extends JPanel {
private BufferedImage myImage;
private Graphics myBuffer;
private Timer t;
public Panel () {
myImage = new BufferedImage(700, 700, BufferedImage.TYPE_INT_RGB);
myBuffer = myImage.getGraphics();
t = new Timer(0, new Listener());
t.start();
addMouseMotionListener(new Mouse());
}
private class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
drawBackground();
/*try {
Point pos = getMousePosition();
myBuffer.setColor(Color.WHITE);
myBuffer.fillOval(pos.x - 10, pos.y - 10, 20, 20);
}
catch(NullPointerException en) {}*/
repaint();
}
}
private class Mouse implements MouseMotionListener {
public void mouseMoved(MouseEvent e) {
Point pos = new Point(e.getX(), e.getY());
System.out.println(pos);
myBuffer.setColor(Color.BLUE);
myBuffer.fillOval(pos.x - 10, pos.y - 10, 20, 20);
}
public void mouseDragged(MouseEvent e) {}
}
public void drawBackground() {
setBackground(Color.BLACK);
}
public void paintComponent(Graphics g) {
g.drawImage(myImage, 0, 0, getWidth(), getHeight(), null);
}
}

Your code is much more complex than it needs to be. The Panel class members are unnecessary. All you need to do is save the mouse location in the mouseMoved() method - in a class member variable - and reference it in the paintComponent() method to draw your blue circle. The below code is a stripped-down version which displays a blue circle that follows the mouse pointer around on the screen.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MoonLite extends JPanel implements MouseMotionListener {
private Point pt;
public MoonLite() {
setBackground(Color.BLACK);
setForeground(Color.BLUE);
addMouseMotionListener(this);
}
public void mouseMoved(MouseEvent e) {
pt = e.getPoint();
repaint();
}
public void mouseDragged(MouseEvent e) {
// Do nothing.
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (pt != null) {
g.fillOval(pt.x - 10, pt.y - 10, 20, 20);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Moonlight");
frame.setSize(700, 700);
frame.setLocation(350, 50);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new MoonLite());
frame.setVisible(true);
}
});
}
}

Related

How do I connect the dots to get a polygon in JAVA?

I'm making an application for creating convex
polygon.
I imagined it to be so that I first set the vertices of the polygon and then create it.
I was able to make the addition of points (vertices). Now I need help connecting the dots with a line.
This is what it looks like:
It looks like this
And I would like when I click the Draw Polygon button that these points connect and it looks like a polygon, like this:
and it should look like this when I click the button
Here's the code so you can run it yourself:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
public class MyPaint{
public static void main(String[] args){
final PadDraw drawPad = new PadDraw();
JFrame frame = new JFrame("Draw Polygon");
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(100, 100, 450, 300);
Container contentPane = frame.getContentPane();
((JComponent) contentPane).setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
frame.setContentPane(contentPane);
JPanel buttonPanel = new JPanel();
buttonPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
contentPane.add(buttonPanel, BorderLayout.SOUTH);
JButton buttonDrawPolygon = new JButton("Draw Polygon");
buttonPanel.add(buttonDrawPolygon);
JButton buttonReset = new JButton("Reset");
buttonPanel.add(buttonReset);
contentPane.add(drawPad, BorderLayout.CENTER);
}
}
class PadDraw extends JComponent{
private Image image;
private Graphics2D graphics2D;
private int currentX , currentY , oldX , oldY ;
public PadDraw(){
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
oldX = e.getX();
oldY = e.getY();
}
});
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
currentX = e.getX();
currentY = e.getY();
if(graphics2D != null) {
graphics2D.drawLine(oldX, oldY, currentX, currentY);
repaint();
oldX = currentX;
oldY = currentY;
}
System.out.println(oldX + " " + oldY);
}
});
}
public void paintComponent(Graphics g){
if(image == null){
image = createImage(getSize().width, getSize().height);
graphics2D = (Graphics2D)image.getGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.setStroke(new BasicStroke(5));
}
g.drawImage(image, 0, 0, null);
}
}
Introduction
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.
Here's your revised GUI
Here's the GUI with four points
Here's the GUI with a polygon
I'm not showing it, but the Reset button clears the drawing area.
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 Swing application at a time.
A Swing application model consists of one or more plain Java getter/setter classes.
A Swing view consists of a JFrame and one or more JPanels.
Swing controllers are the listeners that are attached to JButtons and drawing JPanels.
Model
For this application, I created a PolygonModel class. The class holds a boolean that tells me whether or not to draw the polygon and a java.util.List of java.awt.Point instances. The Point class holds an X and Y int value.
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.
I broke up your main method into a couple of methods. I separate the creation of the JFrame from the creation of the JPanels. This allows me to separate my concerns and focus on one part of the GUI at a time.
The JFrame methods must be called in a specific order. This is the order I use for most of my Swing applications.
I changed your PadDraw class to extend a JPanel. I moved the MouseAdapter code to its own class. Your drawing panel should draw. Period. Nothing else.
The paintComponent method always starts with a call to the super.paintComponent method. This maintains the Swing paint chain and helps to eliminate unwanted drawing artifacts.
The drawing JPanel is cleared before every repaint. Therefore, you have to completely redraw your image each time. That's why we store the List of Point instances in the model.
Controller
I created three controller classes.
The PointListener class extends MouseAdapter. Notice how simple the mousePressed method becomes with an application model.
The two JButtons each have their own ActionListener. Since they are so simple, I made each of them lambdas.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could make them public and post the 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.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
public class PolygonImage implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new PolygonImage());
}
private final PolygonModel model;
private final PadDraw drawPad;
public PolygonImage() {
this.model = new PolygonModel();
this.drawPad = new PadDraw(this, model);
}
#Override
public void run() {
JFrame frame = new JFrame("Draw Polygon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawPad, BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel buttonPanel = new JPanel();
buttonPanel
.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
JButton buttonDrawPolygon = new JButton("Draw Polygon");
buttonDrawPolygon.addActionListener(event -> {
model.setConnectPoints(true);
repaint();
});
buttonPanel.add(buttonDrawPolygon);
JButton buttonReset = new JButton("Reset");
buttonReset.addActionListener(event -> {
model.setConnectPoints(false);
model.clearList();
repaint();
});
buttonPanel.add(buttonReset);
return buttonPanel;
}
public void repaint() {
drawPad.repaint();
}
public class PadDraw extends JPanel {
private static final long serialVersionUID = 1L;
private final PolygonModel model;
public PadDraw(PolygonImage view, PolygonModel model) {
this.model = model;
this.addMouseListener(new PointListener(view, model));
this.setPreferredSize(new Dimension(450, 300));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.black);
// Draw points
for (Point p : model.getPoints()) {
int radius = 6;
int diameter = radius + radius;
g2d.fillOval(p.x - radius, p.y - radius, diameter, diameter);
}
// Draw polygon
if (model.isConnectPoints()) {
g2d.setStroke(new BasicStroke(5));
List<Point> points = model.getPoints();
if (points.size() >= 1) {
Point old = points.get(0);
for (int index = 1; index < points.size(); index++) {
Point p = points.get(index);
g2d.drawLine(old.x, old.y, p.x, p.y);
old = p;
}
Point p = points.get(0);
g2d.drawLine(p.x, p.y, old.x, old.y);
}
}
}
}
public class PointListener extends MouseAdapter {
private final PolygonImage view;
private final PolygonModel model;
public PointListener(PolygonImage view, PolygonModel model) {
this.view = view;
this.model = model;
}
#Override
public void mousePressed(MouseEvent event) {
model.addPoint(event.getPoint());
view.repaint();
}
}
public class PolygonModel {
private boolean connectPoints;
private final List<Point> points;
public PolygonModel() {
this.points = new ArrayList<>();
this.connectPoints = false;
}
public void setConnectPoints(boolean connectPoints) {
this.connectPoints = connectPoints;
}
public boolean isConnectPoints() {
return connectPoints;
}
public void clearList() {
this.points.clear();
}
public void addPoint(Point point) {
this.points.add(point);
}
public List<Point> getPoints() {
return points;
}
}
}

Jcomponent paintcomponent not appearing on the panel

i am making a simple GUI in which small boxes should appear on the Jpanel according to their x,y coordinates. So i in my structure i have got three classes:
1: MyFrame which contains the main JFrame
2: MyPanel extends JPanel
3: Icon extends JComponent
In my MyFrame i want to have a MenuBar through which i can open a file of X,Y coordinates and below the menu bar i want to have the MyPanel which will have all the Icons for each X,Y coordinates. The first problem i have is that the Icon do not appear on MyPanel when i add the Icons.
My code can be seen below:
public class MyFrame {
private JFrame frame;
private MyPanel panel;
public MyFrame(){
panel = new MyPanel();
}
/*
*HERE GOES THE `FILE OPEN` LISTENER
* it creates `Coordinate` for each line
* passes the coordinate to panel.makeIcons()
*/
public void createGui(){
frame = new JFrame("Graph Editor");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(true);
//create, get and set the Jframe menu bar
//createMenuBar() returns a JMenuBar
frame.setJMenuBar(createMenuBar());
Container frame_pane = frame.getContentPane();
panel.setBounds(0, 0, frame.getWidth(), frame.getHeight());
frame_pane.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]){
MyFrame window = new MyFrame();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
window.createGui();
}
});
}
}
Code For the panel for holding icons:
public class MyPanel extends JPanel{
private Set<Icon> points;
public MyPanel(){
setLayout(null);
setPreferredSize(new Dimension(600, 600));
setBackground(Color.YELLOW);
}
//gets called by `FILE OPEN` listener for each coordinate
public void makeIcons(Coordinate obj){
Icon temp = new Icon(obj);
points.add(temp);
//add the temp JComponent to this JPanel
this.add(temp);
}
}
Code for Icon which needs to be shown on the above panel:
public Icon extends JComponent{
private Coordinate location;
public Icon(Coordinate obj){
location = obj;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(location.getX(), location.getY(), 20, 20);
g.setColor(Color.BLACK);
g.drawRect(location.getX(), location.getY(), 20, 20);
}
}
First Problem: The Icons do not show up in the panel with the above code.
Second Problem: When i change the makeIcon method in MyPanel.class to below. It shows the Icons By the MenuBar erases them when the MenuBar appears on any of the icons:
public void makeIcons(Coordinate obj){
Icon temp = new Icon(obj);
points.add(temp);
//add the temp JComponent to this JPanel
this.add(temp);
temp.paintComponent(this.getGraphics());
revalidate();
}
Any help is appreciated. Thanks
Don't call paintComponent (or any paint) method yourself, ever. This is not how painting works.
The core reasons why a component won't be painted are because:
it's size is 0x0
it's invisible
it's not added to a container that is (indirectly) added to a native peer.
Based on my brief observation, I would say you're suffering from point number 1, in part due to the use of a null layout
Also, remember, when painting a component, the component's Graphics context has already been translated so that 0x0 is the top/left corner of the component. This means, based on your code, you'd most likely be painting beyond the visible bounds of the component any way.
So, you basically have two choices. Implement your own layout manager, which uses the Coordinate information to place the Icons with in the container (which would then need to override getPreferredSize in order to provide sizing hints) or, paint it yourself
Which might look something like this...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Graph Editor");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.add(new MyPanel());
frame.pack();
frame.setVisible(true);
}
});
}
public class Coordinate {
private int x;
private int y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
public class Icon {
private Coordinate coordinate;
public Icon(Coordinate coordinate) {
this.coordinate = coordinate;
}
public Coordinate getCoordinate() {
return coordinate;
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, 20, 20);
g2d.setColor(Color.BLACK);
g2d.drawRect(0, 0, 20, 20);
}
}
public class MyPanel extends JPanel {
private Set<Icon> points;
public MyPanel() {
points = new HashSet<>();
setLayout(null);
setPreferredSize(new Dimension(600, 600));
setBackground(Color.YELLOW);
}
//gets called by `FILE OPEN` listener for each coordinate
public void makeIcons(Coordinate obj) {
points.add(new Icon(obj));
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Icon icon : points) {
Graphics2D g2d = (Graphics2D) g.create();
Coordinate coordinate = icon.getCoordinate();
// I'd have a size component associated with the Icon
// which I'd then use to offset the context by half its
// value, so the icon is paint around the center of the point
g2d.translate(coordinate.getX() - 10, coordinate.getY() - 10);
icon.paint(g2d);
g2d.dispose();
}
}
}
}
I've not seeded any values, I'm lazy, but that's the basic idea

Trying to draw lines in java over an image i already drew but i can't get it on top of the image?

I have to draw an archery target with two black lines in the innermost circle that forms a cross, but every time i adjust the lines so that the lines are closer to the centre it goes behind the image instead of appearing on top. How can I stop this? Does it need to have a separate set of instructions entirely?
This is my code:
package sumshapes;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
public class SumShapes extends JFrame
implements ActionListener {
private JPanel panel;
public void paint(Graphics g)
{
g.setColor(Color.BLACK);
g.drawLine(250, 200, 250, 200);
g.drawOval(140,90,200,200);
g.setColor(Color.BLACK);
g.fillOval(140,90,200,200);
g.drawOval(162,109,155,155);
g.setColor(Color.BLUE);
g.fillOval(162,109,155,155);
g.drawOval(183,129,112,112);
g.setColor(Color.RED);
g.fillOval(183, 129, 112, 112);
g.drawOval(210,153,60,60);
g.setColor(Color.YELLOW);
g.fillOval(210, 153, 60, 60);
g.setColor(Color.BLACK);
}
public static void main(String[] args) {
SumShapes frame = new SumShapes();
frame.setSize(500,400);
frame.setBackground(Color.yellow);
frame.createGUI();
frame.setVisible(true);
}
private void createGUI(){
setDefaultCloseOperation(EXIT_ON_CLOSE);
Container window = getContentPane();
window.setLayout (new FlowLayout());
}
public void actionPerformed(ActionEvent event) {
Graphics paper = panel.getGraphics();
paper.drawLine(20,80,120,80);
}
}
All your drawing should go into the paintComponent method of a lightweight component, such as a JPanel.
There should never be a need to call getGraphics. If you wish to change the drawing upon a particular action you should a) program the logic into paintComponent b) alter the logic in the Action c) call repaint on the Component
For example:
private JPanel panel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);//call parent method first thing
//paint here
}
#Override
public Dimension getPreferredSize(){//provided so you can size this component as necessary
return new Dimension(500,400);
}
};
....
frame.add(panel);
frame.pack();
As an aside, I'd recommend placing all calls to to Swing components on the EDT - this means wrapping your Swing calls in the main method with SwingUtilities. eg
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable(){
#Override
public void run() {
SumShapes frame = new SumShapes();
....
}
});
}

Swing Graphics on JFrame

Using Java Graphics, I tried to draw a simple rectangle.
As Applet it runs well but when I use it to show on a JFrame, the rectangle is coming but with some unusual background
Here is coding:
package graphicsex;
import java.awt.Graphics;
public class Graphics2 extends javax.swing.JFrame {
public static void main(String[] args) {
Graphics2 inst = new Graphics2();
inst.setVisible(true);
}
public Graphics2() {
super();
initGUI();
}
private void initGUI() {
try {
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
pack();
setSize(400, 300);
} catch (Exception e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawRect(10, 20, 30, 40);
}
}
Then I tried to use JTextArea using these two classes but in this case the rectangle is not displaying at all.
GraphicsEx1.java:
package graphicsex;
import javax.swing.JTextArea;
import java.awt.Graphics;
public class GraphicsEx1 extends javax.swing.JFrame {
private JTextArea jTextArea1;
{
//Set Look & Feel
try {
javax.swing.UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
GraphicsEx1 inst = new GraphicsEx1();
inst.setVisible(true);
}
public GraphicsEx1() {
super();
initGUI();
}
private void initGUI() {
try {
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
getContentPane().setLayout(null);
{
jTextArea1 = new JTextArea();
getContentPane().add(jTextArea1);
jTextArea1.setBounds(7, 7, 371, 245);
jTextArea1.setEnabled(false);
}
pack();
setSize(400, 300);
} catch (Exception e) {
e.printStackTrace();
}
postInitGUI();
}
public void postInitGUI() {
DisplayItems dp = new DisplayItems();
jTextArea1 = dp;
dp.setVisible(true);
this.add(jTextArea1);
}
}
And DisplayItems.java:
package graphicsex;
import java.awt.Dimension;
import java.awt.Graphics;
public class DisplayItems extends javax.swing.JTextArea {
public DisplayItems() {
super();
initGUI();
}
private void initGUI() {
try {
setPreferredSize(new Dimension(400, 300));
} catch (Exception e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawRect(10, 20, 50, 100);
g.drawString("Kalai", 90, 150);
}
}
Can any one help me display graphics components on any swing containers like JFrame,JPanelorJTextarea` etc.
It is inadvisable to override the paint method of a top level container...
JFrame contains a number of important layers onto which many other components are placed, buy doing this...
public void paint(Graphics g){
g.drawRect(10,20,30,40);
}
You've successfully stopped any of the child components from begin painted, or in fact, anything other then your rectangle.
(The secret life of a top level swing container - Picture taken from How to use Root Panes)
While some might suggest simple adding a call to super.paint(g), I would not recommend it.
Better to use something like the JFrame's glass pane...This will allow you to paint over the components that reside within the frame. If you need to paint under the components, replace the JFrame's content pane instead.
You may find...
Performing Custom Painting
Painting in AWT and Swing
How to Decorate Components with the JLayer Class
Of use...
Update
I think I'm beginning to see you problem with trying to perform custom painting on a text area.
The problem is, paintComponent paints the background AND the text in one foul swoop, meaning that any painting you do before calling super.paintComponent will be rendered over, but any painting done over it will paint over the text.
You could set the text area to be non-opaque and paint the background yourself...
public class CustomTextArea extends JTextArea {
public CustomTextArea() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.fillRect(0, 0, 100, 100);
super.paintComponent(g);
}
}
The problem is though, it's easy for people to rest the opaque level and destroy your work. Sure you could override the setOpaque or getOpaque, but how do you known when the user actually wants to set the component transparent, so you can stop fill the back ground?
* Using jPanel *
using jpanel, the graphics elements can be drawn;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;
public class GraphicsEx1 extends javax.swing.JFrame {
private JScrollPane jsp1;
private JTextArea jta1;
private JPanel jpnl1;
{
//Set Look & Feel
try {
javax.swing.UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel");
} catch(Exception e) {e.printStackTrace();}
}
public static void main(String[] args) {
GraphicsEx1 inst = new GraphicsEx1();
inst.setLocationRelativeTo(null);
inst.setVisible(true);
}
public GraphicsEx1() {
super();
initGUI();
postInitGUI();
}
private void initGUI() {
try {
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
getContentPane().setLayout(null);
{
jsp1 = new JScrollPane();
getContentPane().add(jsp1);
jsp1.setBounds(10, 32, 372, 223);
{
jpnl1 = new JPanel(); //<----------------
jsp1.setViewportView(jpnl1); //<----------------
jpnl1.setBackground(new java.awt.Color(255,255,255));
jpnl1.setLayout(null);
//jpnl1.setPreferredSize(new java.awt.Dimension(359, 327));
}
}
pack();
setSize(400, 300);
} catch (Exception e) {e.printStackTrace();}
}
public void postInitGUI(){
frmGrpView gp=new frmGrpView();
jta1=new JTextArea();
jta1=gp;
jta1.setBounds(0,0, 336, 197);
jta1.setVisible(true);
//jpnl1.setBounds(0, 0, 336, 197);
jpnl1.add(jta1); //<----------------
jpnl1.revalidate();
jsp1.revalidate();
}
}
//----------------------- Second Class --------------------------
class frmGrpView extends JTextArea{
public frmGrpView() {
super();
setEditable(false);
}
public void paint(Graphics g){
super.paint(g);
g.drawRect(10, 10, 100, 100);
}
}

JScrollPane+Canvas: rendering issue

I'm working on a Java version of MS Paint. You can see what it looks like so far here. (images are far too tall and many to embed in a question)
It uses a JScrollPane to move a subclass of Canvas around. If you don't resize the Window, it operates just fine. If you make the window smaller, at first glance it appears to work just the same.
However, if you scroll around, it becomes apparent that the application is rendering the same "viewport", just moved. If you keep scrolling, it becomes more obvious that it overlaps everything else.
So basically, it's rendering the wrong viewport. Resizing the window updates the viewport to be correct. If you try to draw on a grey area, it draws it just fine, you just can't see it.
I've tried doing repaint() on the canvas any time the scrollbars are moved. It didn't change anything.
What should I do to fix this?
This is the code for the frame: (argument img is the image it will paint on. You have to do setVisible(true) yourself as well)
import java.awt.*;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
public class JPaint extends JFrame {
JPaintPanel panel;
public JPaint(BufferedImage img) {
super("Edit your image");
panel = new JPaintPanel(img);
setContentPane(panel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Trial and error got me these numbers.
//I have no idea how find the proper size...
setSize(img.getWidth()+20-1, img.getHeight()+50+50+2);
}
//go to the panel you hold, ask it to retrieve it from the canvas
public BufferedImage grabImage() {
return panel.grabImage();
}
}
class JPaintPanel extends JPanel {
JTools toolbar;
JCanvas canvas;
JScrollPane scrollPane;
public JPaintPanel(BufferedImage img) {
super(new BorderLayout());
toolbar = new JTools();
canvas = new JCanvas(img);
JScrollPane scrollPane = new JScrollPane(canvas);
scrollPane.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
canvas.repaint();
}
});
scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
canvas.repaint();
}
});
setPreferredSize(new Dimension(img.getWidth(),img.getHeight()+50));
add(toolbar, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
}
public BufferedImage grabImage() {
return canvas.getImage();
}
}
class JTools extends JPanel {
JSlider scale;
public JTools() {
scale= new JSlider(JSlider.HORIZONTAL,
0, 400, 100);
scale.setMajorTickSpacing(100);
scale.setPaintTicks(true);
scale.setPaintLabels(true);
scale.setPreferredSize(new Dimension(300,50));
add(scale);
}
}
class JCanvas extends Canvas {
BufferedImage im;
Graphics2D g2d;
Point old = new Point();
Point now = new Point();
public JCanvas(BufferedImage imIn) {
im = imIn;
g2d = im.createGraphics();
setPreferredSize(new Dimension(im.getWidth(), im.getHeight()));
setColor(Color.WHITE);
setWidth(4);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//g2d.drawRect(5, 5, 40, 20);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
//System.out.println(e.getPoint());
//g2d.fillRect(e.getX()-5, e.getY()-5, 10, 10);
old=e.getPoint();
now=e.getPoint();
g2d.drawLine(e.getX(), e.getY(), e.getX(), e.getY());
repaint();
}
});
addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
//System.out.println(e.getPoint());
old=e.getPoint();
now=e.getPoint();
g2d.drawLine(e.getX(), e.getY(), e.getX(), e.getY());
repaint();
}
});
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
//System.out.println(e.getPoint());
//g2d.fillRect(e.getX()-5, e.getY()-5, 10, 10);
old=now;
now=e.getPoint();
g2d.drawLine((int)old.getX(), (int)old.getY(), (int)now.getX(), (int)now.getY());
repaint();
}
});
}
public void paint(Graphics g) {
//super.paint(g);
update(g);
}
public void update(Graphics g) {
//super.update(g);
g.drawImage(im, 0, 0, null);
getToolkit().sync();
}
public void setWidth(float w) {
g2d.setStroke(new BasicStroke(w, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
}
public void setColor(Color c) {
g2d.setColor(c);
}
public BufferedImage getImage() {
return im;
}
}
class JCanvas extends Canvas {
Don't mix Swing with AWT components without good cause. Extend a JComponent instead.

Categories

Resources