How can I make painted graphics in a JPanel scrollable? - java

I am making an adaptation of a board game. The board will be drawn on a JPanel by overriding the paintComponent(Graphics g) method. The board can possibly be larger than the size of the JPanel it is drawn in, so I planned to use a JScrollPane to allow the user to scroll across the board to view it. As an example, I made the board a single large rectangle to see if I could get it to scroll across that, but to no avail. I have used JScrollPane successfully before but I cannot figure out why this case is any different from the way I previously used it.
Can anyone see why it is not working correctly?
Here is the code for the Game's JFrame:
public class GameFrame extends JFrame{
JPanel playerDeckPanel, contentPane;
JScrollPane gameScrollPane;
BoardPanel boardPanel;
public GameFrame(){
super();
SwingUtilities.invokeLater(new Runnable(){
public void run(){
boardPanel = new BoardPanel();
playerDeckPanel = new JPanel();
boardPanel.setLayout(new GridLayout(1,1));
playerDeckPanel.setLayout(new CardLayout());
boardPanel.setSize(1000,1000);
gameScrollPane = new JScrollPane(boardPanel);
gameScrollPane.setPreferredSize(new Dimension(300,300));
contentPane = ((JPanel) getContentPane());
contentPane.setLayout(new GridLayout(1,2));
contentPane.add(gameScrollPane);
contentPane.add(playerDeckPanel);
setMinimumSize(new Dimension(800,600));
}
});
}
public static void main(String[] args){
GameFrame gameFrame = new GameFrame();
gameFrame.setVisible(true);
}
private class BoardPanel extends JPanel{
public BoardPanel(){
super();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(100, 10, 700, 600);
revalidate();
}
}
}
This is my first time posting a question, so let me know if you need additional information to solve this problem

You need to setPreferredSize on BoardPanel, that seems to do the trick. Don't ask why ;-)
-#geert3

Related

JRadioButton needs a click after selection in order for ActionListener to take effect [duplicate]

I have a program where I have a JFrame with a JButton in it. When the user clicks the JButton, all Components of the JFrame are removed, and a JPanel with red background is added to it.
When I click the JButton, that red JPanel does not become visible unless I resize the JFrame (I am using Windows 7). Is there a way to achieve what I want without having to manually resize the JFrame?
Here is a part of the code I am using:
public class Demo implements ActionListener{
public static void main(String args[]){
...............
button.addActionListener(this); //'button' is an object of Jbutton class.
frame.setVisible(true); //'frame' is an object of JFrame class.
............
}
public void actionPerformed(ActionEvent ae){
frame.removeAllComponents();
frame.add(panel1); //panel1 is an object of Jpanel class with red background.
/* Here is where my problem lies.
panel1 is not visible to me unless I manually resize the JFrame. */
}
}
For removing (and then, for example, add new JComponents) JComponents from JPanel or from top-level containers you have to call, only once and on the end of the action:
revalidate();
repaint();
And if you only resize or change JComponents:
validate();
repaint();
For me, this was a bit of an oddity. As it turned out, invoking remove(Component comp), adding the new JPanel, and then invoking pack() worked for me.
public class Demo{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
createAndShowGUI();
}
});
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
final JButton button = new JButton("Press Me");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
frame.remove(panel);
final JPanel redPanel = new JPanel(){
#Override
public Dimension getPreferredSize(){
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g){
Graphics g2 = g.create();
g2.setColor(Color.RED);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.dispose();
}
};
frame.add(redPanel);
frame.pack();
}
});
panel.add(button);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
BEFORE PRESSING THE BUTTON
AFTER PRESSING THE BUTTON
ODDITIES
Invoking removeAll() actually caused the GUI to freeze. It seems that this event has occurred before. This occurred even after I attempted to remove the action listener prior to removal of all components.
I did not need to invoke any of the validate methods, or even repaint the GUI.
you have to force a repaint() in the frame so the frame have to repaint itself.

Cannot get an Oval to Display

