JScrollPane won't scroll over JPanel - java

I have a JFrame in which I am drawing a lot of images in a large grid. My window is not big enough to contain the entire grid, so I want to use a JScrollPane that will allow me to show certain parts of the grid in the window.
My problem is that I cannot scroll. When I made it so the scrollbars always show up, they appear to already cover the entire area.
As you can see, the scrollbars give the impression that the entire area is shown in the window. However, if I resize it, you'll notice that is not the case at all!
And the scrollbars continue to say they cover the entire area.
(Note that the numbers in the grid are actually 16 by 16 px images but in order to make a Minimal, Complete, and Verifiable example, I replaced the images with coordinate strings.)
Here is my code for the Minimal, Complete and Verifiable example:
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main {
public static int[][] tiles;
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
}
class Foo {
public void run(){
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Panel panel = new Panel();
panel.setPreferredSize(new Dimension(512,448));
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
frame.add(scrollPane);
frame.setVisible(true);
frame.pack();
Main.tiles = new int[32][32];
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
panel.repaint();
}
}
}
class Panel extends JPanel {
public void paintComponent(Graphics g){
paintScreen(g);
}
private void paintScreen(Graphics g){
int x =0, y =0;
for (int i = 0; i < Main.tiles.length; i++) {
for (int j = 0; j < Main.tiles[i].length; j++){
g.drawString(i + " " + j, x*32, y*32);
y++;
}
y=0;
x++;
}
}
}
So my question is:
Q: Why is the JScrollPane not allowing me to scroll over the JPanel?

Your problem looks to be here:
panel.setPreferredSize(new Dimension(512,448));
where you constrain the JPanel held by the JScrollPane to a specific size, not allowing it to expand. Never do this. Instead constrain the JScrollPane or its view port.
e.g.,
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main {
public static int[][] tiles;
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
}
class Foo {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Panel panel = new Panel();
// !! panel.setPreferredSize(new Dimension(512,448));
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.getViewport().setPreferredSize(new Dimension(512, 448));
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
frame.add(scrollPane);
frame.setVisible(true);
frame.pack();
Main.tiles = new int[32][32];
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
panel.repaint();
}
}
}
class Panel extends JPanel {
private static final int PREF_W = 1200;
private static final int PREF_H = 1200;
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintScreen(g);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private void paintScreen(Graphics g) {
int x = 0, y = 0;
for (int i = 0; i < Main.tiles.length; i++) {
for (int j = 0; j < Main.tiles[i].length; j++) {
g.drawString(i + " " + j, x * 32, y * 32);
y++;
}
y = 0;
x++;
}
}
}
Edit
Attempt at improving version based on MadProgrammer's recommendation:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
public class Main {
public static int[][] tiles;
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
}
class Foo {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
NumberPanel panel = new NumberPanel();
// !! panel.setPreferredSize(new Dimension(512,448));
JScrollPane scrollPane = new JScrollPane(panel);
// !! scrollPane.getViewport().setPreferredSize(new Dimension(512, 448));
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
frame.add(scrollPane);
frame.setVisible(true);
frame.pack();
Main.tiles = new int[32][32];
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
panel.repaint();
}
}
}
class NumberPanel extends JPanel implements Scrollable {
private static final int PREF_W = 1200;
private static final int PREF_H = 1200;
private static final int VP_WIDTH = 512;
private static final int VP_HEIGHT = 448;
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintScreen(g);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private void paintScreen(Graphics g) {
int x = 0, y = 0;
for (int i = 0; i < Main.tiles.length; i++) {
for (int j = 0; j < Main.tiles[i].length; j++) {
g.drawString(i + " " + j, x * 32, y * 32);
y++;
}
y = 0;
x++;
}
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(VP_WIDTH, VP_HEIGHT);
}
#Override
public int getScrollableBlockIncrement(Rectangle arg0, int arg1, int arg2) {
// TODO Consider improving
return 0;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return false;
}
#Override
public int getScrollableUnitIncrement(Rectangle arg0, int arg1, int arg2) {
// TODO Consider improving
return 0;
}
}

Related

paintComponent not called when I scroll both Horizontal and Vertical scrollbars in a JScrollPane

