Почему repaint() не всегда вызывает paintComponent и почему он не всегда ведет себя правильно при вызове

Я работаю над клоном тетриса в Java, и все, кажется, работает должным образом, пока я не хочу очистить полный ряд и удалить все вышеперечисленное. Хотя все мои данные правильно представляют преобразование, мой метод paintComponent, кажется, только очищает строку, но оставляет все отображаемое выше, как это было до вызова repaint(). Новая фигура провалится через призрачные блоки и приземлится на невидимые блоки в нижнем ряду, где упали бы вышеуказанные фигуры.

Вот мой метод рисования компонентов:

public void paintComponent(Graphics page)
{
    super.paintComponent(page);
    Graphics2D page2D = (Graphics2D) page;
    for (int c = 0; c < 10; c++) 
    {
        for (int r = 0; r < 18; r++)
        {
            if (well[c][r] != null) //Well is a 2D array of Block objects that have Rectangle object, coordinates and color
            {
                page2D.setColor(well[c][r].getColor());
                page2D.fill(well[c][r].getSquare());
                page2D.setColor(Color.gray);
                page2D.draw(well[c][r].getSquare());
            }       

        }
    }
    for (int i = 0; i < 4; i++) //tetro = the player's tetris piece
    {
        page2D.setColor(tetro.getColor());
        page2D.fill(tetro.getBlock(i).getSquare());
        page2D.setColor(Color.GRAY);
        page2D.draw(tetro.getBlock(i).getSquare());
    }

}

Это часть моего метода actionPerformed в моем слушателе Timer, который обнаруживает / очищает блоки и вызывает метод перекраски.

        int count = 0;  //Number of occupied cells in well
        int clears = 0; //number of rows to be clear
        int lowestClear = -1; //Lowest row that was cleared, -1 if none
        for (int row = 0; row < 18; row++)
        {
            for (int col = 0; col < 10; col++)
            {
                if (well[col][row] != null)
                {
                    count++;
                }
            }
            if (count == 10)
            {
                clears++;
                if (lowestClear < 0)
                {
                    lowestClear = row;
                }
                for (int col = 0; col < 10; col++)
                {
                    well[col][row] = null;
                }
            }
            count = 0;
        }
        if (clears > 0)
        {
            repaint(); //Doesn't call paintComponent()
            for (int i = 1; i <= clears; i++)
            {
                for (int r = 16; r >= 0; r--)
                {
                    if (r > lowestClear)
                    {
                        break;
                    }
                    for (int c = 0; c < 10; c++)
                    {
                        if (well[c][r] != null)
                        {
                            well[c][r+1] = well[c][r];
                            well[c][r] = null;
                        }           
                    }
                }
            }
            repaint(); //Does not call paintComponent()
        }       
        tetro.fall();
        repaint(); //DOES call paint component

К тому моменту, когда вызывается первый метод repaint (), массив скважин правильно показывает, что полная строка теперь полностью пуста. Мне бы хотелось, чтобы метод repaint () обновлял панель, отображая эту пустую строку, но paintComponent(), похоже, не вызывается. Это также относится и ко второму методу repaint (), где я хотел бы обновить кадр, чтобы показать блоки в их новых позициях после очистки строки и опускания их вниз. Опять же, paintComponent() не вызывается. Однако для последнего вызова repaint (), где я просто хочу обновить позицию падающей части независимо от того, какие обновления она может или не должна была делать раньше, repaint () ДОЛЖНА вызывать paintComponent(). Итак: вопрос номер один: почему paintComponent() вызывается только в этом экземпляре вызова repaint().

Однако когда вызывается paintComponent() и он достигает конца метода, я следую за ним в режиме отладки, чтобы увидеть, в какой строке панель отражает изменения. Как только он достигает строки "Repaintmanager.paintDirtyRegions(Map)":856, он очищает строку и отображает новый падающий фрагмент, но имеет невидимые блоки и фантомные блоки.

Итак, мой второй вопрос, почему paintComponent() ведет себя таким образом. Очевидно, мне нужно немного почитать Repaintmanager и Java-живопись в целом, но я был бы очень признателен, если бы кто-нибудь смог мне это объяснить.

Вот основной метод, если он важен:

import javax.swing.JFrame;

public class TetrisDriver 
{


public static void main(String[] args) 
{
    JFrame frame = new JFrame("Tetris");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    frame.getContentPane().add(new Matrix()); //Matrix = jPanel class
    frame.pack();
    frame.setVisible(true);
}

}

Я прошу прощения, если это неприятно долго.

3 ответа

Прежде всего, если вы еще этого не сделали, я бы прочитал " Живопись" в AWT и Swing, где объясняется схема рисования, используемая Swing (и AWT).

Во-вторых, вы не контролируете перерисовку, а менеджер перерисовок и ОС, вы просто предоставляете предложения.

В-третьих, вы можете взглянуть на метод JComponent.paintImmediately, для этого потребуется знать область, которую вы хотите обновить, но это может помочь

Из документов Java

Окрашивает указанную область в этом компоненте и всех его потомках, которые перекрывают область, немедленно.

Редко необходимо вызывать этот метод. В большинстве случаев более эффективно вызывать перерисовку, которая откладывает фактическое рисование и может объединять избыточные запросы в один вызов рисования. Этот метод полезен, если необходимо обновить отображение во время отправки текущего события.

Я также мог бы быть более разумным, чтобы отобразить состояние игры в буфер экрана и использовать его в paintComponent метод. Вы могли бы поместить их в очередь и вытолкнуть их во время процесса рисования, позволяя создавать более или менее по требованию (сохраняя пул из нескольких и растущий, сокращая пул по мере необходимости)...

repaint() не звони paintComponent() и это правильно работает Scenerio для repaint() метод.

Это только отмечает состояние, что компонент должен быть перекрашен, и paintComponent() вызывается далее самим Swing.

Много repaint() звонки могут вызвать только один paintComponent() позвоните, и это правильное поведение.

Вместо того, чтобы использовать repaint() Вы можете попробовать напрямую позвонить paint() сам.

Graphics g;
g = getGraphics();
paint(g);

Таким образом, он будет рисовать сразу же, когда вы захотите внести изменения. Присвойте это другой функции и вызовите ее, когда хотите сразу увидеть изменения.

Другие вопросы по тегам