Search code examples
javaresizedrawingdraw

Dynamically draw dashed line to fit resized JPanel


The following is an example using drawLine() to draw horizontal and vertical dashed lines on a JPanel. As the JPanel is resized, so are the dashed lines.

Here is the main object.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class ExampleForPosting  implements Runnable{

    public static void main(String args[]) {
        SwingUtilities.invokeLater(
                new ExampleForPosting ());
    }

    private LineDrawer panel;

    @Override
    public void run() {
        JFrame frame = new JFrame("Line Sample");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        panel = new LineDrawer();
        panel.setPreferredSize(new Dimension(300, 200)); 

        frame.add(panel, BorderLayout.CENTER);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public void setJPanelBackground(Color color) {
        panel.setBackground(color);
        panel.repaint();
    }
}

This is the JPanel which does the drawing.

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;

import javax.swing.JPanel;


public class LineDrawer extends JPanel 
{
    private Stroke dashedLineStroke = getDashedLineStroke(1);

    @Override
       public void paintComponent(Graphics g) {
          super.paintComponent(g);
          Graphics2D g2 = (Graphics2D) g;
          g2.setColor(Color.RED);
          g2.setStroke(dashedLineStroke);

          for (int i = 1; i < (this.getWidth()-10) ; i++)
              g.drawLine(10, (i*10), this.getWidth()-10, (i*10));

          for (int i = 1; i < (this.getHeight()-10) ; i++)
              g.drawLine((i*10), 10, (i*10), this.getHeight()-10);
       }

    public Stroke getDashedLineStroke(int width)
    {
        float[] dashPattern = {2, 2};
         return new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 2, dashPattern, 0);
    }
}

For the most part. It works.

enter image description here

As the panel increases in size so do the dashed lines.

enter image description here

The issue is depending upon where the panel is stopped when resizing, it can be incomplete as shown here by the highlights or there isn't an equal amount of space from the edge.

enter image description here

The objective would be to make this dynamic so that the dashed lines always complete and same spacing on all sides. If need be the spacing could be modified, but it should be dynamic even if the properties of the dashed line are changed. I suspect there is some other attribute required for the calculation but unable to find one that works.


Solution

  • for (int i = 1; i < (this.getWidth()-10) ; i++)
    

    First of all your looping condition is wrong. If your width is 300, then you would loop 289 times. You need to divide the width by 10 to get the number of lines to draw.

    so that the dashed lines always complete and same spacing on all sides.

    Well that is not possible. If the width is 105 and each border is 10, then the width of the last line segment can only be 5.

    The easiest way to solve the problem is to set the clip bounds of your painting. Then any drawing outside of the clipped area will be ignored.

    This will allow a partial line to be drawn as the frame is resized:

    Rectangle clip = new Rectangle(10, 10, getWidth() - 19, getHeight() - 19);
    g.setClip( clip );
    
    int linesX = getHeight() / 10;
    int linesY = getWidth() / 10;    
    
    for (int i = 1; i < linesX ; i++)
        g.drawLine(10, (i*10), this.getWidth(), (i*10));
    
    for (int i = 1; i < linesY ; i++)
        g.drawLine((i*10), 10, (i*10), this.getHeight()-10);