I am having trouble with Swing and the JScrollPane.
I am having a strange behaviour.
I extended JScrollPane. I display an image in it and draw rectangles over it to define areas.
With a big image, I have an Horizontal and a Vertical scrollbars.
I - ok - When I move one scrollbar or the other I see my image move too as it should.
II - not ok - When I move one scrollbar an leave it in between max and min position, then when I move my second scrollbar my image disappears.
With some debug prints, I found out that paintComponent, is not called when in case II.
I would like to know why it is not calling paintComponent and how I can fix it.
Here below is my class:
package GUI;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import javax.swing.JScrollPane;
public class DrawingPanel extends JScrollPane {
private static final long serialVersionUID = 1L;
private static final Color DRAWING_COLOR = new Color(255, 100, 200);
private static final Color FINAL_DRAWING_COLOR = Color.red;
private static final double ZOOMING_STEP = 1.1;
private Image sImg;
private Point startPt;
private Point endPt;
private Point currentPt;
private int prefW;
private int prefH;
private double zoomFactor = 1;
private boolean zoomer = false;
private boolean loaded = false;
public DrawingPanel() {
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void loadImage(Image img) {
sImg = img;
prefW = sImg.getWidth(null);
prefH = sImg.getHeight(null);
zoomFactor = getSize().getWidth() / prefW;
zoomer = true;
loaded = true;
repaint();
revalidate();
}
int countPaint = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("paintComponent " + countPaint);
if (loaded) {
int zoomWidth = (int) (prefW * zoomFactor);
int zoomHeight = (int) (prefH * zoomFactor);
if (zoomer) {
((Graphics2D) g).scale(zoomFactor, zoomFactor);
setSize(zoomWidth, zoomHeight);
zoomer = false;
}
g.drawImage(sImg, 0, 0, zoomWidth, zoomHeight, null);
drawRectangle(g);
}
g.dispose();
countPaint++;
}
#Override
public Dimension getPreferredSize() {
return loaded ?
this.getSize() :
new Dimension((int) (prefW*zoomFactor), (int) (prefH*zoomFactor));
}
private void drawRectangle(Graphics g) {
Point secondPoint = (currentPt != null) ? currentPt : endPt;
Color color = (currentPt != null) ? DRAWING_COLOR : FINAL_DRAWING_COLOR;
if (startPt!=null && secondPoint!=null) {
int x = Math.min(startPt.x, secondPoint.x);
int y = Math.min(startPt.y, secondPoint.y);
int rectangleWidth = Math.abs(startPt.x - secondPoint.x);
int rectangleHeight = Math.abs(startPt.y - secondPoint.y);
g.setColor(color);
g.drawRect(x, y, rectangleWidth, rectangleHeight);
}
}
public void deleteRectangle(){
startPt = null;
endPt = null;
}
public void increaseZoom(Point p) {
double oldZoom = zoomFactor;
zoomFactor *= ZOOMING_STEP;
repositonPointAfterZoom(oldZoom, zoomFactor);
}
public void decreaseZoom(Point p) {
double oldZoom = zoomFactor;
zoomFactor /= ZOOMING_STEP;
repositonPointAfterZoom(oldZoom, zoomFactor);
}
public void repositonPointAfterZoom(double oldZoom, double newZoom) {
double evolution = newZoom/oldZoom;
if (startPt!=null) {
startPt.setLocation(startPt.x * evolution, startPt.y * evolution);
}
if (endPt!=null) {
endPt.setLocation(endPt.x * evolution, endPt.y * evolution);
}
repaint();
}
// Getter et setter
public void setStartPt(Point startPt) {
this.startPt = startPt;
}
public void setEndPt(Point endPt) {
this.endPt = endPt;
}
public void setCurrentPt(Point currentPt) {
this.currentPt = currentPt;
}
public int getZoomCalculateX(int value){
return (int) (value / zoomFactor);
}
public int getZoomCalculateY(int value){
return (int) (value / zoomFactor);
}
public void setZoomer(boolean zoomer) {
this.zoomer = zoomer;
}
}
EDIT : Bellow is the class (simplified) that uses DrawingPanel so you can have a reproducible exemple.
package GUI;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import fileHandler.*;
public class GUI {
private JFrame frame;
private MenuBar menubar;
private DrawingPanel panelImage;
private JScrollPane scroll;
private GroundTruth openFile;
private int[] panelImageDown = new int[2];
private int[] panelImageUp = new int[2];
private Menu CoordinateMenu1 = new Menu();
private Menu CoordinateMenu2 = new Menu();
private int actualPagePdf;
private PDFRenderer renderer;
public static void main(String[] args) throws IOException {
new GUI();
}
public GUI() throws IOException {
JFrame frame = CreateFrame();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JFrame CreateFrame() throws IOException {
frame = new JFrame();
frame.setMenuBar(CreateMenuBar());
frame.setContentPane(SplitScreen());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("GTA - Ground Truth Annotator");
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
return frame;
}
private MenuBar CreateMenuBar() {
menubar = new MenuBar();
menubar.add(CreateFileMenu());
menubar.add(new Menu("Selection coordinates:"));
menubar.add(CoordinateMenu1);
menubar.add(new Menu("Width/Height:"));
menubar.add(CoordinateMenu2);
return menubar;
}
private Menu CreateFileMenu() {
Menu mFile = new Menu("File");
MenuItem miOpenImage = new MenuItem("Open Image/PDF File");
mFile.add(miOpenImage);
miOpenImage.addActionListener(OpenFileActionListener);
mFile.addSeparator();
MenuItem miExit = new MenuItem("Exit Program");
mFile.add(miExit);
miExit.addActionListener(ExitActionListener);
return mFile;
}
private JPanel SplitScreen() throws IOException {
JPanel splittedScreen = new JPanel(new GridLayout(1, 2));
splittedScreen.add(CreateLeftPanel());
splittedScreen.add(CreateRightPanel());
return splittedScreen;
}
private JLayeredPane CreateLeftPanel() throws IOException {
JLayeredPane panel = new JLayeredPane();
panel.setLayout(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.gray));
panel.add(CreateImageScrollPane());
return panel;
}
private JScrollPane CreateImageScrollPane() throws IOException {
scroll = new JScrollPane(CreateImagePanel((String) null));
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.getViewport().setScrollMode(JViewport.BLIT_SCROLL_MODE);
return scroll;
}
private DrawingPanel CreateImagePanel(String path) throws IOException {
if (panelImage == null) {
panelImage = new DrawingPanel();
}
if (path != null) {
panelImage.loadImage(ImageIO.read(new File(path)));
}
panelImage.addMouseListener(PanelImageMouseListener);
panelImage.addMouseWheelListener(PanelImageMouseWheelListener);
panelImage.addMouseMotionListener(PanelImageMouseMotionAdapter);
panelImage.setOpaque(false);
panelImage.revalidate();
panelImage.repaint();
panelImage.requestFocus();
return panelImage;
}
private DrawingPanel CreateImagePanel(Image image) throws IOException {
if (panelImage == null) {
panelImage = new DrawingPanel();
}
panelImage.loadImage(image);
panelImage.addMouseListener(PanelImageMouseListener);
panelImage.addMouseWheelListener(PanelImageMouseWheelListener);
panelImage.addMouseMotionListener(PanelImageMouseMotionAdapter);
panelImage.setOpaque(false);
panelImage.revalidate();
panelImage.repaint();
return panelImage;
}
private JPanel CreateRightPanel() {
JPanel panel = new JPanel(new GridLayout(0, 1));
//...
return panel;
}
ActionListener OpenFileActionListener = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
OpenFile();
}
};
ActionListener ExitActionListener = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Object[] options = {"Yes, quit now", "No, go back"};
int n = JOptionPane.showOptionDialog(
frame, "ATTENTION: closing without saving will cause any unsaved files to be lost. Do you want to proceed?",
"Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]
);
switch (n) {
case JOptionPane.YES_OPTION:
System.exit(0);
case JOptionPane.NO_OPTION:
case JOptionPane.CANCEL_OPTION:
}
}
};
MouseListener PanelImageMouseListener = new MouseListener() {
public void mousePressed(MouseEvent me) {
panelImageDown = new int[]{
panelImage.getZoomCalculateX(me.getX()), panelImage.getZoomCalculateY(me.getY())
};
panelImageUp = null;
CoordinateMenu1.setLabel(String.format("%s:%s", panelImageDown[0], panelImageDown[1]));
CoordinateMenu2.setLabel("");
panelImage.setStartPt(me.getPoint());
panelImage.repaint();
}
public void mouseReleased(MouseEvent me) {
panelImageUp = new int[]{
Math.abs(panelImage.getZoomCalculateX(me.getX()) - panelImageDown[0]),
Math.abs(panelImageDown[1] - panelImage.getZoomCalculateY(me.getY()))
};
CoordinateMenu2.setLabel(String.format("%s:%s", panelImageUp[0], panelImageUp[1]));
panelImage.setEndPt(me.getPoint());
panelImage.setCurrentPt(null);
panelImage.repaint();
}
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
};
MouseMotionAdapter PanelImageMouseMotionAdapter = new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me) {
panelImageUp = new int[]{
Math.abs(panelImage.getZoomCalculateX(me.getX()) - panelImageDown[0]),
Math.abs(panelImageDown[1] - panelImage.getZoomCalculateY(me.getY()))
};
CoordinateMenu2.setLabel(String.format("%s:%s", panelImageUp[0], panelImageUp[1]));
panelImage.setCurrentPt(me.getPoint());
panelImage.repaint();
}
};
MouseWheelListener PanelImageMouseWheelListener = new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent me) {
if (me.isAltDown()) {
if (me.getWheelRotation() < 0) {
panelImage.setZoomer(true);
panelImage.increaseZoom();
panelImage.repaint();
panelImage.requestFocus();
//scroll.repaint();
//Zoom out
} else if(me.getWheelRotation() > 0) {
panelImage.setZoomer(true);
panelImage.decreaseZoom();
panelImage.repaint();
panelImage.requestFocus();
//scroll.repaint();
}
}
}
};
private void OpenFile() {
openFile = new GroundTruth();
JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false);
fileChooser.addChoosableFileFilter(new FileNameExtensionFilter(
"Images / PDF Scan",
"bmp", "jpeg", "jpg", "png", "tif", "tiff", "pdf"
));
if (fileChooser.showOpenDialog(frame) != 0) {
return;
}
openFile.setFilename(fileChooser.getSelectedFile().getAbsolutePath());
if (getExtension(fileChooser.getSelectedFile().getName()).equals("pdf")) {
try {
PDDocument doc = PDDocument.load(fileChooser.getSelectedFile());
numberPagePdf = doc.getNumberOfPages();
actualPagePdf = 0;
renderer = new PDFRenderer(doc);
setPdfPage(actualPagePdf);
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
CreateImagePanel(fileChooser.getSelectedFile().getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void setPdfPage(int pageNumber){
try {
BufferedImage bim = renderer.renderImageWithDPI(pageNumber, 300);
refreshInfoPageSection();
CreateImagePanel(bim);
} catch (IOException e) {
e.printStackTrace();
}
}
// refresh the label who indicate the page display and set visible or inivisble the previous and next button
private void refreshInfoPageSection(){
panelImage.deleteRectangle();
}
public String getExtension(String filename) {
return FilenameUtils.getExtension(filename);
}
}
Here's a simplified example of a drawing JPanel that's larger than the scrolling JPanel.
You can see in the image that the drawing JPanel is larger both horizontally and vertically than the scrolling JPanel.
I don't call the paintComponent method at all in this code. Because I set up the GUI properly, Swing itself calls the repaint method when you move the scroll bars.
Here are the important things I did.
I started the Swing GUI with a call to the SwingUtilities invokeLater method. This method makes sure that the Swing components are created and executed on the Event Dispatch Thread.
I used a JFrame, two JPanels, and a JScrollPane. I extended JPanel to create the drawing panel. I used a JScrollPane, JPanel, and JFrame. The only time you extend a Swing component, or any Java class, is when you want to override one or more class methods.
I used Swing layout managers. I used a BorderLayout for the JFrame and scrolling JPanel.
Here's the complete runnable code. Why, you can even call it a minimal reproducible example!
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LargeDrawingPanel implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LargeDrawingPanel());
}
#Override
public void run() {
JFrame frame = new JFrame("Large Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.setPreferredSize(new Dimension(400, 400));
JScrollPane scrollPane = new JScrollPane(new DrawingPanel());
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(2000, 2000));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
int x = 100;
int y = 100;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
g.fillRect(x, y, 100, 100);
x += 200;
}
y += 200;
x = 100;
}
}
}
}
Edited to add: The OP posted additional questions in a comment.
Thanks for the example but it doesn't show me how to print an image in
a JPanel.
Pretty simple. Read an image using the ImageIO class, save the image in an application model consisting of one or more plain Java getter / setter classes and use the Graphics drawImage method to draw the image.
Your preview has only one scrollbar moved not both - which is my
problem.
Did you actually run the code I provided? I can only move one scrollbar at a time. The drawing JPanel extends both horizontally and vertically.
And it doesn't explain why my example doesn't work.
Your example is riddled with errors. Start over, building a Swing application one Swing component at a time using sound principles. The Oracle tutorial, Creating a GUI With JFC/Swing, will show you the correct way to create a Swing application. You can skip the Netbeans section.

