I have a problem that, I need to only redraw/rebuild the drawing area if the "c" key is pressed.
The way I'm doing using repaint(), turns out to be causing the draw area to be of position.
I also notice that, whenever I re-size the frame, the keylistener is no longer working.
Problems:
unable to repaint correctly.
keylistener is not working after frame is re-sized.
Love to attach the display, but seems like it is blocked because I am newbie.
The following code is the main function that call the class "newZone".
frame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e){
System.out.println("component Rebuild");
frame.getContentPane().removeAll();
frame.getContentPane().invalidate();
JComponent newContentPane = new newZone(frame.getSize());
newContentPane.setOpaque(true);
frame.getContentPane().add(newContentPane);
frame.getContentPane().revalidate();
frame.setContentPane(newContentPane);
}
});
The following is the class of newZone, which contains Paint & keylistener:
public class newZone extends JComponent implements MouseListener, MouseMotionListener, KeyListener {
JPanel panel1;
JTextArea textArea;
JScrollPane scrollPane;
MyDrawingTool Drawing;
static int firsttimer = 0;
static int preposX = 0;
static int preposY = 0;
static int widthPercentage = 80 , heightPercentage = 93;
static int numberOfYboxes,numberOfXboxes;
static Dimension currentPanelSize;
static final String NEWLINE = System.getProperty("line.separator");
static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public newZone(Dimension currentPanelSize1) {
currentPanelSize = currentPanelSize1;
Drawing = new MyDrawingTool();
Drawing.setBackground(Color.WHITE);
Drawing.setBounds( 10, 10,
(int) currentPanelSize.getWidth()*(widthPercentage)/100,
(int) currentPanelSize.getHeight()*(heightPercentage)/100 );
Drawing.setPreferredSize(new Dimension( (int) currentPanelSize.getWidth()*(widthPercentage)/100,
(int) currentPanelSize.getHeight()*(heightPercentage)/100));
Drawing.addMouseListener(this);
Drawing.addMouseMotionListener(this);
add(Drawing);
addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e){
System.out.println( "Key type: "+e.getKeyChar());
if(e.getKeyChar() == 'c'){
Drawing.redraw();
}
}
});
setFocusable(true);
}
class MyDrawingTool extends JPanel{
void redraw(){
repaint();
}
#Override
public void paint(Graphics q){
//super.paint(q);
int j,k, width, height;
int startX = 10, startY = 10;
int boxSize = 50;
width = (int)currentPanelSize.getWidth()*(widthPercentage)/100;
height = (int)currentPanelSize.getHeight()*(heightPercentage)/100;
numberOfYboxes = (height-20)/50;
numberOfXboxes = (width-20)/50;
for ( j = 0; j < numberOfYboxes; j++)
{
startX = 10;
for ( k = 0; k < numberOfXboxes; k++)
{
q.setColor(Color.WHITE);
q.fillRect(startX, startY, boxSize, boxSize);
q.setColor(Color.BLUE); //Set line color
q.drawRect(startX, startY, boxSize, boxSize);
startX+=boxSize;
}
startY+=boxSize;
}
}
}
}
I don't know why you are using a ComponentListener. I don't see any reason to remove/add/invalidate/revalidat and do all the other stuff.
All you need to do is add the panel to the CENTER of a content pane of the frame. The panel will automatically increase/decrease in size as the frame resizes. There is no need for the ComponentListener.
Custom painting should be done in the paintComponent() method and don't forget to invoke super.paintComponent(...) at the start.
The KeyListener doesn't work because focus is now on the JFrame (not the panel) after you resize the frame. You should NOT be using a KeyListener for this. Instead you should be Key Bindings which work even when the panel doesn't have focus.
It seems, you do not need to switch content pane at all.
If you use some layout on default content pane, as #camickr suggested, you won't need to handle resize and other stuff manually.
Good luck there.
Related
I'm practicing Java by creating a text analyzing program (word count, TF-IDF, etc).
I'm now constructing a small GUI to be displayed at the top of the frame. I want a dropdown menu, and I'm trying to create one using a JPanel and several JButton components. But to make the dropdown reusable, I've been trying to construct a class to manage it.
However, I encountered a problem: when I try to create the dropdown menu it's only visible in the upper left corner of the frame, and if I try to move it, it gets cut off or disappears completely.
I tried creating a simple method to do the same thing, but the problem remains the same. Here's the relevant code (ignore the processor and State stuff):
public class ExperimentState extends State {
private JFrame frame;
public ExperimentState(Processor processor) {
super(processor);
initialize();
}
private void initialize() {
int width, height;
//these variables are temporary, for testing
width = 800;
height = 640;
//Set up the frame
frame = new JFrame();
frame.setSize(width, height);
frame.getContentPane().setLayout(null);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Test code
String[] buttonNames = { "42", "Don't panic!", "Towel"};
// x, y, width, height, button names
JPanel panel = createPanel(0, 0, 100, 25, buttonNames);
frame.getContentPane().add(panel);
}
public JPanel createPanel(int x, int y, int width, int height, String[] names) {
//Create and setup the panel
JPanel panel = new JPanel();
panel.setLayout(null);
panel.setBounds(x, y, width, height);
JButton[] buttons = new JButton[names.length];
for(int i = 0; i < names.length; i++) {
JButton button = new JButton(names[i]);
button.setBounds(x, y + height*i, width, height); //each button has the size of the panel, and are placed on top of each other
panel.add(button);
buttons[i] = button;
}
buttons[0].addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
panel.setBounds(x, y, width, height*names.length); //the panel extends to reveal all the buttons
}
public void mouseExited(MouseEvent e) {
panel.setBounds(x, y, width, height); //the panel retracts
}
});
return panel;
}
#Override
public void display() {
frame.setVisible(true);
}
#Override
public void hide() {
frame.setVisible(false);
}
}
I thought there would be a problem with the mouse listener, since I don't really know how they work and thought they wouldn't be able to access the x, y, width and height variables anytime they were called, but there was no problem.
The problem appears when I call createPanel() with something like createPanel(50, 0, 100, 25, buttonNames).
Here, x is 50 instead of 0. When displayed, the panel is moved to the right, as it should be, but its buttons are now cut in half by some invisible line. If x = 100, it will go completely invisible.
Why is this happening? What am I missing?
Components inside a Container will only completely show (without a LayoutManager) if the bounds of these Components are completely inside it. By changing the x-coordinate from 0 to 50 you move the location of the Button. If you do not change its size or the size of your container your Button will be cut off.
The easiest way to fix this problem is to replace x and y in the location of your button with 0 and replace the 0‘s in the location of the Panel with x and y.
EDIT: Similar Problem here: Components not showing in second JPanel
I'm building a program that draws random (User input) rectangles on a JPanel.
Problem 1:
Whenever I type a number in my JTextfield,I need to click twice on
the JBUtton for Rectangles to show up.
Problem 2:
When i type a new number in the JTextField the number of that don't
show the rectangle but it shows the rectangles I typed in previous.
CODE:
private void init() {
final int FRAME_WIDHT = 800;
final int FRAME_HEIGHT = 1000;
int input = 3;
JFrame frame = new JFrame();
frame.setSize(FRAME_WIDHT, FRAME_HEIGHT);
frame.setTitle("Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
west = new JPanel();
west.setSize(500, 500);
west.setBorder(BorderFactory.createLineBorder(Color.black));
east = new JPanel();
east.setSize(300, 1000);
button = new JButton("Add squares");
field = new JTextField(10);
button.setSize(100, 50);
east.add(button);
east.add(field);
east.setBorder(BorderFactory.createLineBorder(Color.black));
button.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
JButton1ActionPerformed(evt);
}
public void JButton1ActionPerformed(ActionEvent evt) {
int aantalRect = Integer.parseInt(field.getText());
MyDrawing draw = new MyDrawing(aantalRect);
west.add(draw);
draw.revalidate();
draw.repaint();
}
});
frame.add(west, BorderLayout.CENTER);
frame.add(east, BorderLayout.EAST);
frame.setResizable(true);
frame.setVisible(true);
}
public static void main(String[] a) {
P1027 form = new P1027();
}
}
class MyDrawing extends JPanel {
int input = 0;
public MyDrawing(int i) {
this.input = i;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Random r = new Random();
setPreferredSize(new Dimension(500, 1000));
for (int i = 0; i < input; i++) {
int x = r.nextInt(460);
int y = r.nextInt(960);
g.drawRect(x, y, 40, 40);
}
}
Can any one tell me how to fix that?
Problem 1: You're not be seeing the squares being drawn on your MyDrawing JPanel the first time because you are calling the setPreferredSize(...) method, when you really should be overriding the getPreferredSize() method as explained by this answer. It is also possible that they are being drawn off-screen. You set the preferred height of MyDrawing to 1000, which doesn't fit on my laptop's screen (The green line is the border of a MyDrawing).
To fix Problem 1, override the method and lower the preferred height if necessary:
class MyDrawing extends JPanel {
... //Constructor
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500); //Changed from 1000 to 500
}
... //paintComponent(...)
//If you change 1000 to 500, don't forget to change 960 to 460 too
}
Problem 2: You're seeing the amount of rectangles you typed into the JTextField previously because:
You are forgetting to remove the previously added MyDrawing from west before adding the new one.
You are calling revalidate() and repaint() on draw when you should be calling it on its parent component, west.
To fix problem 2, remove the old MyDrawing from west, add the new one, then call revalidate() and repaint():
...
public void JButton1ActionPerformed(ActionEvent evt) {
west.removeAll(); //If the old MyDrawing is the only thing
//that has been added to west. Otherwise use
//remove(int index) or remove(Component comp)
west.add(draw);
west.revalidate();
west.repaint();
}
...
Other things:
You switched the T and H around in FRAME_WIDTH.
You could put the code in JButton1ActionPerformed(...) into the actual actionPerformed method.
Your JFrame looks exactly the same with and without the calls to setSize(...) on west, east, and button and that answer I mentioned earlier suggests not using those methods, so consider removing them.
This program is supposed to draw a grid of Rectangles in a JPanel. I drew the grid onto the JPanel by overriding its paintComponent method so that every time the JPanel resizes, the size of the grid would change to fit the height of the JPanel. But when I change the size of the JFrame, the grid only resizes at certain intervals. Is there a better way to change the size of the grid?
import java.awt.*;
import java.awt.geom.Rectangle2D;
import javax.swing.*;
class TextFrame extends JPanel
{
int numOfCells = 99;
int cellSize, xOffSet;
Rectangle2D.Float[][] square = new Rectangle2D.Float[numOfCells][numOfCells];
public TextFrame()
{
setSize(400, 400);
}
public void paintComponent(Graphics comp)
{
Graphics2D comp2d = (Graphics2D)comp;
cellSize = (int)getHeight()/numOfCells;
if(getWidth()<=cellSize*numOfCells)
cellSize = getWidth()/numOfCells;
xOffSet = (getWidth()-(cellSize*numOfCells))/2;
Color black = new Color(0,0,0);
Color grey = new Color(128,128,128);
boolean col = true;
for(int i=0; i<square.length; i++)
{
for(int j=0; j<square[i].length; j++)
{
if(col)
comp2d.setPaint(black);
else
comp2d.setPaint(grey);
col = !col;
square[i][j] = new Rectangle2D.Float(xOffSet+j*cellSize, i*cellSize, cellSize, cellSize);
comp2d.fill(square[i][j]);
}
}
}
public static void main(String[] args)
{
JFrame frame = new JFrame("Conway's Game Of Life");
TextFrame life = new TextFrame();
frame.add(life);
frame.setSize(life.getHeight(), life.getWidth());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
The reason for intervals is integer arithmetic. Change:
int cellSize, xOffSet;
to:
float cellSize, xOffSet;
Also, change:
cellSize = (int)getHeight()/numOfCells;
to:
cellSize = getHeight()/(float)numOfCells;
Some other side notes:
Do not change visibility of paintComponent, it is defined as protected.
Don't forget to add super.paintComponent(comp); in paintComponent()
Do not call setSize(), override panel's getPreferredSize() and pack() the frame. For example:
public Dimension getPreferredSize(){return new Dimension(400, 400);}
Then, add frame.pack(); before making the frame visible.
Try to make paintComponent as fast as possible for better performance and user experience. You can move some things out of it, leaving only painting logic.
My main issue is with the following piece of code when setting up a JFrame:
Why the panel doesn't show if I use the pack() and how to make it work?
Why the first requestFocusInWindow() doesn't work and what the principle to use it?
Why the default layout manager of JPanel doesn't work if I delete the setLayout()?
public class SoundGUI extends KeyAdapter{
public static void main(String[] args) {
SoundGUI sGUI = new SoundGUI();
sGUI.setUp();
}
public void setUp () {
JFrame frame = new JFrame ("Key test");
frame.setSize (1000, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible (true);
Panel p = new Panel ();
p.setLayout(new BorderLayout());//why this sentence is necessary FlowLayout doesn't fill the container but rather lets components size to their preferredSizes.
p.addKeyListener (this);
p.requestFocusInWindow();//it's useless here
//requestFocus only works on focusable components that are displayed.
MyDrawPanel dp = new MyDrawPanel();
dp.setBackground(Color.darkGray);
JLabel test = new JLabel("a trial");
JButton t = new JButton("b");
dp.add(t);
dp.add (test);
p.add (dp);
frame.getContentPane().add(p);
p.requestFocusInWindow();
//frame.pack();//why it doesn't work
//frame.setVisible(true);
}
class MyDrawPanel extends JPanel {
public void paintComponent (Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
for (int i = 0; i < 1000; i += 42) {
g2.fill3DRect(i,100 ,20 ,80 ,true);
}
g2.setColor(Color.black);
for (int i = 21; i < 1000; i += 42) {
g2.fill3DRect(i,100 ,20 ,80 ,true);
}
}
}
}
Suggestions:
Call setVisible(true) after calling pack(). Makes sense, doesn't it?
The BorderLayout will tell the MyDrawPanel to fill the p container, since the component is being added in a default way (meaning BorderLayout.CENTER), and that's how BorderLayout works.
FlowLayout doesn't fill the container but rather lets components size to their preferredSizes.
Don't mix Swing with AWT components. i.e., don't use Panels, but rather use JPanels.
requestFocus only works on focusable components that are displayed.
Better to use Key Bindings than KeyListeners.
Better to avoid setting the sizes of anything if possible.
Based on your new code, your problem is due to you're calling setSize(). Most layout managers don't respect this but rather the preferred size. If your drawing JPanel needs to be so big, then make it so. For example try:
class MyDrawPanel extends JPanel {
private static final int PREF_W = 1000;
private static final int PREF_H = 300;
public void paintComponent(Graphics g) {
super.paintComponent(g); //!! ******** don't forget this!!! *********
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
for (int i = 0; i < 1000; i += 42) {
g2.fill3DRect(i, 100, 20, 80, true);
}
g2.setColor(Color.black);
for (int i = 21; i < 1000; i += 42) {
g2.fill3DRect(i, 100, 20, 80, true);
}
}
// the getPReferredSize will make this JPanel preferentially be this size
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
Also note that request focus does work if the component is focusable:
JPanel p = new JPanel(); //!! This should be a JPanel, not a Panel
p.setFocusable(true); //!! This is needed
p.setLayout(new BorderLayout());
p.addKeyListener(this);
p.requestFocusInWindow();
But also note that KeyListeners should be avoided. Use Key Bindings instead.
I am dragging a JLabel around the screen, and when I release above the JPanel it is supposed to snap to where it completely covers the JPanel. Also, if I release anywhere else it is supposed to snap to its original position. I have the snap part, but I don't know how to tell if it is over the JPanel. I have my code below.
import java.awt.Color;
java.awt.Dimension;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.event.MouseEvent;
public class Main {
public static final int CARD_HEIGHT = 97;
public static final int CARD_WIDTH = 73;
/**
* Mouse Handler components
* Changes the location of the JLabel with the mouse
*/
public static MouseInputAdapter mouseHandler = new MouseInputAdapter(){
public int labelDisX;
public int labelDisY;
public void mousePressed(MouseEvent e) {
labelDisX = e.getX();
labelDisY = e.getY();
//move the card above all others
e.getComponent().getParent().setComponentZOrder(e.getComponent(), 0);
e.getComponent().getParent().repaint();
}
public void mouseReleased(MouseEvent e) {
//if not above panel, then move to original spot
if(!abovePanel()) {
e.getComponent().setLocation(labelDisX, labelDisY);
}
}
public void mouseDragged (MouseEvent e) {
JPanel panel = (JPanel) e.getComponent().getParent();
//get preliminary new X coordinate
int newX = e.getComponent().getX() + e.getX() - labelDisX;
//get preliminary new Y coordinate
int newY = e.getComponent().getY() + e.getY() - labelDisY;
//Not moved off edges of JFrame
if(newX > panel.getWidth() - CARD_WIDTH) {
newX = panel.getWidth() - CARD_WIDTH;
}
if(newY > panel.getHeight() - CARD_HEIGHT) {
newY = panel.getHeight() - CARD_HEIGHT;
}
if(newX < 0) { newX = 0; }
if(newY < 0) { newY = 0; }
e.getComponent().setLocation(newX, newY);
}
};
/**
* check to see if the JLabel is above the JPanel
* #return
*/
public static boolean abovePanel() {
return false;
}
/**
* Adds ability to drag to JLabel
* #param label
*/
public static void addDrag( JLabel label) {
System.out.println("Adding drag");
label.addMouseMotionListener(mouseHandler);
label.addMouseListener(mouseHandler);
}
public static void main(String[] args) {
// Create a JFrame
JFrame frame = new JFrame("Example Frame");
// JPanel to add JLabels to
JPanel panel = new JPanel();
// Add a drop target text area in the center of the frame
DropTargetArea dropPanel = new DropTargetArea();
dropPanel.setPreferredSize(new Dimension(CARD_WIDTH, CARD_HEIGHT));
dropPanel.setBackground(Color.gray);
panel.add(dropPanel);
// Add several draggable labels to the container
JLabel blue = new JLabel();
blue.setOpaque(true);
blue.setPreferredSize(new Dimension(CARD_WIDTH, CARD_HEIGHT));
blue.setBackground(Color.blue);
addDrag(blue);
panel.add(blue);
// Add the container to the frame
frame.add(panel);
// Display the frame
frame.setPreferredSize(new Dimension(400,400));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();
}
}
I've done this using a JLayeredPane that holds a JPanel that itself holds a grid of JPanels using BorderLayout, with each smaller JPanel representing a chess square, and each smaller JPanel can accept a single JLabel. I added the MouseAdapter to the JLayeredPane itself, and when clicked, it checks to see if a moveable JLabel is located below the click. If so, the JLabel is raised up to the JLayeredPane's DRAG_LAYER, and then when released, check which JPanel the mouse cursor is over, and drop the JLabel if it is a valid square, otherwise return it to its original position. You can see my code here.