Calculate cell sizes and draw (with lines between them)

I want to draw a grid and draw material in the cells (so that everything is simple, just fill them). In general, it works for me quite a lot only in some panel sizes, where the cell is located about 1 pixel where it should be placed (overlapping the line). TBH I actually didn’t do enough calculation to possibly find the answer myself, so I apologize for this, I'm really not too sure how to approach this β€œerror”, though.

Anyway, here is the code:

public class Gui extends JFrame { public static void main(String[] args) { new Gui().setVisible(true); } public Gui() { setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); add(new JPanel() { public static final int SIZE = 3; /** Line thickness ratio to a block */ public static final float LINE_THICKNESS = 0.1f; /** @return the width of a block. */ protected final int getBlockWidth() { return getWidth() / SIZE; } /** @return the height of a block. */ protected final int getBlockHeight() { return getHeight() / SIZE; } /** @return the width of a cell. */ protected final int getCellWidth() { return (int) Math.ceil(getBlockWidth()*(1-LINE_THICKNESS)); } /** @return the height of a cell. */ protected final int getCellHeight() { return (int) Math.ceil(getBlockHeight()*(1-LINE_THICKNESS)); } @Override public void paintComponent(Graphics g) { g.setColor(new Color(0, 0, 255, 100)); int lineWidth = (int) (LINE_THICKNESS * getBlockWidth()); int lineHeight = (int) (LINE_THICKNESS * getBlockHeight()); for(int i = 0; i <= SIZE; i++) { g.fillRect(i * getBlockWidth() - lineWidth / 2, 0, lineWidth, getHeight()); g.fillRect(0, i * getBlockHeight() - lineHeight/2, getWidth(), lineHeight); } g.setColor(new Color(255, 0, 0, 100)); for(int i = 0; i < SIZE; i++) { for(int j = 0; j < SIZE; j++) { int x = j * getBlockWidth() + lineWidth/2; int y = i * getBlockHeight() + lineHeight/2; Graphics temp = g.create(x, y, getCellWidth(), getCellHeight()); drawCell(temp, i, j); } } } private void drawCell(Graphics g, int i, int j) { g.fillRect(0, 0, getCellWidth(), getCellHeight()); } }); setLocation(new Point(500, 200)); setSize(new Dimension(600, 600)); } } 

If you run it, you'll probably see what I mean. I cannot come up with a good explanation in words. At first I thought that I needed to add + 1 to x and y, since I want to draw next to the line, but this (obviously) just carries the problem to the other side.

Doing this with a SIZE larger (e.g. 30) gives me another error that gives open space to the sides. I know (or guess), this is because I use integers, and this is not a big deal. But tips for a better approach (overall) are always welcome.

+7
java graphics
source share
2 answers

There are several ways to fix this. I will not give you the code, as I believe (based on how you asked your question) you are one of those who like to think and solve problems on their own.

First of all: first draw a background on the entire panel, and then draw lines. There will be no lines and the drawing will be a little faster.

The second way: the drawing order is important. You can safely draw the background first (even if it overlaps) and then overwrite it with borders.

Third way: do not use ints. Use floats or doubles. All your problems will disappear.

Fourth way: calculate the remainder. You can predict when the lines are drawn, and when not, think about it. Predict it and draw accordingly.

+4
source share

Hi, I had the same problem, but the solution I implemented was inspired by a sample available from the Java tutorial for drawing multi-line text and drawing text in a cell using text APIs.

http://java.sun.com/docs/books/tutorial/2d/text/drawmulstring.html

 import java.awt.Component; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextLayout; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.BreakIterator; import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; public class MultilineTableCell implements TableCellRenderer { class CellArea extends DefaultTableCellRenderer { /** * */ private static final long serialVersionUID = 1L; private String text; protected int rowIndex; protected int columnIndex; protected JTable table; protected Font font; private int paragraphStart,paragraphEnd; private LineBreakMeasurer lineMeasurer; public CellArea(String s, JTable tab, int row, int column,boolean isSelected) { text = s; rowIndex = row; columnIndex = column; table = tab; font = table.getFont(); if (isSelected) { setForeground(table.getSelectionForeground()); setBackground(table.getSelectionBackground()); } } public void paintComponent(Graphics gr) { super.paintComponent(gr); if ( text != null && !text.isEmpty() ) { Graphics2D g = (Graphics2D) gr; if (lineMeasurer == null) { AttributedCharacterIterator paragraph = new AttributedString(text).getIterator(); paragraphStart = paragraph.getBeginIndex(); paragraphEnd = paragraph.getEndIndex(); FontRenderContext frc = g.getFontRenderContext(); lineMeasurer = new LineBreakMeasurer(paragraph,BreakIterator.getWordInstance(), frc); } float breakWidth = (float)table.getColumnModel().getColumn(columnIndex).getWidth(); float drawPosY = 0; // Set position to the index of the first character in the paragraph. lineMeasurer.setPosition(paragraphStart); // Get lines until the entire paragraph has been displayed. while (lineMeasurer.getPosition() < paragraphEnd) { // Retrieve next layout. A cleverer program would also cache // these layouts until the component is re-sized. TextLayout layout = lineMeasurer.nextLayout(breakWidth); // Compute pen x position. If the paragraph is right-to-left we // will align the TextLayouts to the right edge of the panel. // Note: this won't occur for the English text in this sample. // Note: drawPosX is always where the LEFT of the text is placed. float drawPosX = layout.isLeftToRight() ? 0 : breakWidth - layout.getAdvance(); // Move y-coordinate by the ascent of the layout. drawPosY += layout.getAscent(); // Draw the TextLayout at (drawPosX, drawPosY). layout.draw(g, drawPosX, drawPosY); // Move y-coordinate in preparation for next layout. drawPosY += layout.getDescent() + layout.getLeading(); } table.setRowHeight(rowIndex,(int) drawPosY); } } } public Component getTableCellRendererComponent( JTable table, Object value,boolean isSelected, boolean hasFocus, int row,int column ) { CellArea area = new CellArea(value.toString(),table,row,column,isSelected); return area; } } It resizes row heigth too but it does it well only when this renderer is used for a single column. And this is the way I used to invoke it for render my table. final int wordWrapColumnIndex = ...; myTable = new JTable() { public TableCellRenderer getCellRenderer(int row, int column) { if (column == wordWrapColumnIndex ) { return wordWrapRenderer; } else { return super.getCellRenderer(row, column); } } }; 
0
source share

All Articles