7

我想画一个网格并在单元格中画一些东西(为了让事情变得简单,只需填充它们)。总的来说,我只在某些面板尺寸下工作得很好,单元格距离它应该放置的位置大约 1 个像素(与线重叠)。TBH 我还没有真正做足够的计算来可能自己找到答案,所以对此我深表歉意,我真的不太确定如何处理这个“错误”。

无论如何,这是代码:

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));
}
}

如果你运行它,你可能会明白我的意思。我想不出一个好的语言解释。起初我以为我必须将 + 1 添加到 x 和 y ,因为我想在线条旁边绘制,但这(显然)只是将问题转移到了另一边。

以更大的尺寸(如 30)运行它会给我另一个错误,即它为两侧提供了开放空间。我知道(或假设)这是因为我使用的是整数,但这并不是什么大不了的事。但是总是欢迎提供更好方法的提示(通常)。

4

2 回答 2

4

有几种方法可以解决此问题。我不会给你代码,因为我相信(根据你提出问题的方式)你是喜欢自己思考和解决问题的人之一。

首先:先在整个面板上画出背景,再画线。不会有 while 线,绘图会稍微快一些。

第二种方式:绘制顺序很重要。您可以先安全地绘制背景(即使它重叠),然后用边框覆盖它。

第三种方式:不要使用整数。使用浮点数或双精度数。你所有的烦恼都会烟消云散。

第四种方法:计算余数。您可以预测何时绘制线条,何时不绘制,请考虑一下。预测它并适当地绘制。

于 2013-10-14T18:13:35.283 回答
0

嗨,我遇到了同样的问题,但我实施的解决方案受到 Java 教程中用于绘制多行文本的示例的启发,并使用文本 API 在单元格上绘制文本。

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);
        }
    }
};
于 2013-10-15T11:21:03.787 回答