Java graphic not painting properly

I've written the following code
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
final public class Test
{
JFrame frame;
DrawPanel drawPanel;
boolean up = false;
boolean down = true;
boolean left = false;
boolean right = true;
private int timeStep = 0;
private int ballYTravel = 100;
private int BALL_NUM = 24;
public static void main(String... args)
{
new Test().go();
}
private void go()
{
frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawPanel = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
frame.setResizable(false);
frame.setSize(800, 600);
frame.setLocationByPlatform(true);
frame.setVisible(true);
moveIt();
}
class DrawPanel extends JPanel
{
private static final long serialVersionUID = 1L;
public double getY(int i, int t) {
return 200 + ballYTravel / 2 * (Math.sin(timeStep * (i / 200 + 0.08)));
}
public void paintComponent(Graphics g)
{
for (int k = 0; k < BALL_NUM; k++ ) {
g.fillRect(100 + 20 *k , (int) getY(k, timeStep), 6, 6);
}
timeStep++;
}
}
private void moveIt()
{
while (true)
{
try
{
Thread.sleep(10);
}
catch (Exception e)
{
e.printStackTrace();
}
frame.repaint();
}
}
}
It runs and animates, however it is not animating in the same fashion as the Javascript code I referenced it from which can be found here http://codepen.io/anon/pen/ZYQoQZ
any help in understanding why is appreciated
There are (possibly) two basic problems...
In getY, you are ignoring the parameter t and using timeStep instead, while, technically, this probably isn't going to make a MASSIVE difference, it is an area of concern
You have an integer division issue. i/200 will result in int result, where you really want a double. Change it to i/200d
For example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
final public class Test {
private int timeStep = 0;
private final int ballYTravel = 100;
private final int BALL_NUM = 24;
public static void main(String... args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
class DrawPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawPanel() {
new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timeStep++;
repaint();
}
}).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
public double getY(int i, int t) {
return 100 + ballYTravel / 2 * (Math.sin(t * (i / 200d + 0.08)));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int k = 0; k < BALL_NUM; k++) {
g.fillRect(10 + 20 * k, (int) getY(k, timeStep), 6, 6);
}
}
}
}
You're also breaking the paint chain, which is going to cause you issues in the long run, make sure you are calling super.paintComponent...
For more details see...
Performing Custom Painting
Painting in AWT and Swing
Concurrency in Swing
How to use Swing Timers
Initial Threads
Your transliteration reveals several problems:
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
Don't use setSize() when you really mean to override getPreferredSize().
Invoke pack() to let the container adopt its preferred size.
Use javax.swing.Timer to pace the animation.
Revised code, incorporating #Mad's fix and using drawOval():
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
final public class Test {
JFrame frame;
DrawPanel drawPanel;
private int timeStep = 0;
private int ballYTravel = 100;
private int BALL_NUM = 24;
public static void main(String... args) {
new Test().go();
}
private void go() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawPanel = new DrawPanel();
frame.add(BorderLayout.CENTER, drawPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
Timer t = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
drawPanel.repaint();
}
});
t.start();
}
});
}
private class DrawPanel extends JPanel {
public double getY(int i, int t) {
return 200 + ballYTravel / 2 * (Math.sin(t * (i / 200d + 0.08)));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int k = 0; k < BALL_NUM; k++) {
g.drawOval(100 + 20 * k, (int) getY(k, timeStep), 8, 8);
}
timeStep++;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(700, 500);
}
}
}

