How to add scrolling feature to a JComponent on a JFrame? - java

My GUI consists of a Diagram class which extends JFrame. I've created a different class called DrawingTool which extends JComponent. The DrawingTool class is like a canvas area for users to drop and drag shapes. I've also added a button panel at the bottom of the JFrame for the users to click various buttons to choose their desired shape and control actions. I've added the button panel and an instance of the DrawingTool class to the Diagram class. How do I make the canvas area (DrawingTool) scrollable? The way I have attempted it is not working, I know I am missing something.
Here is the Diagram class:
public class Diagram extends JFrame {
JButton serverButton, vipButton, arrowButton, undoButton, dragButton, loadButton, submitButton;
JButton applicationButton;
int currentAction = 1;
Graphics2D graphSettings;
Color strokeColor = Color.BLUE, fillColor = Color.BLACK;
/**
* Constructor to generate new diagram with empty drawing board and button
* panel.
*/
public Diagram() {
// Define the defaults for the JFrame
this.setSize(1000, 1000);
this.setTitle("Diagram Tool");
//this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel buttonPanel = new JPanel();
// Swing box that will hold all the buttons
Box theBox = Box.createHorizontalBox();
// Make all the buttons in makeButtons by calling helper function
serverButton = makeButtons("Server", 2);
vipButton = makeButtons("VIPs", 3);
arrowButton = makeButtons("Arrow", 4);
undoButton = makeButtons("Undo", 5);
dragButton = makeButtons("Drag", 6);
loadButton = makeButtons("Load", 11);
applicationButton = makeButtons("Application", 8);
submitButton = makeButtons("Submit", 12);
// Add the buttons to the box
theBox.add(serverButton);
theBox.add(vipButton);
theBox.add(applicationButton);
theBox.add(arrowButton);
theBox.add(undoButton);
theBox.add(dragButton);
theBox.add(loadButton);
theBox.add(submitButton);
// Add the box of buttons to the panel
buttonPanel.add(theBox);
// Position the buttons in the bottom of the frame
JPanel container=new JPanel();
container.add(new DrawingBoard(),BorderLayout.CENTER);
JScrollPane jsp=new JScrollPane(container);
this.add(buttonPanel, BorderLayout.SOUTH);
this.add(jsp);
// Make the drawing area take up the rest of the frame
// Show the frame
this.setVisible(true);
}
Here is the DrawingBoard class:
private class DrawingBoard extends JComponent implements MouseListener, MouseMotionListener {
//declare variables
/**
* Constructor to initialize the drawing board
*/
public DrawingBoard() {
addMouseListener(this);
addMouseMotionListener(this);
// initializeCanvas();
}
//Rest of the code for DrawingBoard
}
This is how it looks now. I'd like to make the gray canvas area scrollable.
Diagram Image

What MadProgrammer said in the comments is just about right. You need to set some informations so your ScrollPanel knows how to behave. What is it's own size, the size of the components inside it, etc.
So normally you'll have a ContentPane, and inside of it panes with your content. To do a scrollable pane you only need to put the ScrollPane inside of your ContentPane and then set a viewport for your ScrollPane. A little code I used fully functional:
contentPane = new JPanel();
setContentPane(contentPane);
contentPane.setLayout(null);
JScrollPane scrollPane = new JScrollPane();
//Vertical and Horizontal scroll bar policy is set to choose when the scroll will be visible scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setBounds(0, 217, 414, 505);
scrollPane.setPreferredSize(new Dimension(414, 414));
JPanel viewport = new JPanel();
viewport.setLayout(null);
viewport.setBounds(0, 0, 414, 505);
//Create your components here, then:
//viewport.add(component)
viewport.setPreferredSize(new Dimension(414, 150));
scrollPane.setViewportView(viewport);
contentPane.add(scrollPane);
Anything you put inside of your ViewPort
will be automaticaly scrolable, if it's size is bigger than the PreferredSize.
Note that all the dimensions I've put is only for example.

Related

Set margin about a JPanel of GridLayout in Swing

I'm trying to setup the margin about the JPanel that it has inside the GridLayout refer to the JFrame, but I don't found the solution using other answer. I don't know if it's important problem, but also it show only the first button before I go to each button with mouse.
The image is an example, I want to setup the JPanel to start from the corner of the grid of image, because the image has a border (not from code, but from decoration board), the blue squares are the button inside the GridView, but I'm trying to fit the gridView to the image draw grid, using the set property (using proportion of pixel).
public class Gui extends JPanel implements View {
private final JPanel gui = new JPanel(new BorderLayout(3, 3));
private JButton[][] chessBoardSquares = new JButton[5][5];
private JPanel chessBoard;
private ImageIcon ArrayWithoutPlayer[] = new ImageIcon[7]; //{1,2,3,4,10,11,12}
private ImageIcon ArrayWithPlayer[] = new ImageIcon[3]; //{1,2,3}
private JFrame frame; //This is the whole frame
public Gui() {
createAndShowGUI();
}
private void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("TextDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setSize(800, 800);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
//frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
//frame.pack();
frame.getContentPane().add(new ImagePanel( setImageIconFromUrl("/home/amministratore/Documenti/Java/ing-sw-2020-palini-rigutti-vangi/image/SantoriniBoardR.png",800,800).getImage()));
chessBoard = new JPanel(new GridLayout(0, 5));
chessBoard.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
//chessBoard.setLayout(new BoxLayout());
//chessBoard.setPreferredSize(new Dimension(400, 100));
chessBoard.setBackground(Color.blue);
//chessBoard.setAlignmentX((float) (2.2/21)*frame.getWidth());
//chessBoard.setAlignmentY((float) (2.2/21)*frame.getHeight());
//chessBoard.setMaximumSize(new Dimension((16/21)*frame.getWidth(),(16/21)*frame.getHeight()));
//chessBoard.setAlignmentX(JLabel.LEFT_ALIGNMENT);
//chessBoard.setBorder(new LineBorder(Color.BLACK));
Insets buttonMargin = new Insets(0,0,0,0);
for (int ii = 0; ii < chessBoardSquares.length; ii++) {
for (int jj = 0; jj < chessBoardSquares[ii].length; jj++) {
JButton b = new JButton();
b.setMargin(buttonMargin);
b.setBorder(null);
b.setBorderPainted(false);
b.setContentAreaFilled(false);
b.setOpaque(false);
chessBoardSquares[ii][jj] = b;
b.setText("AA");
chessBoard.add(chessBoardSquares[ii][jj]);
}
}
//chessBoard.setOpaque( false );
chessBoard.setBackground(new Color(255,0,0,0));
frame.dispose();
frame.add(chessBoard);
frame.setVisible(true);
//chessBoardSquares[0][0].setIcon( ArrayWithoutPlayer[0]); //This is the method to set Icon inside the button
}
}
I'm trying to setup the margin about the jpanel that it has inside the GridLayout
//b.setMargin(buttonMargin);
//b.setBorder(null);
//b.setBorderPainted(false);
I don't think you need all that code.
Instead just set the Border of the button:
b.setBorder( new EmptyBorder(5, 5, 5, 5) );
Edit:
frame.getContentPane().add(new ImagePanel(...));
…
frame.add(chessBoard);
First of all frame.getContentPane().add(…) and frame.add(…) is the same thing. That is the component will be added to the content pane. The second format is just a shortcut for the first.
So you are attempting to add two components to the BorderLayout.CENTER. This will not work as the BorderLayout will only support a single component in any location.
Swing was designed with a parent/child relationship so it appears you want something like:
JFrame (content pane)
ImagePanel
chessBoard
So your logic should be something like:
ImagePanel background = new ImagePanel(…);
background.setLayout( new BorderLayout() );
background.add(chessPanel, BorderLayout.CENTER);
frame.add(background, BorderLayout.CENTER);
Now you have your parent/child relationship between the components.
frame.setSize(800, 800);
Don't set the size of the frame. (800, 800) is the wrong size. If your ImagePanel is (800, 800) then the frame must be bigger because the frame also includes the title bar and the border.
So instead your logic should be:
frame.pack();
frame.setVisible(true);
The pack() method will allow the frame to determine its own preferred size AFTER all the components have been added to the frame.
Note:
In you ImagePanel class you will also need to implement the getPreferresSize() method of your Image. This will allow the pack() method to work properly. Read the section from the Swing tutorial on Custom Painting for a working example.

How do you make it so that a button doesn't interfere with the location of drawn shape?

So basically when I add a button it essentially pushes the black rectangle drawn in this program down, putting it out of its given location. How would you fix this?
import javax.swing.*;
import java.awt.*;
public class Grid {
public class homeGraphics extends JComponent {
homeGraphics() {
setPreferredSize(new Dimension(450, 600));
}
public void paint(Graphics g) {
super.paint(g);
g.fillRect(200, 275, 50, 50);
}
}
public void homeFrame() {
JFrame frame1 = new JFrame();
frame1.setSize(450, 600);
frame1.setResizable(false);
frame1.setDefaultCloseOperation(frame1.EXIT_ON_CLOSE);
JButton playButton = new JButton("Play");
playButton.setPreferredSize(new Dimension(60, 30));
JPanel panel1 = new JPanel();
panel1.add(playButton);
panel1.add(new homeGraphics());
frame1.add(panel1);
frame1.setVisible(true);
}
public static void main(String args[]) {
Grid frame = new Grid();
frame.homeFrame();
}
}```
it essentially pushes the black rectangle drawn in this program down, putting it out of its given location.
What do you mean out of its location? Painting is always done relative to the component. So your painting will always be done at (200, 275) of the component.
If you are attempting to paint at (200, 275) relative to the "frame", then don't. That is NOT how painting works.
Other problems with your code:
Don't attempt to set the size of your frame. If the custom panel is (450, 600) how can the frame possibly be the same size? The frame also contains the "title bar" and "borders". Instead of using setSize(), you invoke frame.pack()just beforeframe1.setVisible(….)`.
Class names start with an upper case character. Learn by example. Have you ever seen a class name in the JDK that doesn't start with an upper case character?
Custom painting is done by overriding paintComponent(…), not paint().
By default a JPanel uses a FlowLayout. So what you see it the button on one line and then the "HomeGraphics" class is too big to fit on the same line so it wraps the to the second line.
You should be more explicit when you do frame layout. So your code should be something like:
JPanel wrapper = new JPanel();
wrapper.add( playButton );
//JPanel panel1 = new JPanel();
//panel1.add(playButton);
//panel1.add(new homeGraphics());
JPanel panel1 = new JPanel( new BorderLayout() );
panel1.add(wrapper, BorderLayout.PAGE_START);
panel1.add(new HomeGraphics(), BorderLayout.CENTER);
Now the code shows your layout attempt more clearly.

JFrame Scroll bar and Canvas

I'm having trouble trying to add a horizontal scroll bar to a JFrame that has a canvas. I draw an images onto the canvas and sometimes these images over-run the screen so I want a scroll bar to enable the user to see them.
Here is the key section in the code, if any other code is needed please let me know and i'll edit.
Could anyone point out what i'm doing wrong ?
Many Thanks J
public class TheFrame extends JFrame {
private static ThePanel canvas;
private String deckImagesToUse;
/**
* The constructor creates a Frame ready to display the cards
*/
public TheFrame(String cardImgFile) {
// Calls the constructor in the JFrame superclass passing up the name to
// display in the title
super("Window title");
//Using users choice of cards
deckImagesToUse = cardImgFile;
// When you click on the close window button the window will be closed
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// This has North, East, South, West and center positions for components
setLayout(new BorderLayout());
// This is what we will draw on (see the inner class below)
canvas = new ThePanel();
setSize(700, 300);
this.add(canvas, BorderLayout.CENTER);
//Scroll Bar
JScrollPane scrollPane = new JScrollPane(canvas);
//scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
this.getContentPane().add(scrollPane);
int desiredWidth = (Pack.cardsInPlay.size() * 70);
Dimension d = new Dimension(desiredWidth,300);
canvas.setPreferredSize(d);
setVisible(true); // Display the window
}
canvas = new ThePanel();
setSize(700, 300);
this.add(canvas, BorderLayout.CENTER);
The setting size and adding to this is futile because later scrollpane ignores it.
When your image is bigger than the JScrollPane just call canvas.setPreferredSize(imageWidth, imageHeight);

JButtons are not resizing to match their preferred size

I am trying to make a menu screen for a game. I've added two buttons, play and exit, and am currently trying to figure out how to resize them. When I run my code the buttons are almost the exact same size (different I imagine because of the text). I'm using BoxLayout for my buttons, and I just read here Why will BoxLayout not allow me to change the width of a JButton but let me change the height? why it would only resize the width or height, but it's not resizing either right now. In my code I use BoxLayout.PAGE_AXIS, I don't know if that makes a difference, but it didn't resize vertically either with BoxLayout.Y_AXIS.
Here's my code:
public class Stage extends JFrame {
/* PRIVATE */
private JButton play, exit;
// Setup the Menu screen.
private void createMenuScreen() {
Container window = getContentPane();
// window.setBackground(Color.BLACK);
JPanel menuScreen = new JPanel();
menuScreen.setLayout(new BoxLayout(menuScreen, BoxLayout.PAGE_AXIS));
window.add(menuScreen, "Center");
play = new JButton("Play");
play.setPreferredSize(new Dimension(20, 20));
play.setAlignmentX(Component.CENTER_ALIGNMENT);
exit = new JButton("Exit");
exit.setPreferredSize(new Dimension(100, 100));
exit.setAlignmentX(Component.CENTER_ALIGNMENT);
menuScreen.add(play);
menuScreen.add(exit);
}
/* PUBLIC */
public Stage() {
// Setup the frame.
setSize(224, 288);
setLocationRelativeTo(null); // Centers the window.
setUndecorated(true); // Removes the Windows border.
setVisible(true);
createMenuScreen();
}
}
Try to use setMaximumSize() method
exit.setMaximumSize(new Dimension(100,100));
Or try to use BorderLayout instead of BoxLayout

How to make JLabel consume space when not visible?

I have a program which creates 2 Panels and then places a label and two buttons in them. The label is set to invisible setVisible(false) and then the two buttons are added and the frame is packed. When i click the first button, the label is shown, setVisible(true), and the seccond one hides it again, setVisible(false). When i click each button, they move to fill the space of the label as it hides, and move again to get out of the way of the label as it is shown. I want to stop this from happening and have the buttons stay in the same place even when the label is hidden.
Here is the code:
public class MainFrame extends JFrame{
public JLabel statusLabel;
public JButton show;
public JButton hide;
public MainFrame(){
super("MagicLabel");
JPanel topPanel = new JPanel(); //Create Top Panel
statusLabel = new JLabel(""); //Init label
statusLabel.setVisible(false); //Hide label at startup
topPanel.setSize(400, 150); //Set the size of the panel, Doesn't work
topPanel.add(statusLabel); //Add label to panel
JPanel middlePanel = new JPanel(); //Create Middle Panel
show= new JButton("Show"); //Create show button
hide= new JButton("Hide"); //Create hide button
middlePanel.setSize(400, 50); //Set the size of the panel, Doesn't work
middlePanel.add(show); //Add show button
middlePanel.add(hide); //Add hide button
this.add(topPanel, "North"); //Add Top Panel to North
this.add(middlePanel, "Center"); //Add Middle Panel to Center
addActionListeners(); //void:adds action listeners to buttons
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(100, 100, 512, 400);
this.setPreferredSize(new Dimension(400,200)); //Set size of frame, Does work
this.pack();
this.setVisible(true);
}
public void animateInstall(boolean var0){ //Void to show and hide label from action listeners
statusLabel.setVisible(var0);
sendWorkingMessage("Boo!");
}
public void sendWorkingMessage(String message){ //Void to set text of label
this.statusLabel.setForeground(new Color(225, 225, 0));
this.statusLabel.setText(message);
}
void addActionListeners(){
show.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
animateInstall(true);
}
});
hide.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
animateInstall(false);
}
});
}
this.setPreferredSize(new Dimension(400,200));
this.setMinimumSize(new Dimension(400,200));
So pack() cannot interfere.
Use CardLayout. Add the JLabel and empty JPanel. Instead of seting it visible/invisible swap the cards showing the JLabel or the JPanel when necesary.
Extending JFrame is not advisable, better extend JPanel put all your components inside and then add it to a JFrame
You need to learn how to use SwingUtilities.invokeLater(): See example how your should look like
You need to learn about Layout: Tutorial
Very dumb and easy approach in your code would be:
this.statusLabel.setForeground(bgColor); //background color
this.statusLabel.setText(" "); //some number of characters
By default for you frame you are using BorderLayout. You can try to have like:
this.add(topPanel, BorderLayout.NORTH); //Add Top Panel to North
this.add(middlePanel, BorderLayout.SOUTH); //Add Middle Panel to South
rather than at center.
Or you can create an intermediate container panel for these 2 panels, or consider other layout managers like BoxLayout, etc

Categories