So I have a JPanel that has an inner border (it's toggled based on MouseEnter/MouseExit, as a sort of a rollover effect). I also have a JLabel. The problem is that the JLabel seems to be positioned relative to the border - not the actual edge of the JPanel. So whenever I move my mouse over the panel, the label shifts over a couple of pixels. I would prefer it to stay stationary.
So I guess my question is, what's the best way to change the border of a panel without affecting the positions of the components inside the panel?
Here's the mouselisteners for the panel:
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
panel.setBorder(BorderFactory.createBevelBorder(1, Color.BLACK, Color.BLACK));
}
#Override
public void mouseExited(MouseEvent e) {
panel.setBorder(null);
}
});
The JLabel is added simply using borderlayout:
panel.setLayout(new BorderLayout());
JLabel label = new JLabel("testlabel");
panel.add(label,BorderLayout.PAGE_END);
You could try using an EmptyBorder when the bevel border is not in use. Give it the same width/height you would the bevel border.
I don't do a lot of messing around with layouts or their managers but that's what I would try.
Edit
Since it seems you may wish to have an overlay type effect instead of a border, you could create a custom JPanel class and include some code in the paintComponent(Graphics g) method to draw this overlay.
Something similar to:
public class OverlayBorderJPanel extends JPanel
{
boolean containsMouse = false; //set to true by mouseListener when contains
BufferedImage overlay = //you would need to load an image border here,
//rather than having a java created border
//You could have alpha so it is half see-through
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (containsMouse)
{
g.drawImage(//use 0,0 position with panel width/height)
}
}
}
I think it would work with something like that, but you may need to call the panel's repaint() method in the listener as well.
Related
What my application is meant to do is change the background and foreground on a click of a radio button and change the shape of the item based on a radio button.I am trying to get my application to actively change shape based on the radio button that is selected.I have the background and foreground working just not the shape. I have seen another post kinda like this but it has a submit button and does not use the JSlider
Below is what I have been messing with and cannot seem to get the program to execute correctly. I have gotten the shape to change but then the slider breaks. Am i approaching this the wrong way?
public class OvalPanel extends JPanel
{
private int diameter = 10; // default diameter
// draw an oval of the specified diameter
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(rectFillRadioButton.isSelected()){
g.fillRect(10,10,10,10);
//repaint();
}
if(ovalFillRadioButton.isSelected()){
g.fillOval(10,10,10,10);
//repaint();
}
}
// validate and set diameter, then repaint
public void setDiameter(int newDiameter)
{
// if diameter invalid, default to 10
diameter = (newDiameter >= 0 ? newDiameter : 10);
repaint(); // repaint panel
}
// used by layout manager to determine preferred size
public Dimension getPreferredSize()
{
return new Dimension(200, 200);
}
// used by layout manager to determine minimum size
public Dimension getMinimumSize()
{
return getPreferredSize();
}
}
this is the class that i initially have that sets the paintComponent. I also have
private class TopRadioButtonHandler extends JFrame implements ItemListener {
private Graphics panel;
public TopRadioButtonHandler(Graphics p) {
panel = p;
}
#Override
public void itemStateChanged(ItemEvent event) {
if(rectFillRadioButton.isSelected()){
panel = myPanel.getGraphics();
panel.fillRect(10,10,10,10);
repaint();
}
if(ovalFillRadioButton.isSelected()){
panel = myPanel.getGraphics();
panel.fillOval(10,10,10,10);
repaint();
}
}
}
i dont think i need the repaint but when i use this method my JSlider stops working.
Am i approaching this the wrong way?
Yes, the paintComponent() method should not be referencing another Swing component.
When you do custom painting, the paintComponent() should only paint the current state of your component.
For example when you use a Jlabel you have methods like setText() and setIcon() to set the text and icon you want to paint.
You already have a method, setDiameter() which is a good start. However, your painting code just hard codes the size of the oval/rectangle. The painting methods should reference you diameter variable.
Now, you need another property to idicate whether to paint an oval or a rectangle. So maybe you need a property like setPaintOval(boolean paintOval).
Then your painting code could be:
If (paintOval)
g.fillOval(10, 10, diameter, diameter);
else
g.fillRect(10, 10, diameter, diameter);
Of course the problem with this approach is that you can only paint two objects.
Also, you should never invoke repaint() in a painting method. The repaint() should only be invoked from your setter methods when you change the state of the component.
but then the slider breaks
The code you posted has nothing to do with a slider.
I'm guessing you want the slider to change the diameter of the oval? Well you need to add a ChangeListener to the slider and then invoke your setDiameter() method with the slider value.
Read the section from the Swing tutorial on How to Use Sliders for a working example.
I have a JScrollPanel and a JPanel added to it. I would like to draw to the JPanel and make the scrollbars of the JScrollPane appear whenever the drawing exceeds the size of the panel and be able to scroll the drawing both vertically and horizontally.
I have tried consulting with various forums and the official docs and tried a few things (setting the borders, the preferred size, etc.) but none seems to yield the desired effects.
I have a JFrame (with GridBagLayout, btw.) :
JFrame frame1 = new JFrame("Application");
frame1.setVisible(true);
frame1.setMinimumSize(new Dimension(580,620));
frame1.setResizable(false);
frame1.setLocationRelativeTo(null);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
The relevant components are :
JPanel panel1 = new JPanel();
JScrollPane scrollPane = new JScrollPane(panel1);
frame1.add(scrollPane, gbc_panel1); //added with layout constraints
JPanel :
panel1.setBackground(Color.BLACK);
panel1.setPreferredSize(new Dimension(500,500));
panel1.setMinimumSize(new Dimension(360,360));
panel1.setMaximumSize(new Dimension(1000,1000));
JScrollPane :
scrollPane.setAutoscrolls(true);
The relevant code from the action event
of a button that does the drawing :
Graphics g;
g = panel1.getGraphics();
panel1.paint(g);
g.setColor(new Color(0,128,0));
/* this is followed by some more code that
does the drawing of a maze with g.drawLine() methods */
The code does the drawing perfectly, I just can't seem to figure it out how to make the scrolling and dynamic resizing happen.
I would appreciate any helpful comments or remarks!
Thank you!
Ultimately rewriting the paint method did the trick as #MadProgrammer suggested. I was just hoping that I could do the painting without having to define my custom JPanel class, but looks like it doesn't work that way.
The custom class looks like this:
class Drawing extends JPanel {
int mazeSize;
public Drawing(JTextField jtf)
{
try {
this.mazeSize = Integer.parseInt(jtf.getText());
}
catch (Exception e)
{
JOptionPane.showMessageDialog(this, "ERROR! Invalid size value!");
}
} // the constructor gets the size of the drawing from a textField
public Dimension getPreferredSize() {
return new Dimension(mazeSize*10,mazeSize*10);
} //getPreferredSize - this method is used by the scroll pane to adjust its own size automatically
public void drawMaze (Graphics g)
{
/* some irrelevant code that does the desired drawing to the panel by calling g.drawLine()*/
} // drawMaze method that does the de facto drawing
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
drawMaze(g);
}// paintComponent() #Override method - this was the tricky part
}//Drawing JPanel subclass
It is also worth noting (if some noob like myself happens to stumble upon this question), that after instantiating the new JPanel subclass in the action event, I had to add it to the JScrollPanel in the following way, instead of just simply using its add() method:
Drawing drawPanel = new Drawing(textfield1);
scrollPane.getViewport().add(drawPanel);
Again, thanks for the suggestion!
Once finished with the program (a random maze generator that uses a recursive backtracking algorithm), I will make the source code available at my github profile.
I'm currently trying to create a program that moves a rectangle over a background Image with keyboard keys. The problem I'm facing is that when I draw the components they are simply placed next to each other, instead of the square overlaying the background image. Here's the code to display both the components;
JLayeredPane panel = new JLayeredPane();
panel.setLayout(new FlowLayout());
add(panel);
paintBackground pb = new paintBackground(bimg);
panel.add(pb, 1, 0);
paintPlayer cc = new paintPlayer(startX, startY);
panel.add(cc, 2, 0);
pack();
setVisible(true);
I believe the problem is that the paintPlayer component is set to full size, and there seems to be a background. The paintPlayer component code looks like this:
public Dimension getMinimumSize() {
return new Dimension(800,600);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800,600);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(800,600);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
System.out.println(startX + startY );
g.fillRect(startX, startY, 30, 30);
}
I've had a go at setting the component size to just the size of the rectangle, but that way I can't move the rectangle by using the first two values in fillRect. The background for the rest of the space filled by the component (800x600) seems to be opaque.
When added, the components just display next to each other, like this: https://gyazo.com/57245c518e02778c36ffc89ba75d5a81. How do I go about adding the paintPlayer ontop of the paintBackground, so that it only covers the rectangle on the background Image.
I've done a fair bit of searching but I can't seem to work it out. Perhaps something to do with the layout? One other thing I've noticed is that by doing this, neither the frame or the pane benefit from a setBackground, as it's not visible.
Cheers for any help.
This is the default Constructor of JLayerdPane.
public JLayeredPane() {
setLayout(null);
}
You see it uses normaly AbsolutLayout. And if you read here:
Note: that these layers are simply a logical construct and LayoutManagers will affect all child components of this container without regard for layer settings.
You should understand what is wrong. Check OverlapLayout.
I have no idea what the heck is going on while I'm trying to render an image on a JPanel in a JFrame. In fact I just want an image displayed in a fixed position on my JFrame.To achieve this I have a JPanel of a fixed size (min, max, pref size set) on my JFrame. On press of a button, I add another panel to mentioned panel, having the same size. The "child" panel has an overriden paint method to draw an image. So far so good, once I press the button and add that child panel, nothing happens at all. If I then click on the empty panel, the image is drawn as it should be, not overflowing the bounds of the child panel. However, all other components width increases drastically, stretching them out of the JFrames bounds. One would think that at least a scrollbar would appear, but no, the components are then out of reach. I'm attaching two screenshots displaying that very logic behavior.
The code is as follows:
void setPoster(BufferedImage poster) {
ImagePanel ip = new ImagePanel(poster);
ip.setSize(new Dimension(222, 327));
panelPoster.add(ip);
ip.setSize(new Dimension(222, 327));
}
Inside ImagePanel:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
System.out.println("Paiting imagepanel with size " + getSize());
}
Before screenshot:
After screenshot:
Could anyone enlighten me on what could possibly occur here?
I fixed the strange resizing by setting the layout to null on both the JFrame and the JPanel inside my tabbed pane.
setLayout(null);
jPanel4.setLayout(null);
Anyway, that's not needed when using the JLabel instead of the JPanel to hold the image.
However, my ImagePanel would not draw the image anymore, so I replaced it by a JLabel which draws the image just fine:
void setPoster(BufferedImage poster) {
JLabel imageLabel = new JLabel(new ImageIcon(poster));
panelPoster.add(imageLabel);
imageLabel.setSize(new Dimension(222, 327));
}
Note that without setting the size of the JLabel, it won't display anything.
I'm writing a custom component that displays some bioinformatics data, and I'd like to be able to show additional information about the location the mouse is at when the user holds down a certain key. This seems like an obvious job for a tooltip, but there are a few problems that seem to be preventing this from working. First, I want to have the tooltip follow the mouse and change its text dynamically. This works somewhat by overriding getToolTipText and getToolTipLocation for the component, but the tooltip flickers as the mouse position is updated and doesn't display over the sub-components (it's a JPanel with some JTextPanes inside it). I also don't think there's any way to make it display immediately without a call to the ToolTipManager, which I believe would change the delay for all other components.
It looks like there are workarounds for some of these problems, but they're rather complicated and inelegant so I'm thinking a good solution would be to just create my own component, fill it with the relevant information and show it myself. However, this needs to be some kind of top-level component because it needs to be able to extend slightly beyond the borders of the parent component or even the containing JFrame and be drawn over everything else. The only objects I know of that have this functionality outside of JToolTip are JFrame and JDialog, which have borders with titles and close buttons which I don't want. Is there some way to accomplish this?
One option is to use a glass pane. In this case your tooltip won't be able to go outside of the frame, but you can easily position it relative to how near it is to a side of the frame. Some example code that draws a bubble (which you can fill with text in the paint method) that follows the mouse.
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(new Dimension(500, 500));
JPanel glassPane = new JPanel();
glassPane.setOpaque(false);
glassPane.setLayout(null);
frame.setGlassPane(glassPane);
frame.getGlassPane().setVisible(true);
final MyInfoBubble mib = new MyInfoBubble();
mib.setBounds(10, 30, 100, 50);
((JPanel)frame.getGlassPane()).add(mib);
frame.getContentPane().addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent me) {
mib.setBounds(me.getPoint().x, me.getPoint().y, 100, 50);
}
});
((JPanel)frame.getGlassPane()).validate();
((JPanel)frame.getGlassPane()).repaint();
frame.setVisible(true);
}
static class MyInfoBubble extends JPanel
{
public MyInfoBubble()
{
setVisible(true);
}
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.BLUE);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 20, 20);
}
}