Passing final vars to a MouseListener method and return a result

I'm struggling with this code, I want to click on one of the cells of the grid, made by JPanel objects, and in that cell make appear a label with the index of that cell. I made a method to add final vars and return the JPanel with that label. It's not working. How can I do this?
public MyTest01(int width, int length) { //constructor
frame.setLayout(new GridLayout(width, length)); //set layout
JPanel temp = null;
JLabel l;
for (int y = 0; y < length; y++) {
for (int x = 0; x < width; x++) {
temp = new JPanel();
temp.setBorder(new LineBorder(Color.black, 1));
temp=doStuff(temp,x,y);
frame.add(temp);
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack(); //sets appropriate size for frame
frame.setVisible(true); //makes frame visible
}
public static JPanel doStuff( final JPanel temp,final int x, final int y) {
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvent) {
JLabel l = new JLabel("("+x+" - "+y+")");
temp.add(l);
}
};
return temp;
}
You never add the listener to the JPanel.
You need to revalidate and repaint the JPanel after adding a component (JLabel);
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvent) {
JLabel l = new JLabel("(" + x + " - " + y + ")");
temp.add(l);
temp.revalidate();; <-------- revalidate
temp.repaint(); <-------- repaint
}
};
temp.addMouseListener(mouseListener); <-------- add listener
return temp;
Here is the working code
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class MyTest {
JFrame frame = new JFrame();
public MyTest(int width, int length) { //constructor
frame.setLayout(new GridLayout(width, length)); //set layout
JPanel temp = null;
JLabel l;
for (int y = 0; y < length; y++) {
for (int x = 0; x < width; x++) {
temp = new JPanel();
temp.setBorder(new LineBorder(Color.black, 1));
temp = doStuff(temp, x, y);
frame.add(temp);
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack(); //sets appropriate size for frame
frame.setVisible(true); //makes frame visible
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MyTest(3, 3);
}
});
}
public static JPanel doStuff(final JPanel temp, final int x, final int y) {
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvent) {
JLabel l = new JLabel("(" + x + " - " + y + ")");
temp.add(l);
temp.revalidate();;
temp.repaint();
}
};
temp.addMouseListener(mouseListener);
return temp;
}
}

