Java Tips Weblog

  • Blog Stats

    • 2,571,297 hits
  • Categories

  • Archives

Resizing Components

Posted by Rob Camick on September 13, 2009

A recent entry on Moving Windows discussed how you might add functionality to move a component or a non decorated window. Today we will look at adding resizing functionality to these same components.

The ComponentResizer class will be used to implement the resizing functionality. Its design is similiar to the ComponentMover class in that you need to specify the drag insets for the component. The insets are used to recognize when the mouse is moved over a resizable border. When this happens the cursor is changed to the appropriate cursor to indicate the resizing border. One difference is that an inset value of 0 for any border implies that the border cannot be resized. The default value is 5 for each border. The same drag insets should be used for the ComponentResizer and ComponentMover so the classes don’t get confused over whether a component resize or move is required.

The other features of the class are as follows:

  • snap size (default 1) – specifies the resize increment. That is the distance the mouse must be dragged before the border changes size. The border will snap to this value once dragging has reached the halfway mark.
  • minimum size – specify the minimum size of the component. The minimum size is constrained by the drag insets. (ie. the width cannot be less that the sum of the insets left and right values)
  • maximum size – specify the maximum size of the component. The size of the component will also be constrained by the size of its parent. When resizing a window or frame the constraint is the desktop size. For other components the constraint is the parent container.

Usage of the ComponentResizer is straight forward. There are various constructors you can use. One example would be:

ComponentResizer cr = new ComponentResizer();
cr.setSnapSize(new Dimension(10, 10));
cr.registerComponent(component1, component2);

Hopefully this class provides the flexibility you might need when resizing components.

Get The Code

ComponentResizer.java

See Also

Moving Windows (ComponentMover)