I'm new here. Been reading questions and answers for aeons. Recently I found time to start studying Java, and I'm seriously enjoying the process. Until I started to write some code. The getting stuck is killing me. So I've come to seek advice on something extremely simple but I cannot crack it.
The code below attempts to create a frame, maximize it, and place elements inside. I was just fooling around. First the button1, I tried to change its size (so I got it into a FlowLayout). Then a button in the mainPanel, just to... try. Then an oval. I tried for 2 hours to get the oval to display but it is Impossible. When I found about "drawOval" I thought that was it but it made no difference. And to think that I was planning for the button1 to create Moving Balls. I'm so, So far away from that.
Please, why does the silly Oval refuse to display itself. Help.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MainClass {
JFrame frame = new JFrame();
JPanel mainPanel = new JPanel();
JPanel southPanel = new JPanel();
JButton button1 = new JButton("Button1");
JButton button2 = new JButton("Button2");
Oval oval = new Oval();
public static void main(String[] args) {
MainClass program = new MainClass();
program.go();
}
public void go() {
buildGUI();
}
public void buildGUI() {
button1.setBorder(BorderFactory.createMatteBorder(2,2,2,2, Color.BLACK));
button1.addActionListener(new Button1Listener());
frame.getContentPane().add(BorderLayout.CENTER, mainPanel);
button1.setPreferredSize(new Dimension(200, 50));
frame.getContentPane().add(BorderLayout.SOUTH, southPanel);
southPanel.add(button1);
mainPanel.add(button2);
mainPanel.add(oval);
frame.setSize(400, 400);
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class Button1Listener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
//What the button will do.
}
}
}
And the Oval part
import java.awt.*;
import javax.swing.*;
public class Oval extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.orange);
g.fillOval(20, 50, 100, 100);
}
}
JPanel uses a FlowLayout by default. Since you Oval class doesn't provide any sizing hints, then it's set to a default size of 0x0.
Start by updating your class to something more like...
public class Oval extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.orange);
g.fillOval(20, 50, 100, 100);
}
}
to create Moving Balls.
Ok, you might want to have a look at:
java bounceBall mouse escape
paintComponent not working

How to successfully draw background JPanel once and update foreground JPanel constantly?

I have a custom JLayeredPane, and I am repainting it in my game loop. There are two custom JPanels added into the JLayeredPane. These are foreground and background JPanels. How do I successfully only draw my background JPanel once, (And repaint when window is re-sized or any other reason) to reduce impact on system resources, while continuing to update my foreground JPanel constantly.
To re-iterate, I dont want to constantly repaint the background JPanel in a loop. I want to repaint it only when it is nessessary, as the background does not change. and is large.
In my attempt to do this, I have only drawn the background once. However. the background JPanel is simply not visible. while the foreground JPanel updates as normal. It is almost as if the foreground JPanel paints ontop of the background JPanel, even though I have both of the JPanels set to setOpaque(false)
I have made a mvce which shows my attempt at only drawing the background JPanel once, while updating the foreground JPanel constantly.
The problem with my code is that the background JPanel does not show.
Now. I know that if I were to draw it constantly it would show. But that defeats the purpose of what i'm trying to do. I am trying to only draw it once, and have be seen at the same time
My code successfully only draws the background JPanel once. The problem is that the background JPanel does not show. How do I fix THIS problem
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main extends JLayeredPane {
static JFrame frame;
static Main main;
static Dimension screenSize;
public Main() {
JPanel backPanel = new BackPanel();
JPanel frontPanel = new FrontPanel();
add(backPanel, new Integer(7));
add(frontPanel, new Integer(8));
new Thread(() -> {
while (true){
repaint();
}
}).start();
}
public static void main(String[] args) {
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame = new JFrame("Game"); // Just use the constructor
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main = new Main();
frame.add(main, BorderLayout.CENTER);
frame.pack();
frame.setSize(screenSize);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class BackPanel extends JPanel{
public boolean drawn = false;
public BackPanel(){
setVisible(true);
setOpaque(false);
setSize(screenSize);
JLabel test1 = new JLabel("Test1");
JLabel test2 = new JLabel("Test2");
add(test1);
add(test2);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
drawOnce(g);
}
public void drawOnce(Graphics g){
if (!drawn){
g.setColor(Color.red);
g.fillRect(0, 0, screenSize.width, 200);
drawn=true;
}
}
}
public class FrontPanel extends JPanel{
public FrontPanel(){
setVisible(true);
setOpaque(false);
setSize(screenSize);
JLabel test = new JLabel("Test");
add(test);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.blue);
g.fillRect(0+screenSize.width/2, 0, screenSize.width/4, 300);
}
}
}
Try RepaintManager.currentManager(component).markCompletelyClean(component). It will prevent the component from repainting. You might need to do this after each time you add new components.
http://docs.oracle.com/javase/6/docs/api/javax/swing/RepaintManager.html#markCompletelyClean%28javax.swing.JComponent%29
I don't know if this two lines of code
super.paintComponent(g);
drawOnce(g);
are the root of problem, I sincerly don't remember how paintComponent works (a test could help) but try to swap them :
drawOnce(g);
super.paintComponent(g);
maybe, on your original version, you tells JVM to paint the whole component and, only after the AWTEvent has been added to the queue, to draw what you need.
I guess that the awt's documentation will explain it.

