I have a question. I want to make a swing form that, when clicking in a button he slides a panel (with his content) to the left so the panel on the right replaces it with a smooth effect.
I Have tried to do a while how checks the size of the panel and then minimize it and shows the next one like this :
while (jpanelprincipal1.getWidth() < 439 || jpanelprincipal1.getHeight() > 250)
{
int panel1width = jpanelprincipal1.getWidth();
int panel2height = jpanelprincipal1.getHeight();
jpanelprincipal1.setSize(panel1width -- , panel2height --);
jpanelprincipal2.setSize(440,250);
}
I used this trick in C# but with the Application.DoEvent(); (how obviously it's not available on java).
Is there anyway i can make a slide effect of 2 or more panels?
BTW : Sorry for my very bad english !
Thanks In Advance,
Luis Da Costa
he slides a panel (with his content) to the left so the panel on the right replaces it with a smooth effect
You question mentions you want the panel to "slide", but the code looks like you are trying to get the panel to "shrink", so it is replaced by another panel.
Assuming you have two panels each with the same size, then you can "slide" one out of view while the other slides into view.
To do this you an use a panel with a GridLayout. This way each component will be the same size. Then you add the panel to a scrollpane without any scrollbars. The size of the scrollpane will need to be set to the size of the first compnoent. Then you can "slide" the two panels by changing the position of the viewport. So in your Timer you would have code something like:
JViewport viewport = scrollPane.getViewport();
Point position = viewport.getViewPosition();
position.x += 5;
viewport.setViewPosition( position );
You would then stop the Timer when the position is greater than the size of the component.
As suggested by #HFOE, javax.swing.Timer is a good choice for animation. The setDividerLocation() method of JSplitPane can be called from the ActionListener. See How to Use Split Panes for additional options.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
/** #see http://stackoverflow.com/questions/5069152 */
public class SplitPaneTest {
double ratio = 0.5;
double delta = ratio / 10;
private void create() {
JFrame f = new JFrame("JSplitPane");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyPanel p1 = new MyPanel(Color.red);
MyPanel p2 = new MyPanel(Color.blue);
final JSplitPane jsp = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT, true, p1, p2);
Timer timer = new Timer(200, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ratio += delta;
if (ratio >= 1.0) {
ratio = 1.0;
delta = -delta;
} else if (ratio <= 0) {
delta = -delta;
ratio = 0;
}
jsp.setDividerLocation(ratio);
}
});
f.add(jsp);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
timer.start();
}
private static class MyPanel extends JPanel {
Color color;
public MyPanel(Color color) {
this.color = color;
this.setPreferredSize(new Dimension(300, 300));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.drawLine(0, 0, getWidth(), getHeight());
g.drawLine(getWidth(), 0, 0, getHeight());
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new SplitPaneTest().create();
}
});
}
}
I would probably do this with a Swing Timer. Change a class field representing the x, y position of the sliding JPanel in the timer's ActionListener and then call repaint on the container holding the JPanels. A JLayeredPane could work well as the container for the sliding JPanels.
Edit 1: regarding your request for code, I think the best thing is for you to try to create a very small compilable runnable program that attempts to do this, and then post your code with an explanation of your program's behavior as an edit to your original post. Also send us a comment to notify us of your changes. Then we can inspect your code, test it, modify it, and help you mold it into a working program. This is called creating a "Short, Self Contained, Correct (Compilable), Example" or SSCCE (please check the link).
Related
I'm going to visualize the implementation of cluster analysis k-means and for this I use swing. There was a problem with the fact that the second jpanel (nodePanel), which was added, is not processed as it happens with clusters.
I tried to deal with the classes jframe and jpanel, as I understood it can be solved not at the level of multithreading, but then I had difficulties with the implementation of this idea.
public class MainUI extends JFrame {
JPanel canvas;
NodePanel nodePanel;
ClusterPanel clusterPanel;
public static boolean isPaintCluster;
MainUI(String title) {
super(title);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
init();
super.setSize(700, 540);
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frm = super.getSize();
int xpos = (int) (screen.getWidth() / 2 - frm.getWidth() / 2);
int ypos = (int) (screen.getHeight() / 2 - frm.getWidth() / 2);
super.setLocation(xpos, ypos);
super.setVisible(true);
}
void init(){
this.setLayout(null);
canvas = new JPanel();
nodePanel = new NodePanel();
clusterPanel = new ClusterPanel();
nodePanel.addMouseListener(new NodeClickListener(nodePanel));
clusterPanel.addMouseListener(new ClusterClickListener(clusterPanel));
canvas.setBackground(Color.white);
canvas.setBounds(10,10,480,480);
nodePanel.setBounds(10,10,480,480);
clusterPanel.setBounds(10,10,480,480);
this.add(canvas);
this.add(clusterPanel);
this.add(nodePanel);
JPanel ButtonPanel = new JPanel();
JRadioButton radioButtonNodes = new JRadioButton("add Nodes");
radioButtonNodes.addActionListener(new isPaintNode());
JRadioButton radioButtonCluster = new JRadioButton("add Clusters");
radioButtonCluster.addActionListener(new isPaintCluster());
ButtonPanel.setLayout(null);
this.add(ButtonPanel);
ButtonPanel.setBounds(500,10,180,480);
ButtonPanel.add(radioButtonNodes);
radioButtonNodes.setBounds(0,200,120,20);
ButtonPanel.add(radioButtonCluster);
radioButtonCluster.setBounds(0,230,120,20);
}
class isPaintCluster implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if(isPaintCluster){isPaintCluster = false;}
else {isPaintCluster = true;}
}
}
class isPaintNode implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if(isPaintNode){isPaintNode = false;}
else {isPaintNode = true;}
}
}
}
I expect to get a solution in which the clusters and nodes will be independent as end classes, but in the application at each step of learning the position of the cluster will be redefined and the color of the nodes will also change according to the color of the cluster.
canvas.setBounds(10,10,480,480);
nodePanel.setBounds(10,10,480,480);
clusterPanel.setBounds(10,10,480,480);
this.add(canvas);
this.add(clusterPanel);
this.add(nodePanel);
Swing paints components in the reverse order the component is added to a panel. So the nodePanel is painted, then the clusterPanel is painted and finally the canvas panel is painted.
Since your panels have the same location and size, the canvas paints on top of the clusterPanel which paints on top of the nodePanel. So in reality you will only see the "canvas" panel.
You could try making all the panels non-opaque by using setOpaque( false ), but I would not recommend this approach.
Instead, I suggest you should only have a single panel and then you override the paintComponent() method of the panel to paint multiple objects on the panel.
Check out Custom Painting Approaches for an example of this approach.
I didn’t think of anything better than to combine the NodePanel and СlusterPanel classes into one class that will process the added nodes and clusters in the form.
This is my first project with AWT/Swing. I'm trying to design a simple cellular automaton. I had some problems choosing a layout manager, now I'm using GridLayout because is the closest I got to what I want. However, when a try to place a cell in the JPanel, the coordinates do not work as I expected. Maybe I should not be extending from JComponent and using fillRect()? Or maybe GridLayout is not the layout I need? The main problem is that the point (0,0) seems to be "moving". Is fillRect conflicting with GridLayout?
Note 1: I've tried GridBagLayout but did not work (because I have no idea how to configure it). I've also tried the add(component, x, y) method but it did not work.
Note 2: I did not post the code regarding the State of the Cell because it was not relevant.
Edit: Ok, I wrote an example in a single public class, I don't think I can be more concise and reproduce the same results.
Solution: https://docs.oracle.com/javase/tutorial/uiswing/painting/refining.html
This is my code:
public class Example{
class Cell extends JComponent{
private int x = 0; //Cell position ?
private int y = 0;
public Cell(int x, int y){
this.x = x;
this.y = y;
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
//draw cell
g.setColor(Color.white);
g.fillRect(x,y,15,15);
}
}
Example(){
JFrame frame = new JFrame("title");
frame.setBackground(Color.black);
frame.getContentPane().setPreferredSize(new Dimension(300,300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
JPanel box = new JPanel(new GridLayout(20,20)){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
setBackground(Color.black);
//draw grid
for(int i = 0; i <= this.getHeight(); i += 15){
g.drawLine(0,0+i,getWidth(),0+i);
}
for(int i = 0; i <= this.getWidth(); i += 15){
g.drawLine(0+i,0,0+i,getHeight());
}
}
};
/*box.add(new Cell(0,0)); //TEST 1
box.add(new Cell(0,0));
box.add(new Cell(0,0));
box.add(new Cell(0,0));*/
box.add(new Cell(0,0)); //TEST 2
box.add(new Cell(15,0));
box.add(new Cell(30,0));
box.add(new Cell(45,0));
frame.add(box);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new Example();
}
}
And this are the results corresponding to TEST 1 and TEST 2:
TEST 1
TEST 2
All painting is done relative to the component that contains the custom painting, not the panel you add the component to.
So in your case just do the painting from (0, 0).
The layout manager will position the Cell at the location determined by the layout manager.
Note:
A painting method is for painting only. It should NEVER create a component, as your current Box class is doing.
The basic logic is to:
create your panel with the desired layout.
add components to the panel.
the size/location of the components added to the panel will be determined by the layout manager. So in your Cell class you need to implement the getPreferredSize() method so the layout manager can use this information to position each component that is added to the panel.
If you want to manage the painting at different locations on the panel, then don't use real components. Instead you keep an ArrayList of shapes that you want to paint. Each shape will contain the location it should be painted. Then you iterate through the ArrayList in the paintComponent() method to paint each shape. For an example of this approach check out the Draw On Component example found in Custom Painting Approaches.
This question already has answers here:
Why does the first panel added to a frame disappear?
(2 answers)
Closed 5 years ago.
I've been trying all day long to make this happen with no success. What can be going wrong?
I want 2 threads printing simultaneously in my JFrame:
Thread-1: Prints Squares
Thread-2: Prints Circles
I'am ending up with only one thread printing on the JFrame. The other get executed but don't print in the JFrame.
Look, only squares are getting printed:
This is my main class:
public class Main {
public static void main(String[] args) {
FigurePlacer circle = new FigurePlacer("circle");
FigurePlacer square = new FigurePlacer("square");
JFrame window = new JFrame();
window.add(circle);
window.add(square);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle("Task");
window.setSize(700, 700);
window.setLocationRelativeTo(null);
window.setVisible(true);
}
}
This is the Threading Class:
import java.awt.Graphics;
import javax.swing.JPanel;
import java.awt.Color;
import java.util.Random;
public class FigurePlacer extends JPanel implements Runnable{
String figure;
final int width = 700;
final int height = 700;
int x_pos = 0;
int y_pos = 0;
int x_width = 50;
int y_height = 50;
public FigurePlacer(String str){
figure = str;
randomCoord();
Thread th = new Thread (this);
th.start();
}
private void randomCoord(){ //this ramdomize x,y coord to place a new object
Random random = new Random();
x_pos = random.nextInt(width);
y_pos = random.nextInt(height);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK); //sets the black color in background
g.fillRect(0, 0, 700, 700);
System.out.println(figure);
switch (figure){
case "square":
g.setColor(Color.GREEN);
g.fillRect(x_pos, y_pos, x_width, y_height);
break;
case "circle":
g.setColor(Color.BLUE);
g.fillOval(x_pos, y_pos, x_width, y_height);
break;
}
}
#Override
public void run(){ //paints the objects
while (true){
randomCoord();
paintImmediately(x_pos, y_pos, x_width, y_height);
try{
Thread.sleep (50);
}
catch (InterruptedException ex){}
}
}
}
JFrame window = new JFrame();
window.add(circle);
window.add(square);
The default layout manager for a JFrame is the BorderLayout. When you add a component to a panel using the BorderLayout and don't specify a constraint then the component goes to the CENTER. However only one component can ever be displayed in the CENTER, so only the last component added is painted.
You could use the OverlayLayout, which allows you to stack two panels on top of one another. Of course you will need to make the top panel non-opaque.
The easier solution is to not attempt to use two panels, just create on panel that can display circles or squares.
Also, your painting code is wrong. You should not be using paintImmediately(...) to do painting. The paintComponent() method should paint every object every time the method is invoked. Try resizing your frame and you will see that all your object disappear since the paintComponent() method will clear all the old paintings.
See Custom Painting Approaches for the two common approaches for this kind of painting.
Background: Making a game in Swing. It is simple turn base game. Not a whole lot going on. Because of this I didn't think I would need to implement a Game Tick. Rather, my thought was when a component got changed or needed to be updated just simply revalidate/repaint that component on the fly rather than repainting the whole screen.
I have a GameJPanel which currently has all the components on it. This JPanel is the one that contains the components that get revalidated/repainted etc.
I figured I could make JLayeredPane that holds GameJPanel and my OptionJPanel. Have a Button on GameJPanel that when pressed causes the OptionJPanel to show on top of it and having its JPanel 50% transparent (so it gives the affect it dims the GameJPanel).
However, once I did this what happened was that the GameJPanel started to replace OptionJPanel components (because of the events... etc; repainting of the components).
So currently I am at a loss on what to do. I'm thinking if I had some sort of game tick I wouldn't be having this issue, however, I am not 100% certain. I'm a little worried if I implemented a gametick that the events in game will cause the GameJPanel components to show through for half a second then get replaced. There are some events that cause components to repaint themselves without manually doing it (like quick example for JLabel setText();)
As an example of what I'm trying to go for.
I have tried with a CardLayout but I couldn't figure out how to have the OptionJPanel be on top of GameJPanel while seeing GameJPanel in the background (I tried setting background color, setOpaque(false)..., tried to limit Option JPanel size but I think the CardLayout stretches it (not 100% sure)) all I got was a gray background when doing so.
I would prefer not to go the CardLayout route because in the future I also plan on placing components on top of the GameJPanel (like someone clicks a button, have another panel on a different layer have a component slide in or out etc).
I use CardLayout a ton with my other components in GameJPanel to swap screens around, but haven't had the need to have the other components behind the one showing to show through.
Any ideas on how to go about this would be great or even example code that shows this.
As noted above, you would use a JDialog, a component that is easy to make (similar to making a JFrame) and easy to place. Simply place it "relative-to" the JFrame, e.g.,
myDialog.setLocationRelativeTo(myJFrame);
... and it will automatically center itself on the JFrame. The tricky part is dimming the underlying JFrame, and for this you would need to use a JGlassPane added to the JFrame's rootpane, one set with a background color that uses an alpha composite value. The tricky part with this is to draw the darker background without causing side effects, and to do this, please read Rob Camick's (StackOverflow user camickr) excellent tutorial on drawing in Swing with alpha composites which you can find here: Java Tips Weblog: Backgrounds with Transparency
An example of such a program is shown here:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class DialogEg {
// path to example image used as "game" background
private static final String IMG_PATH = "https://upload.wikimedia.org/"
+ "wikipedia/commons/7/76/Jump_%27n_Bump.png";
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
// get the "game" background image, or exit if fail
BufferedImage img = null;
try {
URL imgUrl = new URL(IMG_PATH);
img = ImageIO.read(imgUrl);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
// pass "game" image into main JPanel so that it will be drawn
DeMainPanel mainPanel = new DeMainPanel(img);
JFrame frame = new JFrame("Dialog Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel); // add main JPanel to JFrame
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
// main JPanel
#SuppressWarnings("serial")
class DeMainPanel extends JPanel {
private BufferedImage img; // background image
// JButton action that shows the JDialog and darkens the glasspane
private PauseAction pauseAction = new PauseAction("Pause");
public DeMainPanel(BufferedImage img) {
super();
this.img = img;
add(new JButton(pauseAction));
}
// draw the "game" background image within the JPanel if not null
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
// size this JPanel to match the image's size
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || img == null) {
return super.getPreferredSize();
}
int width = img.getWidth();
int height = img.getHeight();
return new Dimension(width, height);
}
}
// Action / ActionListener for JButton -- shows JDialog and darkens glasspane
#SuppressWarnings("serial")
class PauseAction extends AbstractAction {
private static final int ALPHA = 175; // how much see-thru. 0 to 255
private static final Color GP_BG = new Color(0, 0, 0, ALPHA);
private DeDialogPanel deDialogPanel = new DeDialogPanel(); // jpanel shown in JDialog
public PauseAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
// comp is our JButton
Component comp = (Component) e.getSource();
if (comp == null) {
return;
}
// create our glass pane
JPanel glassPane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
// magic to make it dark without side-effects
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
};
// more magic below
glassPane.setOpaque(false);
glassPane.setBackground(GP_BG);
// get the rootpane container, here the JFrame, that holds the JButton
RootPaneContainer win = (RootPaneContainer) SwingUtilities.getWindowAncestor(comp);
win.setGlassPane(glassPane); // set the glass pane
glassPane.setVisible(true); // and show the glass pane
// create a *modal* JDialog
JDialog dialog = new JDialog((Window)win, "", ModalityType.APPLICATION_MODAL);
dialog.getContentPane().add(deDialogPanel); // add its JPanel to it
dialog.setUndecorated(true); // give it no borders (if desired)
dialog.pack(); // size it
dialog.setLocationRelativeTo((Window) win); // ** Center it over the JFrame **
dialog.setVisible(true); // display it, pausing the GUI below it
// at this point the dialog is no longer visible, so get rid of glass pane
glassPane.setVisible(false);
}
}
// JPanel shown in the modal JDialog above
#SuppressWarnings("serial")
class DeDialogPanel extends JPanel {
private static final Color BG = new Color(123, 63, 0);
public DeDialogPanel() {
JLabel pausedLabel = new JLabel("PAUSED");
pausedLabel.setForeground(Color.ORANGE);
JPanel pausedPanel = new JPanel();
pausedPanel.setOpaque(false);
pausedPanel.add(pausedLabel);
setBackground(BG);
int eb = 15;
setBorder(BorderFactory.createEmptyBorder(eb, eb, eb, eb));
setLayout(new GridLayout(0, 1, 10, 10));
add(pausedPanel);
add(new JButton(new FooAction("RESUME")));
add(new JButton(new FooAction("RESTART")));
add(new JButton(new FooAction("EXIT TO MAP")));
}
// simple action -- all it does is to make the dialog no longer visible
private class FooAction extends AbstractAction {
public FooAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
Component comp = (Component) e.getSource();
Window win = SwingUtilities.getWindowAncestor(comp);
win.dispose(); // here -- dispose of the JDialog
}
}
}
The GUI looks like this initially:
but then when the dialog shows and the glass pane is darkened, it looks like this:
So after about a month of working on my game I was drawn to this post once again. I implemented part of my game with what DontKnowMuchButGettingBetter's way and also implemented this by just adding the components to the GlassPane so to speak (Made a JPanel, set it to be GlassPane, did whatever on that Panel)...
The later implementation (GlassPane), isn't the best way to go about this because then you can't use the glass pane for other useful things.
I came back to my original idea to use a JLayeredPane. Having different Components on different levels and working off that. My issue before was that when components were getting repainted, the components in the backer layers were over painting the ones in the front layer.
Well I just came across a method called isOptimizedDrawingEnabled()... By making this method always return false for the JLayeredPane I was able to achieve what I wanted.
Hello im making a game using an MVC layout and cannot get the JSlider to become visible. It's working an fully functional, but will not show unless the window is expanded. Here is my method that creates the JSlider in GameView:
`public void startView() {
playerXPosition = 5; // Initializing the X position
playerYPosition = 80; // Initializing the Y position
score = 0;
tds = 0;
level = 1;
lives = 3;
MyPanel drawingWindow = new MyPanel();
drawingWindow.setSize(800, 500);
drawingWindow.setVisible(true);
this.add(drawingWindow);
SliderView jSlider = new SliderView();
jSlider.setSize(this.getWidth() / 4, 50);
jSlider.setAlignmentX((this.getWidth() / 2) - (this.getWidth() / 2));
jSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int tacklerspeed = jSlider.getValue();
getGameController().updateRatio(tacklerspeed);
System.out.println("Speed changed ");
System.out.println(tacklerspeed);
}
});
this.setSize(800, 700);
this.setVisible(true);
this.add(jSlider, BorderLayout.NORTH);
drawingWindow.setFocusable(true);
}`
And here's my SliderView class: `public class SliderView extends JSlider {
public SliderView() {
this.setEnabled(true);
this.setPaintTicks(true);
this.setMaximum(2);
this.setVisible(true);
this.setFocusable(false);
this.setValue(1);
this.setSnapToTicks(true);
}
}`
You look to be shooting yourself in the foot with your attempts to set sizes of things, but most important, you're calling setVisible(true) on the JFrame before adding the JSlider -- so it remains invisible until the next repaint (resize).
Get rid of all setSize's from your program. Instead let the components size themselves,
or override a getPreferredSize, but in a smart way that allows all components to fully show.
You're adding the slider to its container in the BorderLayout.NORTH position. Be sure that this container is in fact using a BorderLayout.
Be sure to call pack() on your top level window (a JFrame?) before displaying it.
MOST important -- call setVisible(true) on the JFrame after adding all components -- including the JSlider
You're also calling this.getWidth() before rendering your GUI components -- this will return a size of 0 -- test it out to see for yourself.