24 Responses to “Resizing Components”

  1. Anitha said

    hi ur code there is an issue // Resizing the West or North border affects the size and location.when i move west side panel location changes after reaches minimum size.plz solve this.

    • Rob Camick said

      I don’t understand the problem. Dragging the West or North border should change both the size and location. If you don’t want the size to change then you need to use my ComponentMover class.

      • Sterling said

        I think understand the problem. though this may be slightly different.

        when you add both ComponentResizer and ComponentMover to a component and then try to ONLY resize using the edge of the component since the edge of the component is still considered part of the component then both the position and size will change.

        for example, I have registered the component on both classes, I find the West edge, the cursor changes, I click and drag the west edge to the left. the west edge moves over as expected. however when I find the east edge and click and drag to only move the east edge because the mover is also activated it will move the entire component the distance the mouse has moved and will also move the east edge the same distance meaning the west edge has move the distance as well and the east edge has moved double. this code illustrates:

        import java.awt.*;
        import javax.swing.*;
        
        public class Gui extends JFrame{
        
          public Gui(){
        
            ComponentResizer cr = new ComponentResizer();
            ComponentMover cm = new ComponentMover();
            JPanel mainPanel = new JPanel(null);
            this.add(mainPanel);
        
            JPanel panel = new JPanel(null);
            cr.registerComponent(panel);
            cm.registerComponent(panel);
            panel.setBackground(Color.red);
        
            mainPanel.add(panel);
            panel.setBounds(50, 50, 150, 150);
          }
        
          public static void main(String[] args){
        
            Gui gui = new Gui();
            gui.setSize(300, 300);
            gui.setLocationRelativeTo(null);
            gui.setVisible(true);
          }
        }
        


        is it possible for the mover class to only work if the mouse is within the component by greater then say 20 pixels so that both the resize and mover classes are not called at the same time even though a component might be registered to both?

      • Rob Camick said

        Thanks for the example. It clarifies your question. This is handled by using:

        cm.setDragInsets( cr.getDragInsets() );

      • Sterling said

        where the default insets are 5 all the way around. I see. I must have missed that looking through the API. Thanks very much for clarifying what I overlooked.

    • Andreas Stahlhofen said

      Hey,

      I also recognize this behavior. So I changed the method changeBounds() to this:

      protected void changeBounds(Component source, int direction, Rectangle bounds, Point pressed, Point current)
      {
      // Start with original locaton and size
      int oldX = bounds.x;
      int oldY = bounds.y;

      int x = bounds.x;
      int y = bounds.y;
      int width = bounds.width;
      int height = bounds.height;

      // Resizing the West or North border affects the size and location
      if (WEST == (direction & WEST))
      {
      int drag = this.getDragDistance(pressed.x, current.x, this.snapSize.width);
      int maximum = Math.min(width + x, this.maximumSize.width);
      drag = this.getDragBounded(drag, this.snapSize.width, width, this.minimumSize.width, maximum);

      x -= drag;
      width += drag;
      }

      if (NORTH == (direction & NORTH))
      {
      int drag = this.getDragDistance(pressed.y, current.y, this.snapSize.height);
      int maximum = Math.min(height + y, this.maximumSize.height);
      drag = this.getDragBounded(drag, this.snapSize.height, height, this.minimumSize.height, maximum);

      y -= drag;
      height += drag;
      }

      // Resizing the East or South border only affects the size

      if (EAST == (direction & EAST))
      {
      int drag = this.getDragDistance(current.x, pressed.x, this.snapSize.width);
      Dimension boundingSize = this.getBoundingSize(source);
      int maximum = Math.min(boundingSize.width - x, this.maximumSize.width);
      drag = this.getDragBounded(drag, this.snapSize.width, width, this.minimumSize.width, maximum);
      width += drag;
      }

      if (SOUTH == (direction & SOUTH))
      {
      int drag = this.getDragDistance(current.y, pressed.y, this.snapSize.height);
      Dimension boundingSize = this.getBoundingSize(source);
      int maximum = Math.min(boundingSize.height - y, this.maximumSize.height);
      drag = this.getDragBounded(drag, this.snapSize.height, height, this.minimumSize.height, maximum);
      height += drag;
      }

      if (source.getMinimumSize().width >= width)
      {
      x = oldX;
      }

      if (source.getMinimumSize().height >= height)
      {
      y = oldY;
      }

      source.setBounds(x, y, width, height);
      source.validate();
      }

      Now, the ComponentResizer also respects the minimumSize of the given source Component.

      Best regards,
      Andreas

  2. Hello Rob,

    Thank you for those great classes.

    I’m trying to use both the ComponentResizer and ComponentMover classes on an undecorated frame which has a child component that takes up the entire frame.

    Unfortunately I’m unable to get the resizer to work since the child component takes up all the space and thus mouse events can’t go through to the frame.

    Here’s a sample code, which is a modification of Sterling’s code:

    import java.awt.*;
    import javax.swing.*;
    
    public class Gui extends JFrame{
    
      public static void main(String[] args){
    
        Gui gui = new Gui();
        gui.setSize(300, 300);
        gui.setLocationRelativeTo(null);
        gui.setUndecorated(true);
        gui.setVisible(true);
      }
    
      public Gui(){
        
        JPanel mainPanel = new JPanel(null);
        this.add(mainPanel);
    
        JPanel panel = new JPanel();
        panel.setSize(300, 300);
        panel.setLocation(0, 0);
        panel.setBackground(Color.red);
    
        ComponentResizer cr = new ComponentResizer(this);
        ComponentMover cm = new ComponentMover(this, panel);
        cm.setDragInsets(cr.getDragInsets());
    
        mainPanel.add(panel);
      }
    }
    
    
    

    The ComponentMover works great as I setup the destination component to be the frame, but I am unable to figure out how to do the same for the component resizer.

    Any help is highly appreciated.

    Best,

    Oussama

    • Rob Camick said

      Yes, the ComponentResizer isn’t designed to forward events to a parent component. As a simple solution to your problem you can do:

      // JPanel mainPanel = new JPanel(null);
      JPanel mainPanel = new JPanel(new BorderLayout());
      mainPanel.setBorder( new EmptyBorder(5, 5, 5, 5) );
      mainPanel.setBackground(Color.RED);

      • Hello Rob,

        Thank you for your answer, and please excuse me for my late reply.

        I’ve tried your suggestion, but this unfortunately adds a ‘padding’ to the main panel, which is unfortunately not acceptable for my application.

        I’ll try to figure out another way to do this, maybe by allowing the ComponentResizer to forward events.
        Unless you have another suggestion :)

        Best,

        Oussama

  3. Hello,
    It seems your class is great! I used your component resizer class in one of my application.During the development of my application I found some common tasks very useful but not very easy to get a hand on. So I have created a library for those tasks.I have included your one there.Its a simple library but the usage can sometime save a lots of time.If you wish to not publishing it like this,please inform me.I will remove your class from the lib.Here’s the URL for the lib- http://sourceforge.net/projects/actioncomponent/.
    If you wish you can contribute to make the lib rich.I will be grateful & honored if you can join in.Let me know if you are interested & if you have a SourceForge id.
    And congrats for great effort in ComponentResizer.(I made one before,but I think yours one is best,that’s why I wish to put it in lib.)
    Thanks in advance.

  4. Anonymous said

    Hello Rob Camick,

    I want to only use cross resizer. Maybe we can add setResizeOption(int)
    then call in the constructor as a diagonal .Could you help me ?

    • Rob Camick said

      If you want to change the cursors that are used for the various resize options then just create a setCursor(…) method. You would call this method after creating the ComponentResizer class.

  5. John Ross said

    Just wanted to say thank you for this code. This and your mover class saved me a day of developing and testing one from scratch.

  6. Pasi Paasiala said

    When I use multiple monitors, and put the window on the second display, this didn’t work, since the getBoundingSize returned bounds whose maximum x value was smaller than the mouse cursor’s location. The width went negative.

    Here’s a quick fix:

    /*
     *  Keep the size of the component within the bounds of its parent.
     */
    private Dimension getBoundingSize(Component source)
    {
    	if (source instanceof Window)
    	{
    		GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
    		GraphicsDevice[] devices = e.getScreenDevices();
    		Rectangle bounds = new Rectangle();
    
    		for (GraphicsDevice device: devices) 
    		{ 
    			GraphicsConfiguration[] configurations = device.getConfigurations();
    
    			for (GraphicsConfiguration config: configurations) 
    			{
    				Rectangle gcBounds = config.getBounds();
    				bounds.add(gcBounds);
    		        }
    	    }
    		return new Dimension(bounds.width, bounds.height);
    	}
    	else
    	{
    		return source.getParent().getSize();
    	}
    }
    

    Of course, this would only need to be done once and not every time.

    • tim8w said

      Pasi,
      The “Quick Fix” works in some cases, but not in mine. My main monitor has another monitor to the left. The bounds looks like this:

      bounds.height = 1080
      bounds.width = 3360
      bounds.x = -1920
      bounds.y = 0

      Unfortunately since only the height and width are returned, I cannot move the Frame to my monitor on the left… Any ideas on how to correct this?

    • Vlad said

      Hi guys,

      I fixed this by adding in

      protected void changeBounds(…) {

      Rectangle currentScreenBounds = ScreenUtils.getActiveScreen(source).getDefaultConfiguration().getBounds();
      x -= currentScreenBounds.x;
      y -= currentScreenBounds.y;

      ……………………..

      x += currentScreenBounds.x;
      y += currentScreenBounds.y;

      source.setBounds(x, y, width, height);
      source.validate();
      }

      For obtaining the active screen you only have to iterate through all monitors – graphicsEnvironment.getScreenDevices(); and checking if the bounds of the screen intersect the bounds of the dialog.
      I tried this with primary monitor left/right and on top and seemed to work. Please let me know if you see anything wrong with it.

  7. Arild said

    Thank you for this magnificent code… really, really useful, still today.

  8. How about scaling image using those borders? I changed setBounds with my scale method but didnt work :(

    • Rob Camick said

      Scaling the image has nothing to do with this class. It is the job of the component to do the custom painting when it is scaled. Check out the `Stretch Icon` on this blog for a possible solution.

  9. AlexGaskarth said

    Is there a way to disable the mouse when it is maximized to avoid resizing?

    • Rob Camick said

      I guess in the MouseListener code you could use the SwingUtilities.windowForComponent(…) method to get the frame for the component that generated the MouseEvent. Then you can use the getExtendedState() method of the frame to check if the frame is maximized. If so then exit the listener.

Leave a comment