paintComponent overriding to draw on a panel

This is a noob question.
We are being taught applets in class, and I was trying something on my own.
The following is the code
import java.awt.*;
import javax.swing.*;
class controls extends JPanel{
#Override public void paintComponent(Graphics g) {
g.drawOval(50, 50, 50, 50); // <-- draws an oval on the panel
}
}
public class test extends JApplet{
public void init(){
final JPanel stage = new JPanel();
final JPanel controlPanel = new controls();
final JPanel banner = new JPanel();
final JLabel name = new JLabel("Test", JLabel.CENTER);
this.setLayout(new BorderLayout());
banner.setBackground(Color.CYAN);
banner.add(name);
this.add(controlPanel, BorderLayout.WEST);
this.add(banner, BorderLayout.NORTH);
}
}
As far as I understand, paintComponent() need not be called explicitly.
The controls class works well when used alone.
I mean the following code works.
public class test extends JApplet{
public void init(){
JPanel controlPanel = new controls();
this.add(controlPanel);
}
}
I am not able to understand the difference. Why does the same code work in this case, and not in the previous?
Thank you.
Override public Dimension getPreferredSize() (and return a new Dimension) in the controls class. When putting components in WEST the width will be determined by the preferredSize. If you don't override getPreferredSize, the preferred size will be 0. The CENTER will take up the rest of the space, after the WEST, ect is calculated. The second case works because it is in the CENTER of the default BorderLayout

Having trouble adding graphics to JPanel

I have looked online, but I am still having trouble understanding how to add graphics to a JPanel
Here is the code for my panel class:
public class GamePanel extends JPanel {
public GamePanel(){
}
public void paintComponent(Graphics g) {
g.drawString("asd", 5, 5);
}
}
And my main method:
public static void main(String[] args) {
frame.setLayout(new FlowLayout());
frame.getContentPane().setBackground(Color.WHITE);
//i is an instance of GamePanel
frame.add(i);
frame.setPreferredSize(new Dimension(500, 500));
frame.pack();
frame.setVisible(true);
}
Text will only appear in a very tiny section of the screen (this applies to any graphics object I try to draw). What am I doing wrong?
FlowLayout respects preferred sizes of components. Therefore override getPreferredSize to give your JPanel a visible size rather than the default zero size Dimension that the panel currently has after JFrame#pack is called:
class GamePanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g); // added to paint child components
g.drawString("asd", 5, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
Edit:
To eliminate gap between JPanel and its containing JFrame, set the vertical gap to 0:
frame.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
Two things jump out
Your Game panel has no preferred size, which, by default, makes 0x0. FlowLayout will use this information to make decisions about how best to layout your panel. Because the size is 0x0, the repaint manager will ignore it. Try overriding the getPreferredSize method and return a appropriate size or use a layout manager that does not use the preferred size, like BorderLayout
Your paintComponent method MUST call super.paintComponet

Categories

Resources