The method about ImageIcons does not work

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Cards extends JFrame {
private GridLayout grid1;
JButton []bt=new JButton[52];
ImageIcon tail=new ImageIcon(getClass().getResource("b1fv.png"));
ImageIcon ori;
public Cards(){
grid1=new GridLayout(7,9,2,2);
setLayout(grid1);
for(int i=0;i<bt.length;i++){
ImageIcon c=new ImageIcon(getClass().getResource(i+1+".png"));
bt[i]=new JButton(c);
bt[i].addActionListener(new RatingMouseListener(i));
add( bt[i]);
}
}
public static void main(String[] args){
Cards frame=new Cards();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1400,700);
frame.setVisible(true);
}
private class RatingMouseListener implements ActionListener {
private int index=0;
public RatingMouseListener(int index) {
this.index = index;
}
public void actionPerformed(ActionEvent e) {
System.out.println("Mouse entered for rating " + index);
ori=new ImageIcon(getClass().getResource(index+1+".png"));
if (bt[index].getIcon()==ori)
bt[index].setIcon(tail);
else
bt[index].setIcon(ori);
}
}
}
When I run this, I expect that the ori and the tail should exchange. But they don't. Can someone help me?
This would be best done via a description tag.
Basically, set the description to the images like below, then swap them if they have the same description.
ori.setDescription("ori");
tail.setDescription("tail");
if ("ori".equals((ImageIcon)bt[index].getIcon()).getDescription())
// The rest is how you had it.
I'm guessing that you want to have playing cards that flip when clicked (but I'm not sure). Again, I recommend that you create your ImageIcons once and at the start of the program. Then you can easily compare if one icon is the same as another by using the equal(...) method or even in this situation the == operator. For example, please have a look at and run this code for an example of what I mean:
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.*;
public class CardsDeck {
public static final String RANKS = "a23456789tjqk";
public static final String SUITS = "cdhs";
public static final String CARDS_IMG_PATH = "http://math.hws.edu/javanotes/source/cards.png";
private static final int BACK_RANK = 2;
private static final int BACK_SUIT = SUITS.length();
private static final String ICON = "icon";
private JPanel panel = new JPanel();
private List<ImageIcon> iconList = new ArrayList<ImageIcon>();
private ImageIcon cardBack;
public CardsDeck() {
try {
URL imgUrl = new URL(CARDS_IMG_PATH);
BufferedImage img = ImageIO.read(imgUrl);
double cardWidth = (double) img.getWidth() / RANKS.length();
double cardHeight = (double) img.getHeight() / (SUITS.length() + 1);
int w = (int) cardWidth;
int h = (int) cardHeight;
for (int rank = 0; rank < RANKS.length(); rank++) {
for (int suit = 0; suit < SUITS.length(); suit++) {
int x = (int) (rank * cardWidth);
int y = (int) (suit * cardHeight);
BufferedImage subImg = img.getSubimage(x, y, w, h);
ImageIcon icon = new ImageIcon(subImg);
iconList.add(icon);
}
}
int x = (int) (BACK_RANK * cardWidth);
int y = (int) (BACK_SUIT * cardHeight);
BufferedImage subImg = img.getSubimage(x, y, w, h);
cardBack = new ImageIcon(subImg);
int hgap = 5;
int vgap = hgap;
panel.setLayout(new GridLayout(SUITS.length(), RANKS.length(), hgap, vgap));
panel.setBorder(BorderFactory.createEmptyBorder(vgap, hgap, vgap, hgap));
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
Icon currentIcon = label.getIcon();
if (currentIcon.equals(cardBack)) {
Icon icon = (Icon) label.getClientProperty(ICON);
label.setIcon(icon);
} else {
label.setIcon(cardBack);
}
}
};
Collections.shuffle(iconList);
for (int i = 0; i < iconList.size(); i++) {
JLabel label = new JLabel(cardBack);
label.putClientProperty(ICON, iconList.get(i));
label.addMouseListener(mouseListener);
panel.add(label);
}
} catch (MalformedURLException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
private JComponent getPanel() {
return panel;
}
private static void createAndShowGui() {
CardsDeck cardsDeck = new CardsDeck();
JFrame frame = new JFrame("CardsDeck");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(cardsDeck.getPanel());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
If run, it will show a 13 x 4 array of cards that can be flipped by clicking on them:

How to add new Graphic element to JPanel and use its link to change its position in loop?

I need only one ring to be created on MyPanel (extends JPanel) initialization. Then I need to change (in a loop iterate() ) position of ring on MyPanel. How and what to add to this code?
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
public class mull {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JPanel p = createAndShowGUI();
iterate(p);
// "The method iterate(MyPanel) in the type mull is not applicable for the arguments (JPanel)"
}
});
}
private static JPanel createAndShowGUI() {
System.out.println("Created GUI on EDT? "+
SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// how to grab link to this panel - in order to use it in iteration loop ?
MyPanel p = new MyPanel();
f.add(p);
// f.add(new MyPanel());
f.pack();
f.setVisible(true);
return p;
}
private static void iterate(JPanel p){
// the loop should change square position on each iteration
// how to implement ?
for (int i = 0; i < 999; i++){
((MyPanel) p).moveSquare(100 + i*10, 200 + i*10); // here is problem:
//"Cannot make a static reference to the non-static method moveSquare(int, int) from the type MyPanel"
}
}
}
class MyPanel extends JPanel {
private int squareX = 50;
private int squareY = 50;
private int squareW = 200;
private int squareH = 200;
public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
}
// originally this method was private - in orger to access it within mull, it vas changed to public
public void moveSquare(int x, int y) {
int OFFSET = 1;
if ((squareX!=x) || (squareY!=y)) {
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
squareX=x;
squareY=y;
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
}
}
public Dimension getPreferredSize() {
return new Dimension(900,700);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("This is my custom Panel!",10,20);
g.setColor(Color.RED);
g.fillRect(squareX,squareY,squareW,squareH);
g.setColor(Color.BLACK);
g.drawRect(squareX,squareY,squareW,squareH);
}
}

Categories