So I have a JFrame of size 500x500 on which I am drawing a line from (0,200) until (100000,200) using g.drawLine(x,y). The problem is I cannot see the entire line as there is no scroll bar. Could someone please tell me how to have a scroll bar in this particular situation to see the entire line upto the point (100000,200).
While I have usability concerns, the example below illustrates how to implement the Scrollable interface as discussed in How to Use Scroll Panes: Implementing a Scrolling-Savvy Client. Note in particular how the result of getPreferredScrollableViewportSize() differs from the result of getPreferredSize(). The unit and block increments make paging slightly easier, too.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
/**
* #see https://stackoverflow.com/a/37460185/230513
*/
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(new DrawingPanel()));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static class DrawingPanel extends JPanel implements Scrollable {
private static final int W = 100_000;
private static final int H = 400;
#Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
g.drawLine(0, getHeight() / 4, 0, 3 * getHeight() / 4);
g.drawLine(W - 1, getHeight() / 4, W - 1, 3 * getHeight() / 4);
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(640, H);
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return W / 10;
} else {
return 10;
}
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return W / 10;
} else {
return 10;
}
}
#Override
public boolean getScrollableTracksViewportWidth() {
return false;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Test()::display);
}
}
Can you show us the code? Since it's hard to know without seeing it, however on the top of my head I think you should just create one and add it to your JFrame like so:
JTextArea ta = new JTextArea(); // Example with a JTextArea,
depends on what you have, as I said, we need to see the code
JScrollPane sp = new JScrollPane(ta); //Add it to the component
needed, in your case the drawn line i guess
jFrame.add(sp); //Add it to the frame
Ande before adding it, you can manipulate the size:
sp.setColumnHeaderView(new JLabel("header column"));
sp.setRowHeaderView(new JLabel("header row"));
sp.setPreferredSize(new Dimension(500, 300));
Related
I'm trying out the JSplitPane with a couple of scrollable side-by-side JTables.
However I'm experiment a behavior where the JScrollPane gets shrinked too much, as per gif.
Notice the left component, that besides having a minimum width of 250px, continues shrinking.
The relevant code is
final var objetsTable = new JTable();
final var objectsScrollPane = new JScrollPane(objetsTable);
objectsScrollPane.setMinimumSize(new Dimension(250, 0));
objectsScrollPane.setPreferredSize(new Dimension(400, 300));
final var stepsTable = new JTable();
final var stepsScrollPane = new JScrollPane(stepsTable);
stepsScrollPane.setMinimumSize(new Dimension(150, 0));
stepsScrollPane.setPreferredSize(new Dimension(200, 300));
final var splitPane = new JSplitPane();
splitPane.setLeftComponent(objectsScrollPane);
splitPane.setRightComponent(stepsScrollPane);
splitPane.setResizeWeight(1.0);
How can I avoid the JScrollPanes getting shrinked too much in this case?
The getMinimumSize called on a JSplitPane returns a size which is actually taking into account the minimum size of its left and right Components, plus the divider size. So one way to maybe solve your problem would be to make your JSplitPane implement Scrollable (in order to make it respect the minimum size of itself) and add it to a JScrollPane. This way you can ensure that the minimum size is respected and when the user continues shrinking the Scrollable JSplitPane past its minimum size, then the scroll bars will show up.
Here is some working code:
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class Main {
private static class MyScrollableSplitPane extends JSplitPane implements Scrollable {
private int maxUnitIncrement = 10;
public void setMaxUnitIncrement(final int pixels) {
maxUnitIncrement = pixels;
}
public int getMaxUnitIncrement() {
return maxUnitIncrement;
}
/**
* This is being constantly checked by the scroll pane instead of the
* getPreferredScrollableViewportSize...
*/
#Override
public Dimension getPreferredSize() {
final Dimension minSz = getMinimumSize(),
curSz = getSize();
curSz.width = Math.max(curSz.width, minSz.width);
curSz.height = Math.max(curSz.height, minSz.height);
return curSz;
}
/**
* This is only checked once (at the beginning).
*/
#Override
public Dimension getPreferredScrollableViewportSize() {
return super.getPreferredSize();
}
/**
* Source: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html .
*/
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
//Get the current position.
int currentPosition;
if (orientation == SwingConstants.HORIZONTAL) {
currentPosition = visibleRect.x;
} else {
currentPosition = visibleRect.y;
}
//Return the number of pixels between currentPosition
//and the nearest tick mark in the indicated direction.
if (direction < 0) {
int newPosition = currentPosition -
(currentPosition / maxUnitIncrement)
* maxUnitIncrement;
return (newPosition == 0) ? maxUnitIncrement : newPosition;
} else {
return ((currentPosition / maxUnitIncrement) + 1)
* maxUnitIncrement
- currentPosition;
}
}
/**
* Source: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html .
*/
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation,
int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return visibleRect.width - maxUnitIncrement;
} else {
return visibleRect.height - maxUnitIncrement;
}
}
#Override
public boolean getScrollableTracksViewportWidth() {
final Container parent = getParent();
return (parent instanceof JViewport) && (getMinimumSize().width < ((JViewport) parent).getWidth());
}
#Override
public boolean getScrollableTracksViewportHeight() {
final Container parent = getParent();
return (parent instanceof JViewport) && (getMinimumSize().height < ((JViewport) parent).getHeight());
}
}
private static void createAndShowGUI() {
/*Since I don't add any Components to the 'left' and 'right' panels, I am going to set the
preferred size of them. This is only for demonstrating the concept. Setting the minimum size
though is somewhat required by the JSplitPane itself.*/
final JPanel left = new JPanel();
left.setMinimumSize(new Dimension(150, 100));
left.setPreferredSize(new Dimension(200, 200));
final JPanel right = new JPanel();
right.setMinimumSize(new Dimension(300, 100));
right.setPreferredSize(new Dimension(400, 200));
final JSplitPane split = new MyScrollableSplitPane();
split.setBorder(BorderFactory.createLineBorder(Color.CYAN.darker(), 3));
split.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
split.setLeftComponent(left);
split.setRightComponent(right);
final JFrame frame = new JFrame("MyScrollableSplitPane demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(split));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::createAndShowGUI);
}
}
This is my 1st question here. I'm trying to build a White Page adjustable by zoom. It's inside a JScrollPane, so the size of the JScrollPane's ScrollBars are adjustable in the Dimension of that JPanel.
I want to adjust the size of those ScrollBars as the Size of the page (variables width and height in the code) + 2 borderSize, so the full size is equal the Page + margin of a borderSize around it. It works if zoom = 1.0.
If zoom < 1.0, the scroll bar is smaller than the Page and cut a piece of it. If zoom > 1 the Dimension size is way bigger than the page, leaving a huger border on its right and down corners, bigger than the borderSize.
How do I do this?
PS: I'm started learning java by myself, in the Quarantine last year, never had a teacher, just the internet, so any critics or suggestions, please, tell me.
Here's the JPanel's code:
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.JSlider;
import javax.swing.JScrollPane;
import javax.swing.JLabel;
public class Main2 {
private MyPanel mp = new MyPanel();
private JFrame frame;
private JSlider zoomSlider = new JSlider();
private JLabel zoomLabel = new JLabel("Zoom: XXX");
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main2 window = new Main2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Main2() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 619, 403);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SpringLayout springLayout = new SpringLayout();
frame.getContentPane().setLayout(springLayout);
springLayout.putConstraint(SpringLayout.SOUTH, zoomSlider, 40, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.EAST, zoomSlider, -115, SpringLayout.EAST, frame.getContentPane());
zoomSlider.setValue(100);
zoomSlider.setSnapToTicks(true);
zoomSlider.setPaintTicks(true);
zoomSlider.setMaximum(200);
zoomSlider.setMinorTickSpacing(5);
zoomSlider.setMinimum(5);
springLayout.putConstraint(SpringLayout.NORTH, zoomSlider, 0, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.WEST, zoomSlider, 0, SpringLayout.WEST, frame.getContentPane());
frame.getContentPane().add(zoomSlider);
JScrollPane scrollPane = new JScrollPane(mp);
springLayout.putConstraint(SpringLayout.NORTH, scrollPane, 10, SpringLayout.SOUTH, zoomSlider);
springLayout.putConstraint(SpringLayout.WEST, scrollPane, 10, SpringLayout.WEST, frame.getContentPane());
springLayout.putConstraint(SpringLayout.SOUTH, scrollPane, -10, SpringLayout.SOUTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.EAST, scrollPane, -10, SpringLayout.EAST, frame.getContentPane());
frame.getContentPane().add(scrollPane);
springLayout.putConstraint(SpringLayout.NORTH, zoomLabel, 10, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.WEST, zoomLabel, 6, SpringLayout.EAST, zoomSlider);
frame.getContentPane().add(zoomLabel);
frame.addWindowStateListener(new WindowStateListener() {
#Override
public void windowStateChanged(WindowEvent arg0) {
// TODO Auto-generated method stub
mp.draw();
}
});
zoomSlider.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
int temp = (zoomSlider.getValue())-zoomSlider.getValue()%5;
setZoom(temp);
mp.draw();
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
int temp = (zoomSlider.getValue())-zoomSlider.getValue()%5;
setZoom(temp);
mp.draw();
}
});
mp.addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getPreciseWheelRotation() < 0) {
setZoom(zoomSlider.getValue()- 5);
} else {
setZoom(zoomSlider.getValue()+ 5);
}
// zoom += e.getPreciseWheelRotation();
if (mp.getZoom()*100 < 10) {
setZoom(10);
}
mp.draw();
}
});
AdjustmentListener adj = new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
setZoom(zoomSlider.getValue());
mp.draw();
}
};
scrollPane.getVerticalScrollBar().addAdjustmentListener(adj);
scrollPane.getHorizontalScrollBar().addAdjustmentListener(adj);
}
public void setZoom(int n) {
mp.setZoom(n);
zoomSlider.setValue(n);
zoomLabel.setText("Zoom: "+mp.getZoom()+"x");
}
}
class MyPanel extends JPanel{
private static final long serialVersionUID = -716735372803790424L;
int borderSize=28;
int zoom=100;
int height = 3565;
int width = 2537;
int widthz, heightz;
public MyPanel() {
setBackground(Color.DARK_GRAY);
}
#Override
public Dimension getPreferredSize() {
int a, b;
String temp;
Float x, y;
x=(getZoom()*width); //Size of the page adjusted by zoom
y=(getZoom()*height);
temp = x.toString();
String temp1[] = temp.split("\\."); // converted to string to convert it to int
a = Integer.valueOf(temp1[0])+2*borderSize; //that value + 2 BorderSize
temp = y.toString();
String temp2[] = temp.split("\\.");
b = Integer.valueOf(temp2[0])+2*borderSize;
return new Dimension (a,b);
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d = putZoom(g2d);
g2d.setColor(Color.WHITE);
g2d.fillRect(this.getX(),this.getY(), width, height);
g2d.setColor(Color.BLACK);
g2d.drawRect(this.getX()+borderSize,this.getY()+borderSize,width-2*borderSize,height-2*borderSize);
g2d.dispose();
}
public Graphics2D putZoom(Graphics2D g) {
AffineTransform at = new AffineTransform();
at.translate(borderSize,borderSize); // put the page a borderSize from the upper-left corner
at.scale(getZoom(),getZoom()); //adjust the page as zoom
Graphics2D g2d = g;
g2d.setTransform(at);
return g2d;
}
public void draw() { //this method is to update the draw from the main
repaint();
}
public Float getZoom() {
return Float.valueOf(zoom)/100;
}
public void setZoom(int zom) { //this method is to update Zoom from the main
zoom=zom;
String zoomheight []= (String.valueOf(getZoom()*height)).split("\\.");
heightz = Integer.valueOf(zoomheight[0]);
String zoomwidth []= (String.valueOf(getZoom()*width)).split("\\.");
widthz = Integer.valueOf(zoomwidth[0]);
}
public int getZoomInt() {
return this.zoom;
}
}
Zoom(values from 0.1 to 2.0).
How can i improve this? Also, i have no idea how to update the JScrollPane's scrollbars together with the zoom.Thanks for the help.
UPDATE: i've created a minimal reproducible exemple.
Introduction
I started working on this before you updated your question. I used a zoom percentage rather than a zoom factor.
I created the following GUI and set the initial state to 30 percent.
I made the inner JPanel a checkerboard so you can more easily see the zoom. I modified your initial values so the inner JPanel would represent an 8 1/2 x 11 piece of paper at 50 pixels per inch.
Here's the same GUI at 100 percent.
Here's the same GUI at 10 percent.
Explanation
I created a JFrame and a control JPanel to hold the JSlider. I used a GridLayout to create the control JPanel.
I created an inner JPanel to hold the drawing and a display JPanel that holds the JScrollPane. I made the display JPanel proportionate to the size of the inner JPanel so I wouldn't have any stretching issues.
Getting the GUI to revalidate / repaint turned out to be the biggest challenge. I wound up having to invalidate the JScrollPane, both JScrollBars, and the display JPanel. I also had to reset the JScrollBars to zero each time I changed the zoom percentage.
Code
Here's the complete runnable code. I made all of the classes inner classes so I could post this as one code block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ZoomJPanelGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new ZoomJPanelGUI());
}
private int zoomPercentage = 30;
private DisplayPanel displayPanel;
private JFrame frame;
#Override
public void run() {
frame = new JFrame("Zoom JPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createControlPanel(), BorderLayout.BEFORE_FIRST_LINE);
this.displayPanel = new DisplayPanel(zoomPercentage);
frame.add(displayPanel.getPanel(), BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createControlPanel() {
JPanel panel = new JPanel(new GridLayout(0, 1));
panel.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15));
JLabel label = new JLabel("Zoom Percentage");
label.setFont(panel.getFont().deriveFont(Font.BOLD, 24f));
panel.add(label);
JSlider slider = new JSlider(
JSlider.HORIZONTAL, 10, 100, zoomPercentage);
slider.setFont(panel.getFont().deriveFont(16f));
slider.setMajorTickSpacing(30);
slider.setMinorTickSpacing(5);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent event) {
JSlider slider = (JSlider) event.getSource();
if (!slider.getValueIsAdjusting()) {
zoomPercentage = (int) slider.getValue();
displayPanel.setZoomPercentage(zoomPercentage);
displayPanel.repaint();
frame.pack();
}
}
});
panel.add(slider);
return panel;
}
public class DisplayPanel {
private InnerPanel innerPanel;
private final JPanel panel;
private JScrollPane scrollPane;
private int zoomPercentage;
public DisplayPanel(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
this.panel = createDisplayPanel();
}
private JPanel createDisplayPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
this.innerPanel = new InnerPanel(zoomPercentage);
scrollPane = new JScrollPane(innerPanel);
scrollPane.setPreferredSize(new Dimension(475, 600));
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
public void setZoomPercentage(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
innerPanel.setZoomPercentage(zoomPercentage);
}
public JPanel getPanel() {
return panel;
}
public void repaint() {
innerPanel.repaint();
scrollPane.invalidate();
JScrollBar hScrollBar = scrollPane.getHorizontalScrollBar();
JScrollBar vScrollBar = scrollPane.getVerticalScrollBar();
hScrollBar.setValue(0);
vScrollBar.setValue(0);
hScrollBar.invalidate();
vScrollBar.invalidate();
panel.invalidate();
}
}
public class InnerPanel extends JPanel {
private static final long serialVersionUID = 1L;
private int maximumBorderSize = 25;
private int maximumCellSize = 50;
private int maximumHeight = 5500;
private int maximumWidth = 4250;
private int zoomPercentage;
public InnerPanel(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
}
public void setZoomPercentage(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
}
#Override
public Dimension getPreferredSize() {
int width = maximumWidth * zoomPercentage / 100;
int height = maximumHeight * zoomPercentage / 100;
return new Dimension(width, height);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int borderSize = maximumBorderSize * zoomPercentage / 100;
paintBackground(g2d);
paintBorder(g2d, borderSize);
paintCheckerboard(g2d, borderSize);
}
private void paintBackground(Graphics2D g2d) {
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
private void paintBorder(Graphics2D g2d, int borderSize) {
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(3f));
g2d.drawRect(borderSize, borderSize, getWidth() - 2 * borderSize,
getHeight() - 2 * borderSize);
}
private void paintCheckerboard(Graphics2D g2d, int borderSize) {
int cellSize = maximumCellSize * zoomPercentage / 100;
int width = maximumWidth - maximumBorderSize * 2 - 2;
int height = maximumHeight - maximumBorderSize * 2 - 2;
int cellWidth = width / maximumCellSize;
int cellHeight = height / maximumCellSize;
boolean isBlue = true;
int x = borderSize;
int y = borderSize;
int heightRemainder = height - cellHeight * cellSize;
for (int i = 0; i < cellHeight; i++) {
int widthRemainder = width - cellWidth * cellSize;
for (int j = 0; j < cellWidth; j++) {
if (isBlue) {
g2d.setColor(Color.BLUE);
} else {
g2d.setColor(Color.YELLOW);
}
isBlue = !isBlue;
g2d.fillRect(x, y, cellSize, cellSize);
x += cellSize;
if (widthRemainder > 0) {
x++;
widthRemainder--;
}
}
// isBlue = !isBlue;
x = borderSize;
y += cellSize;
if (heightRemainder > 0) {
y++;
heightRemainder--;
}
}
}
}
}
I've finally did it. Started by not using transformation for scaling it, but making a new draw with the size zoomed, adapting all sizes in the method setSizes(), and adjusting the Dimension by those sizes.
(Just changed this class)
class MyPanel extends JPanel{
private static final long serialVersionUID = -716735372803790424L;
int borderSize=28;
int zoom=100;
int height = 3565;
int width = 2537;
int widthz, heightz;
int maxHeight, maxWidth; //max size of draw
int maxAreaHeight, maxAreaWidth; //max size of area
public MyPanel() {
setBackground(Color.DARK_GRAY);
}
#Override
public Dimension getPreferredSize() {
setSizes();
return new Dimension (maxAreaWidth,maxAreaHeight);
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d= createBase(g2d);
}
public void draw() { //this method is to update the draw from the main
repaint();
}
public Float getZoom() {return Float.valueOf(zoom)/100;}
public void setZoom(int zom) {zoom=zom;}
public int getZoomInt() {return this.zoom;}
public void setSizes () {
widthz= width*getZoomInt()/100;
heightz=height*getZoomInt()/100;
maxHeight = heightz+2*borderSize;
maxWidth = widthz +2*borderSize;
maxAreaHeight = this.getY()+maxHeight;
maxAreaWidth = this.getX()+maxWidth;
if (this.getSize() != new Dimension(maxAreaWidth, maxAreaHeight)) {
this.setSize(maxAreaWidth, maxAreaHeight);
}
}
public Graphics2D createBase(Graphics2D g2d) {
Graphics2D g = g2d;
setSizes();
g.setColor(Color.WHITE);
g.fillRect(this.getX()+borderSize,this.getY()+borderSize, widthz, heightz);
g.setColor(Color.BLACK);
g.drawRect(this.getX()+borderSize+borderSize*zoom/100,this.getY()+borderSize+borderSize*zoom/100,widthz-2*borderSize*zoom/100,heightz-2*borderSize*zoom/100);
return g;
}
}
Thanks for all the help.
I am trying to make a program that work like this:
In Window class every time I click on the button, the method panel2 of Panel is called: first it is drawing a first circle, then a second one (after the time defined in the timer). Then, I click again on the button, and it is drawing a fist circle, then a second one then a third one. etc.
The problem is that it when I click to obtain 3 circles appearing one after the other, the two first circles drawn at the previous step (before I pressed a second time the button) stay on the screen and only the third circle is drawn when i press the button (instead of having : first circle drawn, second circle drawn, third circle drawn). I hope I am clear.
Here is a simple code:
Window
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Window extends JFrame implements ActionListener{
int h = 2;
Panel b = new Panel();
JPanel container = new JPanel();
JButton btn = new JButton("Start");
JButton bouton = new JButton();
Panel boutonPane = new Panel();
public Window(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
btn.addActionListener(this);
top.add(btn);
container.add(top);
this.setContentPane(container);
this.setVisible(true);
}
public void window2(){
this.setTitle("ADHD");
this.setSize(1000,700);
this.setLocationRelativeTo(null);
if (h < 11){
boutonPane.panel2(h);
bouton.addActionListener(this);
boutonPane.add(bouton);
this.add(boutonPane);
this.setContentPane(boutonPane);
updateWindow2();
}
this.setVisible(true);
}
public void updateWindow2(){
boutonPane.panel2(h);
this.revalidate();
this.repaint();
}
public void actionPerformed(ActionEvent e){
if ((JButton) e.getSource() == btn){
System.out.println("pressed0");
window2();
}
if ((JButton) e.getSource() == bouton){
h++;
System.out.println("pressed" + h);
updateWindow2();
}
}
public static void main(String[] args){
Window w = new Window();
}
}
Panel
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Panel extends JPanel implements ActionListener{
int m;
int u=0;
int lgi, lrgi;
int [] ta;
Timer timer1 = new Timer(300, this);
Panel(){
}
public void panel2(int n){
m=n;
ta = new int [n];
for(int it=0; it<m;it++){
ta[it]=100*it;
}
timer1.start();
}
public void paintComponent(Graphics gr){
super.paintComponent(gr);
gr.setColor(Color.red);
for(int i=0;i<m;i++){
gr.fillOval(ta[i],ta[i], 150, 150);
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
if(u<m){
u++;
revalidate();
repaint();
}
}
}
Your code needs use two int values to decide how many circles to draw and when:
The first int should be the count of current circles to draw, say called, currentCirclesToDraw.
The second int will be the number of circles to draw total.
If you use a List<Ellipse2D> like I suggest, then this number will be the size of the list. So if the List is called ellipseList, then the 2nd number will be ellipseList.size().
The first variable will be incremented in the timer up to the size of the list, but no larger, and will be used by paintComponent method to decide how many circles to draw.
Key point here: the first number, the currentCirclesToDraw, must be re-set to 0 when the button is pressed. This way your paintComponent method will start out drawing 0 circles, then 1, then 2, ...
For example, the paintComponent method could look like so:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(CIRCLE_COLOR);
for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
g2.fill(ellipseList.get(i));
}
}
I use the second term in the for loop conditional statement, i < currentCirclesToDraw && i < ellipseList.size() as an additional fail-safe to be sure that we don't try to draw more circles then we have in our list.
My Timer's ActionListener would increment the currentCirclesToDraw variable and call repaint. It would stop the Timer once currentCirclesToDraw reaches the size of the ellipseList:
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (currentCirclesToDraw < ellipseList.size()) {
currentCirclesToDraw++;
repaint();
} else {
// stop the Timer
((Timer)e.getSource()).stop();
}
}
}
And my button's actionPerformed method would reset currentCirclesToDraw to 0, would add a new Ellipse2D to my ellipseList (if we've not yet reached the MAX_CIRCLE_INDEX), would call repaint() to clear the JPanel, and would construct and start the Timer:
public void actionPerformed(java.awt.event.ActionEvent arg0) {
currentCirclesToDraw = 0; // this is key -- reset the index used to control how many circles to draw
if (ellipseList.size() < MAX_CIRCLE_INDEX) {
double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
double y = x;
double w = CIRCLE_WIDTH;
double h = CIRCLE_WIDTH;
ellipseList.add(new Ellipse2D.Double(x, y, w, h));
}
repaint(); // clear image
new Timer(TIMER_DELAY, new TimerListener()).start();
};
Edit 3/30/14
Note it all can be put together like this:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
/**
* http://stackoverflow.com/a/22714405/522444
* http://stackoverflow.com/questions/22712655/repaint-in-panel-method-not-updated
* #author Pete
*
*/
#SuppressWarnings("serial")
public class TimerCircles extends JPanel {
private static final int PREF_W = 1000;
private static final int PREF_H = 700;
private static final Color CIRCLE_COLOR = Color.RED;
public static final int MAX_CIRCLE_INDEX = 11;
public static final int TIMER_DELAY = 300;
public static final int CIRCLE_WIDTH = 100;
private final List<Ellipse2D> ellipseList = new ArrayList<>();
private int currentCirclesToDraw = 0;
public TimerCircles() {
add(new JButton(new ButtonAction("New Circle", KeyEvent.VK_C)));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(CIRCLE_COLOR);
for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
g2.fill(ellipseList.get(i));
}
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
public void actionPerformed(java.awt.event.ActionEvent arg0) {
currentCirclesToDraw = 0; // this is key -- reset the index used to control how many circles to draw
if (ellipseList.size() < MAX_CIRCLE_INDEX) {
double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
double y = x;
double w = CIRCLE_WIDTH;
double h = CIRCLE_WIDTH;
ellipseList.add(new Ellipse2D.Double(x, y, w, h));
}
repaint(); // clear image
new Timer(TIMER_DELAY, new TimerListener()).start();
};
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (currentCirclesToDraw < ellipseList.size()) {
currentCirclesToDraw++;
repaint();
} else {
// stop the Timer
((Timer)e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
TimerCircles mainPanel = new TimerCircles();
JFrame frame = new JFrame("TimerCircles");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I have created a graph in a Java applet and I'm trying to get the g.fillRect to auto adjust with the screen. I want the first bar to be a third of the screen and then to halve in size for each other bar.
g.fillRect(xpos, 550, width, hight);
I seem to have a problem with getting a gap in between each bar. Could you give me a hand with this problem? Thanks in advance.
You have to compute the width of each individual bar, which is
barWidth = availableWidth / numberOfBars - 1
The "-1" will be the space that will be left between the bars, and thus, has to be added again when computing the actual coordinates. You could spend some time with the details there: When the numbers are not "nicely divisble", then the bars either have to have different widths, or you have to add a small margin at the left and right side to compensate for the odd size.
However, here is a quick sketch of one possible solution:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class BarChart
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
final JSlider slider = new JSlider(1, 50, 3);
final BarChartPanel barChartPanel = new BarChartPanel();
slider.addChangeListener(new ChangeListener()
{
#Override
public void stateChanged(ChangeEvent e)
{
barChartPanel.setNumberOfBars(slider.getValue());
}
});
f.getContentPane().add(slider, BorderLayout.NORTH);
f.getContentPane().add(barChartPanel, BorderLayout.CENTER);
f.pack();
f.setSize(600,600);
f.setVisible(true);
}
}
class BarChartPanel extends JPanel
{
private int numberOfBars = 3;
void setNumberOfBars(int n)
{
this.numberOfBars = n;
repaint();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
Random random = new Random(0);
int barWidth = getWidth() / numberOfBars - 1;
int barsWidth = numberOfBars * (barWidth+1);
int offsetX = (getWidth() - barsWidth) / 2;
for (int b=0; b<numberOfBars; b++)
{
int x = offsetX + b * (barWidth + 1);
int barHeight = random.nextInt(500);
int y = getHeight() - barHeight;
g.fillRect(x, y, barWidth, barHeight);
}
}
}
I want to have a bubbling effect - some image with score bubbling from bottom to up. Below is the complete code. The problem is that paint() never gets called by repaint(). I am not good at java awt or swing. Any reason?
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
* The Bubble object for implementing some animations for each block of LOVE pieces
*/
class Bubble extends JLabel {
private static final long serialVersionUID = 1L;
boolean isBubbling = false;
int xpos;
int ypos;
float transparency;
String text = "+100";
GameSettings config;
AlphaComposite transparency05=AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
Bubble(int x, int y) {
isBubbling=false;
this.xpos = x;
this.ypos = y;
}
void Bubbling() {
isBubbling=true;
for(int i=1;i<=50; i+=4) {
System.out.println("Bubbling init");
ypos -= (int)(7.2*i);
transparency = transparency/2;
setFont(new Font("arial, helvetica",Font.BOLD|Font.ITALIC,i));
repaint();
try {Thread.sleep(15);} catch(Throwable e) {}
}
new Thread() {
public void run() {
for(int i=50;i>=0; i-=4) {
System.out.println("Bubbling run");
ypos -= (int)(7.2*i);
transparency = transparency/2;
setFont(new Font("arial, helvetica",Font.BOLD|Font.ITALIC,i));
repaint();
try {Thread.sleep(15);} catch(Throwable e) {}
}
isBubbling=false;
repaint();
}
}.start();
}
#Override
public void paint(Graphics g1) {
super.paint(g1);
System.out.println("Bubbling paint begin");
if(isBubbling) {
System.out.println("Bubbling paint");
Graphics2D g=(Graphics2D) g1;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FontMetrics fm=g.getFontMetrics();
Rectangle r = getBounds();
int width=fm.stringWidth(text);
int height=fm.getHeight()*4/5;
int cx=g.getFont().getSize()/10;
int x=(r.width-width)/2;
int xx=fm.charWidth('i');
//g.setComposite(transparency05);
g.setComposite(AlphaComposite.SrcOver);
g.setColor(Color.red);
//g.drawString(text,xpos*config.pieceWidth,ypos*config.pieceHeight);
g.drawString(text,xpos,ypos);
//Image img=Tetris.getResource().getImage(config.imagePath+config.imagePrefix+"heart"+config.imagePostfix);
//g.drawImage(img,img.getWidth(null)+3,0,null);
//g.drawImage(img,xpos*config.pieceWidth,ypos*config.pieceHeight,null);
}
}
//for test
static public void main(String[] args) {
//Create and set up the window.
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JPanel newContentPane = new JPanel();
newContentPane.setOpaque(true); //content panes must be opaque
Bubble bub = new Bubble(50, 50);
newContentPane.add(bub);
frame.setContentPane(newContentPane);
frame.setPreferredSize(new Dimension(300, 300));
//Display the window.
frame.pack();
frame.setVisible(true);
bub.Bubbling();
}
}
There are many issues in your code, but the main reason why paint() is not invoked, is because your Bubble component has a size of 0 x 0. This is because you did not override getPreferredSize() to return an adequate value. Moreover, your ypos variable becomes negative in no time meaning that you don't have the time to see the animation.
Now, you should really consider taking care of the following other issues:
Override rather paintComponent than paint
Make sure that you never sleep on the EDT (here it does not happen because you are quite lucky)
Start your UI from the EDT with a SwingUtilities.invokeLater call.
Why do you create another thread in your Bubbling method remains a mistery to me (either do it from the start, for the entire method, or don't do it at all)
a javax.swing.Timer would be much more appropriate in your situation
Follow java naming conventions (methods start with a lower-case letter)
Here is an update of your code (but I did not changed everything according the remarks above):
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
* The Bubble object for implementing some animations for each block of LOVE pieces
*/
class Bubble extends JLabel {
private static final long serialVersionUID = 1L;
boolean isBubbling = false;
int xpos;
int ypos;
float transparency;
String text = "+100";
AlphaComposite transparency05 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
Bubble(int x, int y) {
isBubbling = false;
this.xpos = x;
this.ypos = y;
}
void Bubbling() {
isBubbling = true;
for (int i = 1; i <= 50; i += 4) {
System.out.println("Bubbling init");
ypos--;
transparency = transparency / 2;
setFont(new Font("Arial", Font.BOLD | Font.ITALIC, i));
repaint();
try {
Thread.sleep(50);
} catch (Throwable e) {
}
}
new Thread() {
#Override
public void run() {
for (int i = 50; i >= 0; i -= 4) {
System.out.println("Bubbling run");
ypos--;
transparency = transparency / 2;
setFont(new Font("Arial", Font.BOLD | Font.ITALIC, i));
repaint();
try {
Thread.sleep(50);
} catch (Throwable e) {
}
}
isBubbling = false;
repaint();
}
}.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(180, 50);
}
#Override
protected void paintComponent(Graphics g1) {
super.paintComponent(g1);
if (isBubbling) {
System.out.println("Bubbling paint");
Graphics2D g = (Graphics2D) g1;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FontMetrics fm = g.getFontMetrics();
Rectangle r = getBounds();
int width = fm.stringWidth(text);
int height = fm.getHeight() * 4 / 5;
int cx = g.getFont().getSize() / 10;
int x = (r.width - width) / 2;
int xx = fm.charWidth('i');
// g.setComposite(transparency05);
// g.setComposite(AlphaComposite.SrcOver);
g.setColor(Color.red);
// g.drawString(text,xpos*config.pieceWidth,ypos*config.pieceHeight);
System.err.println(xpos + " " + ypos);
g.drawString(text, xpos, ypos);
// Image img=Tetris.getResource().getImage(config.imagePath+config.imagePrefix+"heart"+config.imagePostfix);
// g.drawImage(img,img.getWidth(null)+3,0,null);
// g.drawImage(img,xpos*config.pieceWidth,ypos*config.pieceHeight,null);
}
}
// for test
static public void main(String[] args) {
// Create and set up the window.
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create and set up the content pane.
JPanel newContentPane = new JPanel(new FlowLayout());
newContentPane.setOpaque(true); // content panes must be opaque
Bubble bub = new Bubble(50, 50);
newContentPane.add(bub);
frame.setContentPane(newContentPane);
frame.setPreferredSize(new Dimension(300, 300));
// Display the window.
frame.pack();
frame.setVisible(true);
bub.Bubbling();
}
}
The problem is that your label no Dimension. You can set with setPreferredSize()
public static final Dimension PREF_SIZE = new Dimension(70, 70);
//in main method or you can override `getPreferredSize()`
Bubble bub = new Bubble(50, 50);
bub.setPreferredSize(PREF_SIZE);
Your are sleeping The Event Dispatch Thread. All gui stuff must be executed in that thread, instead of that take a look to Swing Timers.
Also you should override paintComponent() instead of paint() for swing components. Read more in this article